ES6深入学习(一)块级作用域详解

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。 注意:只有声明本身被提升,而任何赋值或者其他执行逻辑都被留在原处。 函数声明会被提升,但函数表达式不会

foo() //Function foo
function foo(){
    console.log('Function foo')
}

foo2() //TypeError: foo2 is not a function
var foo2 = function() {
    console.log('Function foo2')
}
复制代码

函数声明和变量声明都会被提升,但(可以拥有多个“重复”声明的代码中出现)是,函数会优先被提升。 考虑这段代码:

foo2() //2
var foo2 = function() {
console.log(1)
}
function foo2(){
console.log(2)
}
foo2()//1
复制代码

注意var foo2是一个重复的(因此被无视),即使它出现在function foo2()...声明之前。因为函数声明是在普通变量之前被提升的。但后续的声明会覆盖前一个。

var声明及变量提升(Hoisting)机制 在函数作用域或全局作用域中,通过关键字var声明的变量,无论实际上在哪声明的,都会被当成在当前作用域顶部声明的变量。

function getValue(flag){
    //var value,变量value的声明被提升至函数顶部,而初始化操作停留在原处执行
    if(flag){
        var value = "true";  
    }else{
        return null
    }
    //此处可以访问变量value,值为undefined
}
复制代码

块级声明

块级作用域 (也被称为词法作用域)存在于函数内部、块中({}之间的区域)

  • let声明的用法与var相同,用let代替var来声明可以把变量的作用域限制在当前代码块中。let声明不会被提升,假设作用域中已经存在某个标识符,在使用let关键字声明则会抛出错误。
var a = 1;
let a = 2;//Uncaught SyntaxError

var b = 3;
if(true){
    let b = 4;//不会抛出错误
}
复制代码
  • const声明一个常量,其值一旦被设定后不可更改,因此通过const声明的常量必须进行初始化。

  • 对于复合类型的变量,const声明不允许修改绑定,但允许修改值。变量名不指向数据,而是指向数据所在的地址,const命令只是保证变量名指向的地址不变,并不能保证该地址的数据不变,急可以修改该对象的属性值。

const b = 'b';
b = 'c';//Uncaught SyntaxError: 

const obj = {};
obj.a = 'a';
console.log(obj);//{a:'a'}
复制代码

临时死区(Temporal Dead Zone) 与var不同,let和const声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,即使相对安全的typeof操作符也会触发引用错误

if(true){
    console.log(typeof a)//Uncaught ReferenceError
    let a = 1;
}
复制代码

由于console.log(typeof a)语句会抛出错误,因此用let定义并初始化变量a的语句不会被执行,此时a还位于所谓的临时死区(TDZ)中;将let换成const会有相同的效果。 JavaScript引擎在运行代码是发现变量声明时,要么将它们提升至作用域顶部(var声明),要么将声明放到TDZ中(let和const声明)。访问TDZ中的变量会触发运行是的错误,只有执行过变量声明语句后,变量才会从TDZ中移出,然后方可正常访问。

循环中的块作用域绑定

for(var i = 0;i<3;i++){
    console.log(i)//0,1,2
}
console.log(i)//3
//由于var声明提升,变量i在循环结束后仍可以访问。如果换用let声明,循环结束后访问i则会抛出一个错误
复制代码

循环中的函数:

var data = [];
for(var i = 0;i<10;i++){
    data[i] = function(){
        console.log(i)
        return i
    }
}
data[1]();//10
复制代码

**循环里的每次迭代同时共享着变量i,循环内部创建的函数保留了对相同变量的引用,循环结束时变量i的值为10,所以每次调用console.log(i)时就会输出是在10。

为解决这个问题,通常会使用立即调用函数表达式(IIFE),以强制生成计数器变量的副本

var data = [];
for(var i = 0;i<10;i++){
    data[i] = function(){
        console.log(i);
        return i
    }()
}
data[5];//5
复制代码

循环中的let声明 let声明模仿上述示例中IIFE所做的一切来简化循环过程,每次迭代都会创建一个新变量,并以之前迭代中同名变量的值将其初始化(即删除IIFE之后仍可得到预期的效果)

var arr = [];
for(let i = 0;i<10;i++) {
    data[i] = function(){
        return i
    }
}
data[1]();//1
复制代码

全局作用域绑定

let和const与var的另一个区别是它们在全局作用域中的行为,当var被用于全局作用域时,会创建一个新的全局变量作为全局对象(浏览器中的window对象)的属性,这意味着var可能会无意覆盖一个已经存在的全局属性

//浏览器中
var RegExp = "hello";
console.log(window.RegExp);//hello
复制代码

即使全局对象RegExp定义在window上,也不能幸免于var声明覆盖。如果使用let或const,会在全局作用一下创建一个新的绑定,但该绑定不会添加为全局对象的属性,即不会覆盖全局变量,只能遮蔽它。

let RegExp = "hello";
console.log(RegExp)//"hello"
console.log(window.RegExp === RegExp);//false,不会破坏全局作用域
复制代码

相关文章:ES6深入学习(二)关于函数(juejin.im/post/5cb7e9…)

转载于:https://juejin.im/post/5cb6c857f265da03981fbd87

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值