JavaScript-变量提升与词法作用域

变量提升

函数声明跟普通的变量会被提升到作用域的顶部。
注意是函数声明哦,函数表达式是不会提升函数的

console.log(foo);
var foo = function(){
console.log("哈哈哈");
} // undefined
console.log(foo);
function foo(){
console.log("哈哈哈");
} // Function foo

函数跟变量的提升规则有些许的不同。

  • 当变量提升时,将变量的声明给提升,提升的同时给变量赋值为undefined
  • 当函数提升时,会将整个函数体给提升到顶部。
  • 当函数碰到之前已有任何声明时,会进行覆盖操作。
  • 当变量碰到之前已有变量声明时,同样会进行覆盖操作。但是假如碰到已经声明的函数声明,则不会进行覆盖操作。即函数的优先值>变量
var f = 5; 
function f() { 
console.log("Hello from function f"); 
} 
console.log(f);//Function f

这时候我们就能解出下面这道经典的面试题了

var foo = function () {
    console.log('foo1');
}
foo();  // foo1
var foo = function () {
    console.log('foo2');
}
foo(); // foo2

我们来详细说一说代码的执行过程:

  • 首先创建全局执行上下文
  • 碰到foo变量声明将foo进行变量提升,值为undfined。
  • 接着又碰到了foo的变量声明,将上面的声明给覆盖。
  • 编译完成,开始执行代码
  • 左查找foo并给foo进行赋值操作。
  • 执行foo
  • 给foo进行赋值操作,覆盖之前值。
  • 执行foo
function foo() {
    console.log('foo1');
}
foo();  // foo2
function foo() {
    console.log('foo2');
}
foo(); // foo2

执行过程:

  • 首先创建全局执行上下文
  • 碰到函数声明,将foo整个函数进行变量提升。同时将函数声明放进全局执行上下文的词法环境中
  • 接着给第二个foo进行声明,将上面的函数声明给覆盖。
  • 编译完成,开始执行代码
  • 执行foo
  • 执行foo

词法作用域

因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。
而与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。
让我们认真看个例子就能明白之间的区别:

//词法作用域
var a = '你好'
function foo(){
var a = 1;
bar();
}
function bar(){
console.log(a);
}//你好
//动态作用域
var a = '你好'
function foo(){
var a = 1;
bar();
}
function bar(){
console.log(a);
}// 1

词法作用域这里打印的是你好而不是1,因为bar的作用域在定义时就决定了,当bar内查找不到变量时会查找全局变量作用域。
作用域查找会在找到第一个匹配的标识符时停止。在多层的嵌套作用域中可以定义同名的 标识符,这叫作“遮蔽效应”(内部的标识符“遮蔽”了外部的标识符)。抛开遮蔽效应, 作用域查找始终从运行时所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见 第一个匹配的标识符为止。
全局变量会自动成为全局对象(比如浏览器中的window对象)的属性,因此 可以不直接通过全局对象的词法名称,而是间接地通过对全局对象属性的引 用来对其进行访问。** window.a** 通过这种技术可以访问那些被同名变量所遮蔽的全局变量。但非全局的变量 如果被遮蔽了,无论如何都无法被访问到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值