JavaScript 基础概念面试题
1. 说一下对原型和原型链的理解,原型和原型链在 JavaScript 中的作用和机制
Details
在 JavaScript 中,每个对象(除了基本数据类型)都有一个原型对象,可以理解为对象之间的关系。原型对象也是一个对象。当我们访问一个对象的属性或方法时,如果这个对象本身没有这个属性或方法,JavaScript 就会去它的原型对象上查找,直到找到为止。这种属性和方法的继承关系就是通过原型来实现的。
原型链(Prototype Chain)是一种对象之间通过原型对象连接起来的链式结构。每个对象都有一个原型,而原型对象本身也可以有自己的原型,形成一条链条。当我们访问一个对象的属性或方法时,JavaScript 会沿着原型链一直向上查找,直到找到对应的属性或方法为止。如果最终都没有找到,就会返回 undefined。
总的来说,原型和原型链是 JavaScript 中实现继承和属性查找的重要机制。通过原型链,我们可以实现对象之间的属性和方法的共享和继承,从而提高代码的复用性和可维护性。
2. 请简述 JavaScript 作用域和作用域链
Details
在 JavaScript 中,作用域和作用域链是理解变量访问和函数执行的重要概念。
作用域
作用域是指变量、函数和对象的可访问范围。JavaScript 中的作用域主要有以下几种类型:
全局作用域:在任何函数外部定义的变量具有全局作用域,可以在任何地方访问。
javascriptvar globalVar = "I am a global variable"; function showGlobalVar() { console.log(globalVar); // 可以访问 } showGlobalVar(); // 输出: I am a global variable函数作用域:在函数内部定义的变量只能在该函数内部访问。使用
var声明的变量具有函数作用域。javascriptfunction myFunction() { var localVar = "I am a local variable"; console.log(localVar); // 可以访问 } myFunction(); // console.log(localVar); // 这是错误的,localVar 在此处不可访问块级作用域:使用
let和const声明的变量具有块级作用域,只能在其被声明的代码块内访问。javascriptif (true) { let blockVar = "I am a block variable"; console.log(blockVar); // 可以访问 } // console.log(blockVar); // 这是错误的,blockVar 在此处不可访问
作用域链
作用域链是用于查找变量的机制。当在一个作用域内访问一个变量时,JavaScript 会首先在当前作用域查找,如果找不到,就会沿着作用域链向上查找,直到全局作用域。
示例
var globalVar = "I am a global variable";
function outerFunction() {
var outerVar = "I am an outer variable";
function innerFunction() {
var innerVar = "I am an inner variable";
console.log(innerVar); // 可以访问
console.log(outerVar); // 可以访问
console.log(globalVar); // 可以访问
}
innerFunction();
}
outerFunction();在上面的示例中:
innerFunction可以访问innerVar(自己的作用域),也可以访问outerVar(外层函数的作用域),以及globalVar(全局作用域)。- 如果在
innerFunction中尝试访问一个未定义的变量,JavaScript 会沿着作用域链查找,直到全局作用域。
总结
- 作用域决定了变量的可访问性,可以是全局的、函数的或块级的。
- 作用域链是查找变量的机制,允许内层作用域访问外层作用域的变量。
3. 请简述 JavaScript 中的闭包
Details
闭包是 JavaScript 中的一个重要概念,它是指函数能够访问其词法作用域之外的变量的能力。
闭包的定义
闭包是指一个函数与其词法环境的组合。当一个函数在其定义的词法作用域之外被调用时,它仍然可以访问其定义时的变量。
闭包的示例
function outerFunction() {
const outerVariable = 'I am from outer function';
function innerFunction() {
console.log(outerVariable); // 可以访问外部函数的变量
}
return innerFunction;
}
const inner = outerFunction();
inner(); // 输出: I am from outer function闭包的作用
- 数据封装:闭包可以用于创建私有变量和方法,实现数据的封装。
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getCount()); // 1- 保存状态:闭包可以在函数执行完毕后保存其词法环境的状态。
function createGreeter(name) {
return function() {
console.log(`Hello, ${name}!`);
};
}
const greetJohn = createGreeter('John');
greetJohn(); // 输出: Hello, John!
const greetJane = createGreeter('Jane');
greetJane(); // 输出: Hello, Jane!闭包的注意事项
内存泄漏:闭包会持有对外部变量的引用,导致这些变量不会被垃圾回收,可能造成内存泄漏。
性能影响:闭包会增加内存使用,因为它需要保存整个词法环境。
总结
闭包是 JavaScript 中的一个强大特性,它允许函数访问其词法作用域之外的变量。通过闭包,我们可以实现数据封装、保存状态等功能。然而,使用闭包时需要注意内存泄漏和性能影响的问题。
4. 请解释 JavaScript 中的执行上下文(Execution Context)
Details
执行上下文是 JavaScript 代码执行的环境,它包含了代码执行所需的所有信息。
执行上下文的类型
- 全局执行上下文:代码开始执行时创建的第一个执行上下文,全局对象(在浏览器中是
window)是其关联对象。 - 函数执行上下文:每次调用函数时创建的执行上下文,函数对象是其关联对象。
- Eval 执行上下文:执行
eval()函数时创建的执行上下文。
执行上下文的创建过程
创建阶段:
- 创建变量对象(Variable Object)
- 建立作用域链(Scope Chain)
- 确定
this指向
执行阶段:
- 变量赋值
- 函数引用
- 代码执行
执行栈(调用栈)
执行栈用于管理执行上下文,遵循后进先出(LIFO)原则。当函数调用时,其执行上下文被压入栈顶;当函数执行完毕时,其执行上下文被弹出栈。
示例
function foo() {
console.log('foo');
bar();
}
function bar() {
console.log('bar');
}
foo();
// 执行栈变化:
// 1. 全局执行上下文入栈
// 2. foo() 执行上下文入栈
// 3. bar() 执行上下文入栈
// 4. bar() 执行完毕,出栈
// 5. foo() 执行完毕,出栈
// 6. 全局执行上下文保持在栈中总结
执行上下文是 JavaScript 代码执行的环境,它包含了变量对象、作用域链和 this 指向等信息。执行栈用于管理执行上下文,遵循后进先出原则。理解执行上下文对于理解 JavaScript 的执行机制非常重要。
5. 请解释 JavaScript 中的变量提升(Hoisting)
Details
变量提升是 JavaScript 中的一种机制,它允许变量和函数在声明之前被使用。
变量提升的原理
在代码执行之前,JavaScript 引擎会进行词法分析和预编译,将变量和函数的声明提升到作用域的顶部。
var、let 和 const 的变量提升
- var:变量声明被提升,赋值不提升
- let 和 const:变量声明被提升,但存在暂时性死区(Temporal Dead Zone)
函数声明和函数表达式的提升
- 函数声明:整个函数声明被提升
- 函数表达式:只有变量声明被提升,函数赋值不提升
示例
// var 的变量提升
console.log(a); // undefined
var a = 1;
console.log(a); // 1
// let 的变量提升
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;
console.log(b); // 2
// 函数声明的提升
foo(); // 输出: Hello
function foo() {
console.log('Hello');
}
// 函数表达式的提升
bar(); // TypeError: bar is not a function
var bar = function() {
console.log('Hello');
};
bar(); // 输出: Hello暂时性死区
// 暂时性死区示例
if (true) {
// 从这里开始到 let 声明,都是 b 的暂时性死区
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;
console.log(b); // 2
}总结
变量提升是 JavaScript 中的一种机制,它允许变量和函数在声明之前被使用。理解变量提升对于避免代码中的错误非常重要。