ES6之作用域与let、const

1.let命令

ES5和以前版本的js采用var声明变量,ES6 新增了 一个JavaScript 关键字: let 。let 的用法类似于var,但相较于var具有以下特点

1.1块作用域

let所声明的变量只在let命令所在的代码块有效

{
	let a=10;
	var b=1;
}
console.log(b);
console.log(a);

输出结果b是1,a是not defined(此处注意not defined和undefined区别,undefined说明变量声明但没有定义,not defined说明该变量甚至没有声明)

1.2不能重复声明

function fn(){
let a=10;
let a=1;
}// Identifier 'a' has already been declared
function fn(){
let a=10;
var a=1;
}//Identifier 'a' has already been declared
function fn(){
var a=10;
let a=1;
}//Identifier 'a' has already been declared
function fn(){
var a=10;
var a=1;
}

四段代码中只有第四段不会报错,因为使用var命令时编辑器会判断如果已经有声明的同名变量时忽略var关键字,然后直接赋值。
而let不允许在相同的作用域内重复声明同一个变量,不管该变量的两次声明都用let或用let和var都会报错。因此,也不能在函数内部重新声明参数。

function func(arg) {
  let arg;
}//报错
function func(arg) {
  {
    let arg;
  }
}//不报错

1.3没有变量提升

console.log(a); // undefined
var a = 1;

var声明的变量无论其实际声明位置在何处,都会被视为声明于所在函数(或全局)的顶部,这就是变量提升。因此实际上的代码可看做为:

var a;
console.log(a); // undefined
a = 1;

而let不具有变量提升的性质,所以输出为变量b没有被声明

console.log(b); //b is not defined
let b = 1;

1.4暂时性死区

ES6明确规定,如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域。
在代码块内,使用let命令声明变量之前,该变量都是不可用的,这个区域被称为“暂时性死区”(TDZ)。

var temp=123;
if(true){
    temp='abc';// Cannot access 'temp' before initialization
    let temp;
}

此段代码中虽然定义了全局变量temp,但块作用域中又let了一个局部变量temp,导致该块级作用域成为封闭作用域,不被全局变量temp影响,所以对局部变量temp进行赋值之前还没有声明该变量,即产生报错,这个区域就是暂时性死区

1.5不影响作用域链

每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链。
当JavaScript查找变量x的时候,会从当前作用域开始跟随作用域链向上查找,直到找到x变量的声明,若到达全局作用域中仍未找到,则抛出一个引用错误(ReferenceError)异常

let a=123
function fun(){
   console.log(a)//123
}

此处fun函数要输出变量a但函数内没有,就会跟随作用域链向上查找,let不影响作用域链的效果,所以找到a的赋值为123

2.块级作用域

ES5只有全局作用域和函数作用域,从而会导致一些问题出现:

var tmp=new Date();
function f(){
    console.log(tmp);
    if(false){
	    var tmp='hello';
    }
}
f();//undefined

此处原本想让if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量,但由于变量提升,导致内层的tmp变量覆盖了外层的tmp变量,输出undefined

var s='hello';
for(var i=0;i<s.length;i++){
	console.log(s[i]);
}
console.log(i);//5

此处变量i只用来控制循环,但是循环结束后,它并没有消失,而是泄露成了全局变量。

因此,let为js新增了块级作用域,块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域,块作用域具有以下特点:

2.1允许作用域任意嵌套

块作用域多层嵌套,不影响内层作用域读取外层作用域的变量

{
    let a = 2;
    {
        a++;
        {
            console.log(a)//3
        }
    }
}

但外层作用域无法读取内层作用域的变量

{
    console.log(a)//a is not defined
    {
        let a= 2
    }
}

2.2内层作用域可以定义外层作用域的同名变量

上文有说外层作用域无法读取内层作用域的变量,所以即使内层作用域定义的变量与外层同名,也不会报错

{
    let a=123
        {
            let a=456
        }
}

2.3不需要立即执行匿名函数

没有块作用域之前,为了防止前文举例的内层覆盖外层变量,计数循环变量泄露为全局变量等问题,广泛采用立即执行匿名函数(IIFE),块作用域的出现使IIFE不再必要

(function() {
  var  a = 0;
}());//立即执行匿名函数

3.const命令

const用来声明一个只读的常量,一旦声明,其值不能改变且必须立即初始化

const a = 2;
a //2
a = 3;// Assignment to constant variable.
const b;//Missing initializer in const declaration

除此之外,与let用法一致,即只在声明所在的块级作用域内有效、声明的常量也是不提升、同样存在暂时性死区、不可重复声明

而在实际上,const保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量,但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针
const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了

const foo={y:10};
foo.x=100;
console.log(foo);//{"y": 10,"x": 100}
foo={n:1000};// Assignment to constant variable.

foo指向对象的属性可以增加,但不能把新的对象赋给foo

4.顶层对象

顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。

window.a = 1;
a // 1

a = 2;
window.a // 2

这样的设计带来了很多问题,ES6 为了改变这一点,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值