JS的作用域
1. 全局作用域
全局作用域是在代码的任何地方都能访问到的最外层作用域。在浏览器环境下,全局作用域就是window对象,因此所有在全局作用域中声明的变量和函数都会成为window对象的属性和方法。
var globalVar = "I am global";
function globalFunc() {
return "Hello from global function";
}
console.log(window.globalVar); // 输出: I am global
console.log(globalFunc()); // 输出: Hello from global function
2. 局部作用域
局部作用域是限定在函数或代码块内部的作用域。
function myFunction() {
var localVar = "I am local";
function localFunc() {
return "Hello from local function";
}
console.log(localVar); // 有效
console.log(localFunc()); // 有效
}
// 在这里尝试访问 localVar 或 localFunc 会导致 ReferenceError
3.块级作用域(ES6+)
在ES6之前,JavaScript只有全局作用域和函数作用域。但从ES6开始,引入了let和const关键字,它们声明的变量具有块级作用域,即只在最近的包含块({})内有效。
if (true) {
let blockScopedVar = "I am block scoped";
console.log(blockScopedVar); // 有效
} // 在这里尝试访问 blockScopedVar 会导致 ReferenceError
作用域链
当JavaScript引擎需要查找一个变量时,它会从当前作用域开始查找,如果没有找到,就会继续向上查找父级作用域,直到找到全局作用域。这个逐级向上查找的过程形成了一条作用域链。
var x = 10;
function outer() {
var x = 20;
function inner() {
var x = 30;
console.log(x); // 30
}
inner();
console.log(x); // 20
}
outer();
console.log(x); // 10
闭包
闭包是JavaScript中一个非常重要的概念,它允许一个函数访问并操作函数外部的变量。闭包是由函数以及创建该函数的词法环境组合而成的。闭包使得内部函数能够记住并访问其词法作用域,即使外部函数已经执行完毕。
function outerFunction() {
var outerVar = "I am outer";
function innerFunction() {
console.log(outerVar); // 访问外部函数的变量
}
return innerFunction;
}
var myClosure = outerFunction();
myClosure(); // 输出: I am outer
预解析
一、预解析的定义
预解析(Hoisting)是JavaScript引擎的一种行为,即在代码执行之前,会将所有用var声明的变量和用function声明的函数提升到其所在作用域的最顶部,但并不会提升变量的赋值或函数的调用。对于var声明的变量,其值会被初始化为undefined;对于function声明的函数,其整个函数体会被提升到顶部,但不会被执行。
二、预解析的类型
变量预解析:
变量预解析也称为变量提升(Variable Hoisting)。
使用var声明的变量会在其所在作用域的最顶部被声明,但赋值操作不会提升。
console.log(a); // undefined
var a = 1;
函数预解析:
函数预解析也称为函数提升(Function Hoisting)
使用function声明的函数会在其所在作用域的最顶部被声明并定义,但不会被调用。
console.log(fn()); // 假设fn函数有返回值
function fn() {
return 1;
}
//匿名函数表达式
console.log(foo); // undefined
var foo = function() {
console.log(10);
}
//函数声明和函数
//相当于执行以下代码
var foo;
foo();
foo = function() {
console.log(10);
}
三、预解析的注意事项
同名变量和函数:
如果存在同名的变量和函数声明,函数声明会覆盖变量声明。但如果变量已经被赋值,则不会被覆盖。
var fn = 10;
function fn() {
console.log('hello world');
}
fn(); // TypeError: fn is not a function
函数声明的优先级:
在同一作用域内,函数声明的优先级高于变量声明。
console.log(fn()); // 假设fn是一个函数
var fn = 10;
function fn() {
return 1;
} // 这种情况在严格模式下会抛出SyntaxError
函数表达式不会被提升:
使用函数表达式(如var fn = function() {...})声明的函数不会被提升。
块级作用域:
在ES6及以后的版本中,let和const声明的变量具有块级作用域,它们不会在块外部被提升。
if (true) {
let a = 1;
}
console.log(a); // ReferenceError: a is not defined