JS-变量提升-进阶-下

169 阅读4分钟

上篇文章我们讲了关于变量提升的基础内容:

  1. 代码的执行顺序:先编译后执行
  2. 什么是变量提升:代码真正执行之前,声明的变量就已经存在于作用域中
  3. 哪些东西可以变量提升:形如var 和 function foo() {}这样的声明

下面我们来了解JS变量提升的进阶内容

重复声明

相同名字的var,相同名字的function声明

  1. 对于var,当遇到编译器遇到到此类的声明语句,会判断当前作用域中,是否已经存在了该变量。如果有,就会忽略该声明,如果没有,就会在当前作用域中存放这个变量,也就是执行真正的函数声明操作
  2. 对于function声明,后面的声明会覆盖之前的声明
console.log(foo);

function foo() {
    console.log('this is a first');
}

function foo() {
    console.log('this is a seconde');
}

vscode编辑上面代码的时候,也许会报错,但是不会影响代码的执行

var和function名字相同的声明,以谁为准

如果当前作用域同时存在var afunction 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变量也会被提升的铁证!

访问没有声明的变量,和访问对象上不存在的属性区别

  1. 首先变量的查找和属性的查找,是两个完全不同的规则:变量查找的规则是作用域链规则,而属性的查找规则是原型链规则
  2. 查找没有声明的变量,回报错ReferenceError: * is not defined
  3. 没有查找到属性,会返回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();

由此看来,还是有所区别的。

其实,在作用域中声明的变量,是放在一个当前执行上下文的变量对象存放的,也就是说在作用域链中求得变量的值,与在原型链上求得变量的值,虽说规则不同,但还是有交集的。

总结

  1. varfunction重复声明,编译器执行的动作是不一样的
  2. varfunction谁的声明优先级更高
  3. varfunction声明,碰到了形参,会发生什么
  4. 引用没有声明的变量,和访问不存在的属性区别
  5. 全局作用域的变量,既可以通过原型链规则查找,也可以通过作用域链规则查找。神奇
  6. 这么精彩的文章,点个赞吧。球球宁了