返回

初识JS,浅析JavaScript 代码的执行机制

前端

JavaScript 代码执行机制概述

JavaScript 是一种解释型语言,这意味着它逐行执行代码,而不是像编译型语言那样将整个程序编译成机器代码再执行。JavaScript 代码的执行机制主要分为以下几个阶段:

  1. 词法分析和解析 :在这个阶段,JavaScript 引擎会将源代码转换成抽象语法树 (AST),AST 是代码的结构化表示,便于后续的执行。
  2. 变量提升 :在解释执行之前,JavaScript 会将所有声明的变量提升到当前作用域的顶部。这意味着,变量可以先使用,再声明。
  3. 代码执行 :JavaScript 引擎逐行解释执行代码,并将执行结果存储在内存中。
  4. 垃圾回收 :当变量不再被使用时,JavaScript 引擎会自动释放该变量所占用的内存空间。

JavaScript 代码执行机制的几个核心概念

变量提升

变量提升是指 JavaScript 会将所有声明的变量提升到当前作用域的顶部。这意味着,变量可以在声明之前使用,但是此时的变量值是 undefined。

console.log(myName); // undefined
var myName = 'John Doe';

函数提升

函数提升是指 JavaScript 会将所有声明的函数提升到当前作用域的顶部。这意味着,函数可以在声明之前调用,但是此时函数内部的变量和参数都是 undefined。

foo(); // TypeError: foo is not a function
function foo() {
  console.log('Hello, world!');
}

作用域

作用域是指代码中变量和函数的可见范围。在 JavaScript 中,有全局作用域和局部作用域之分。全局作用域是指整个程序都可以访问的变量和函数,而局部作用域是指函数内部的变量和函数。

var globalVariable = 'Hello, world!';

function foo() {
  var localVariable = 'Goodbye, world!';
}

console.log(globalVariable); // Hello, world!
console.log(localVariable); // ReferenceError: localVariable is not defined

闭包

闭包是指函数及其内部作用域的组合。这意味着,函数内部的变量和函数即使在函数执行完成后仍然存在。

function createCounter() {
  var count = 0;

  return function() {
    return ++count;
  };
}

var counter = createCounter();

console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

执行上下文

执行上下文是指 JavaScript 代码执行的环境。它包括当前执行的代码、变量和函数。当一个函数被调用时,一个新的执行上下文就会被创建。

function foo() {
  console.log(this); // [object Window]
}

foo();

var obj = {
  foo: function() {
    console.log(this); // [object Object]
  }
};

obj.foo();

调用栈

调用栈是指 JavaScript 引擎用来跟踪函数调用顺序的数据结构。当一个函数被调用时,该函数的信息会被压入调用栈。当函数执行完成后,该函数的信息会被弹出调用栈。

function foo() {
  bar();
}

function bar() {
  baz();
}

function baz() {
  console.log('Hello, world!');
}

foo();

当这段代码执行时,调用栈如下:

foo()
bar()
baz()

当 baz() 函数执行完成后,baz() 函数的信息会被弹出调用栈。当 bar() 函数执行完成后,bar() 函数的信息也会被弹出调用栈。最后,当 foo() 函数执行完成后,foo() 函数的信息也会被弹出调用栈。

堆是指 JavaScript 引擎用来存储对象和数组的数据结构。当一个对象或数组被创建时,该对象或数组的信息会被存储在堆中。

var obj = {
  name: 'John Doe',
  age: 30
};

var arr = [1, 2, 3];

当这段代码执行时,obj 对象和 arr 数组的信息都会被存储在堆中。

原型链

原型链是指 JavaScript 对象继承的链条。当一个对象被创建时,该对象会继承其原型的所有属性和方法。对象的原型是该对象所属类的实例。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

var john = new Person('John Doe', 30);

john.greet(); // Hello, my name is John Doe and I am 30 years old.

当这段代码执行时,john 对象会继承 Person 类的所有属性和方法。因此,john 对象可以调用 Person 类的 greet() 方法。

this

this 是指当前执行代码的对象。this 的值可以是全局对象、函数对象、对象实例等。

console.log(this); // [object Window]

function foo() {
  console.log(this); // [object Window]
}

foo();

var obj = {
  foo: function() {
    console.log(this); // [object Object]
  }
};

obj.foo();

当这段代码执行时,this 的值会在不同的上下文中发生变化。在全局上下文中,this 的值为全局对象。在函数上下文中,this 的值为函数对象。在对象上下文中,this 的值为对象实例。

事件循环

事件循环是指 JavaScript 引擎用来处理事件的机制。当事件发生时,例如鼠标点击、键盘按下、定时器超时等,事件循环会将该事件放入事件队列中。事件循环会不断地从事件队列中取出事件并执行。

// 鼠标点击事件
document.addEventListener('click', function() {
  console.log('The mouse was clicked!');
});

// 定时器事件
setTimeout(function() {
  console.log('1 second has passed!');
}, 1000);

// 键盘按下事件
document.addEventListener('keydown', function() {
  console.log('A key was pressed!');
});

当这段代码执行时,事件循环会将鼠标点击事件、定时器事件和键盘按下事件放入事件队列中。然后,事件循环会不断地从事件队列中取出事件并执行。

异步编程

异步编程是指代码在不等待结果的情况下继续执行。在 JavaScript 中,异步编程可以通过回调函数、Promise 和 async/await 来实现。

// 回调函数
function foo(callback) {
  setTimeout(function() {
    callback('Hello, world!');
  }, 1000);
}

foo(function(result) {
  console.log(result); // Hello, world!
});

// Promise
function foo() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello, world!');
    }, 1000);
  });
}

foo().then(function(result) {
  console.log(result); // Hello, world!
});

// async/await
async function foo() {
  const result = await new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello, world!');
    }, 1000);
  });

  console.log(result); // Hello, world!
}

foo();

当这段代码执行时,foo() 函数会立即执行,但不会等待 setTimeout() 函数执行完成。当 setTimeout() 函数执行完成后,callback() 函数或 Promise.resolve() 函数会被调用。然后,回调函数或 Promise 的 then() 函数会被执行。最后,console.log() 函数会被执行,输出结果 Hello, world!。