ES6

let和const

2019-05-19  本文已影响0人  简单背包客

let 概念:

声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。

//例如
{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

1、不存在变量提升

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

注:变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识。从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。

2、暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)

let tmp = 123;
//代码块中的tmp将不受外部的tmp影响,形成TDZ
if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}
//上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

3、不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。

//不会报错(外面的a和{}里的a不在同一作用域里)
let a = 18; 
if(true) {
  let a = 20;
}

//会报错(在同一作用域{}内)
if(true) {
  let a = 18;
  let a = 20;
}

4、块级作用域

ES5只有全局作用域和函数作用域,没有块级作用域,这导致很多场景不可理,例如:

var name = 'lee';
function getName(){
  console.log(name);     //undefined
  if(true){
      var name = 'jack';
  }
}
getName();
var funs= [];
for(var i = 0; i < 10; i++){
   funs.push(function(){
     console.log(i);
  });
}
funs[6]();   //10

//为了解决这个问题,可以使用立即调用函数表达式(IIFE),
//以强制生成计数器变量的副本,就像这样:

var funs = [];
for(var i = 0; i < 10; i++){
  funs.push((function(value){
    return function(){
       console.log(value);
    }
  })(i));
}
funs[6]();  //6

ES6的块级作用域,块级声明用于声明在指定的作用域外无法访问的变量。块级作用域存在于:

//例1
function f1(){
  let n = 5;
  if(true){
    let n = 10;
  }
  console.log(n);   //5
}

//例2
var funs= [];
for(let i = 0; i < 10; i++){
   funs.push(function(){
     console.log(i);
  });
}
funs[6]();   //6

//上面的代码中,变量i是let声明的,当前的只在本轮循环有效。
//所以每一次循环的i其实都是一个新的变量,
//于是最后输出的是6。大家可能会问,如果每一轮循环的变量i都是重新声明的,
//那它怎么知道上一轮循环的值从而计算出本轮循环的值呢?
//这是因为javascript引擎内部会记住上一轮循环的值,
//初始化本轮的变量i时,就在上一轮的基础上进行计算。

//另外,for循环还有一个特别之处,就是设置循环变量的
//那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for(let i = 0; i < 3; i++){
  let i = 'abc';
  console.log(i);  
}

//abc
//abc
//abc
//正确运行以上代码将输出3次abc。这表明函数内部的变量i和循环变量i不在同一作用域,
//而是各自单独的作用域。

5、不可改变的const命令

const 声明一个只读的常量。一旦声明,常量的值就不会改变。这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const PI = 3.1415;
console.log(PI);  //3.1415
PI = 3.14;      //TypeError: Assignment to constant variable.

注:const命令跟let命令一样不存在变量提升、具有块级作用域、存在暂时性死区、不允许重复声明。

6、const命令的本质

const 命令实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。

const foo = {};
foo.name = 'lee';
foo.age = 18;
console.log(foo.name);  //lee
console.log(foo.age);  //18
foo = {};   //TypeError: Assignment to constant variable.

注:如果真的想对象冻结,可以使用Object.freeze({})

7、顶层对象中的let和const

从es6开始,全局变量(例如:import、class、let、const声明的)将逐步与顶层对象的属性隔离。

//顶层对象中的var
var foo = {name: 'lee', age: 18};
cosole.log(foo === window.foo);  //true
console.log(window.foo);   //{name: 'lee', age: 18}

//顶层对象中的let和const
let obj = {height: 182, weight: 140};
const mt = {msg:'hello’};

console.log(obj === window.obj);  //false
console.log(window.obj);   //undefined

console.log(mt === window.mt);   //false
console.log(window.mt);      //undefined

8、获取顶层对象的两种方法

//方法一
(typeof window  !== 'undefined'
 ? window
  : (typeof process === 'object' &&
     typeof require === 'function' &&
     typeof global === 'object' )
    ? global 
    : this)

//方法二
var getGlobal = function(){
  if(typeof self !== 'undefined') {return self;}
  if(typeof window !== 'undefined') {return window;}
  if(typeof global !== 'undefined') {return global;}
  throw new Error('unable to locate global object');
};

文章参考:
《ECMAScript 6 入门》

上一篇 下一篇

猜你喜欢

热点阅读