上篇文章我们讲了关于变量提升的基础内容:
- 代码的执行顺序:先编译后执行
- 什么是变量提升:代码真正执行之前,声明的变量就已经存在于作用域中
- 哪些东西可以变量提升:形如
var和function foo() {}这样的声明
下面我们来了解JS变量提升的进阶内容
重复声明
相同名字的var,相同名字的function声明
- 对于
var,当遇到编译器遇到到此类的声明语句,会判断当前作用域中,是否已经存在了该变量。如果有,就会忽略该声明,如果没有,就会在当前作用域中存放这个变量,也就是执行真正的函数声明操作 - 对于
function声明,后面的声明会覆盖之前的声明
console.log(foo);
function foo() {
console.log('this is a first');
}
function foo() {
console.log('this is a seconde');
}

vscode编辑上面代码的时候,也许会报错,但是不会影响代码的执行
var和function名字相同的声明,以谁为准
如果当前作用域同时存在var a和function a(){},那么编译结束,当前作用域只会有指向函数的a变量。也就是说,函数声明会覆盖var声明
console.log(foo);
var foo = 'variable foo';
function foo() {
console.log('this is a first');
}

有了形参,function,var声明,以谁为准
function foo(p1, p2) {
console.log(p1);
console.log(p2);
var p2 = 'var p2';
function p1() {
console.log('func p')
}
}
foo('foo p1', 'foo p2');
// [Function: p1]
// foo p2
上面的执行结果表示:
- 当
function声明遇上了形参,以function声明为准 - 当
var声明遇上了形参,以形参为准 - 按声明的优先级排个序:
function、形参、var
let、const会变量提升吗
会变量提升,使用let进行的声明会在当前块作用域中进行提升,但是并不会被赋予初始值,所以在声明变量的代码之前访问变量,会报ReferenceError: Cannot access ... before initialization的错误,
{
console.log( bar ); // ReferenceError: Cannot access 'bar' before initialization!
let bar = 2;
console.log( bar ); //2
}
但是在声明语句后面访问,却能正常运行。这种情况也叫暂时性死区
访问没有初始化的变量,和访问没有定义的变量的区别
console.log(name); // ReferenceError: Cannot access 'name' before initialization!
console.log(age); // ReferenceError: age is not defined
let name = 'zenos';
上面两种不同的报错,就是let变量也会被提升的铁证!
访问没有声明的变量,和访问对象上不存在的属性区别
- 首先变量的查找和属性的查找,是两个完全不同的规则:变量查找的规则是作用域链规则,而属性的查找规则是原型链规则
- 查找没有声明的变量,回报错
ReferenceError: * is not defined - 没有查找到属性,会返回undefined,不会报错
RHS和LHS是什么区别
RHS和LHS是编程语言中的两个重要概念,分别代表右侧值(Right-Hand Side)和左侧值(Left-Hand Side)。
LHS(左侧值)
在赋值语句中,LHS指的是等号左边的变量或表达式。它是赋值操作的目标,接受右侧值的赋值。
例如:x = 5中,x是 LHS。
RHS(右侧值)
在赋值语句中,RHS指的是等号右边的值或表达式。它是赋值操作的源,提供值给左侧变量或表达式。
例如:x = 5中,5是 RHS。
总的来说,LHS是赋值操作的目标,而RHS是赋值操作的源。
在编程中,理解LHS和RHS的区别对写出正确的赋值语句和理解变量的赋值行为非常重要。
如果RHS查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出ReferenceError异常。
但是当引擎执行LHS查询时(非严格模式),如果在顶层(全局作用域)中也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量(类似var变量)。在函数执行过程中,会声明变量。
也就是说,变量声明不一定会发生在编译器,在执行期也会发生。(非严格模式下)
全局作用域的变量,真的是window的属性吗
//全局作用域中
function foo() {
console.log(b); //ReferenceError: b is not defined
}
foo();
//全局作用域中
function foo() {
console.log(window.b); //undefined
}
foo();
由此看来,还是有所区别的。
其实,在作用域中声明的变量,是放在一个当前执行上下文的变量对象存放的,也就是说在作用域链中求得变量的值,与在原型链上求得变量的值,虽说规则不同,但还是有交集的。
总结
var和function重复声明,编译器执行的动作是不一样的var和function谁的声明优先级更高var和function声明,碰到了形参,会发生什么- 引用没有声明的变量,和访问不存在的属性区别
- 全局作用域的变量,既可以通过原型链规则查找,也可以通过作用域链规则查找。神奇
- 这么精彩的文章,点个赞吧。球球宁了