# JavaScript运行逻辑详解
## 1. JavaScript的执行环境
JavaScript是一种单线程、解释型的编程语言,其运行逻辑涉及多个关键概念:
### 1.1 执行上下文(Execution Context)
执行上下文是JavaScript代码执行的环境,主要包括:
– **全局执行上下文**:默认的执行环境,代码首次执行时创建
– **函数执行上下文**:每次调用函数时创建
– **Eval执行上下文**:使用eval()函数时创建(不推荐使用)
### 1.2 执行栈(Call Stack)
执行栈用于管理执行上下文,遵循”后进先出”(LIFO)原则:
“`javascript
function first() {
console.log(‘First function’);
second();
console.log(‘First function again’);
}
function second() {
console.log(‘Second function’);
}
first();
// 输出顺序:
// First function
// Second function
// First function again
“`
### 1.3 变量提升(Hoisting)
JavaScript引擎在执行代码前会进行预解析,将变量和函数声明提升到作用域顶部:
“`javascript
console.log(a); // undefined,变量声明被提升
var a = 10;
foo(); // 正常执行,函数声明被提升
function foo() {
console.log(‘Hello’);
}
“`
## 2. JavaScript的事件循环(Event Loop)
由于JavaScript是单线程的,为了处理异步操作,引入了事件循环机制:
### 2.1 宏任务(Macro Tasks)和微任务(Micro Tasks)
– **宏任务**:setTimeout、setInterval、I/O操作、UI渲染等
– **微任务**:Promise、process.nextTick、MutationObserver等
### 2.2 事件循环的执行顺序
1. 执行全局同步代码
2. 执行所有微任务
3. 执行一个宏任务
4. 重复步骤2-3
“`javascript
console.log(‘Start’);
setTimeout(() => {
console.log(‘Timeout’);
}, 0);
Promise.resolve().then(() => {
console.log(‘Promise’);
});
console.log(‘End’);
// 输出顺序:
// Start
// End
// Promise
// Timeout
“`
## 3. 闭包(Closure)
闭包是指函数能够访问其词法作用域之外的变量:
“`javascript
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
“`
## 4. 原型链(Prototype Chain)
JavaScript通过原型链实现继承:
“`javascript
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
return `Hello, ${this.name}!`;
};
const person = new Person(‘John’);
console.log(person.greet()); // Hello, John!
“`
## 5. 异步编程模式
### 5.1 回调函数(Callbacks)
“`javascript
function fetchData(callback) {
setTimeout(() => {
callback(‘Data received’);
}, 1000);
}
fetchData((data) => {
console.log(data);
});
“`
### 5.2 Promise
“`javascript
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(‘Data received’);
}, 1000);
});
}
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
“`
### 5.3 async/await
“`javascript
async function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(‘Data received’);
}, 1000);
});
}
async function main() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error(error);
}
}
main();
“`
## 6. 内存管理
JavaScript使用自动垃圾回收机制:
– **标记清除**:标记不再使用的对象并回收其内存
– **引用计数**:跟踪对象的引用次数,当引用次数为0时回收内存
## 7. 性能优化
### 7.1 减少DOM操作
“`javascript
// 优化前
for (let i = 0; i < 1000; i++) {
document.getElementById('list').innerHTML += `
`;
}
// 优化后
const list = document.getElementById(‘list’);
let html = ”;
for (let i = 0; i < 1000; i++) {
html += `
`;
}
list.innerHTML = html;
“`
### 7.2 使用事件委托
“`javascript
// 优化前
const items = document.querySelectorAll(‘li’);
items.forEach(item => {
item.addEventListener(‘click’, () => {
console.log(‘Item clicked’);
});
});
// 优化后
const list = document.getElementById(‘list’);
list.addEventListener(‘click’, (e) => {
if (e.target.tagName === ‘LI’) {
console.log(‘Item clicked’);
}
});
“`
## 8. 常见陷阱
### 8.1 作用域陷阱
“`javascript
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // 输出5次5
}, 100);
}
// 修复方法1:使用let
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // 输出0,1,2,3,4
}, 100);
}
// 修复方法2:使用闭包
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(() => {
console.log(j); // 输出0,1,2,3,4
}, 100);
})(i);
}
“`
### 8.2 this指向问题
“`javascript
const obj = {
name: ‘Object’,
greet: function() {
console.log(this.name);
}
};
const greet = obj.greet;
greet(); // undefined,this指向全局对象
// 修复方法1:使用bind
const boundGreet = obj.greet.bind(obj);
boundGreet(); // Object
// 修复方法2:使用箭头函数
const obj2 = {
name: ‘Object 2’,
greet: function() {
setTimeout(() => {
console.log(this.name); // Object 2,箭头函数继承外部this
}, 100);
}
};
“`
通过理解JavaScript的运行逻辑,我们可以编写更高效、更可靠的代码,避免常见的陷阱和错误。