zoukankan      html  css  js  c++  java
  • ES6之路第一篇:let、const

    let        相当于之前的var

    const   常量,定义好了不能改变

    let基本用法

    ES6新增了let命令,用来声明变量。它的用法类似var,但是所声明的变量,只在let命令所在的代码块内有效(即块级作用域),否则会报错。

    1 {
    2   let a = 10;
    3   var b = 1;
    4 }
    5 a // ReferenceError: a is not defined.
    6 b // 1

    var和let的作用域

    先上两个实例:

    var定义的for循环:

    1 var a = [];
    2 for (var i = 0; i < 10; i++) {
    3   a[i] = function () {
    4     console.log(i);
    5   };
    6 }
    7 a[6](); // 10

    let定义的for循环:

    1 var a = [];
    2 for (let i = 0; i < 10; i++) {
    3   a[i] = function () {
    4     console.log(i);
    5   };
    6 }
    7 a[6](); // 6

    从上面的两个例子可以看出,代码结构都一样,但是结果却不一样,这是怎么回事呢?原来变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。如果变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

    var存在变量提升,let不存在变量提升

    1 // var 的情况
    2 console.log(foo); // 输出undefined
    3 var foo = 2;
    4 
    5 // let 的情况
    6 console.log(bar); // 报错ReferenceError
    7 let bar = 2;

    从上面代码可以看出使用var时,先使用后定义会输出undefined,而使用let时,会直接报错,这里就是因为在使用var定义的变量时存在变量提升了,实际的运行如下所示:

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

    暂时性死区(TDZ) 

    只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

    1 var tmp = 123;
    2 
    3 if (true) {
    4   tmp = 'abc'; // ReferenceError
    5   let tmp;
    6 }

    上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

    总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

     1 if (true) {
     2   // TDZ开始
     3   tmp = 'abc'; // ReferenceError
     4   console.log(tmp); // ReferenceError
     5 
     6   let tmp; // TDZ结束
     7   console.log(tmp); // undefined
     8 
     9   tmp = 123;
    10   console.log(tmp); // 123
    11 }

    ES6的块级作用域

    let实际上为JavaScript新增了块级作用域

    1 function f1() {
    2   let n = 5;
    3   if (true) {
    4     let n = 10;
    5   }
    6   console.log(n); // 5
    7 }

    上面的函数有两个代码块,都声明了变量n,运行后输出 5。这表示外层代码块不受内层代码块的影响。如果两次都使用var定义变量n,最后输出的值才是 10。

    ES6 允许块级作用域的任意嵌套。

     1 {{{{ 2 {let insane = 'Hello World'} 3 console.log(insane); // 报错 4 }}}}; 

    块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了。

     1 // IIFE 写法
     2 (function () {
     3   var tmp = ...;
     4   ...
     5 }());
     6 
     7 // 块级作用域写法
     8 {
     9   let tmp = ...;
    10   ...
    11 }

    块级作用域与函数声明

    ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

     1 // 浏览器的 ES6 环境
     2 function f() { console.log('I am outside!'); }
     3 (function () {
     4   var f = undefined;
     5   if (false) {
     6     function f() { console.log('I am inside!'); }
     7   }
     8 
     9   f();
    10 }());
    11 // Uncaught TypeError: f is not a function

    考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

     1 // 函数声明语句
     2 {
     3   let a = 'secret';
     4   function f() {
     5     return a;
     6   }
     7 }
     8 
     9 // 函数表达式
    10 {
    11   let a = 'secret';
    12   let f = function () {
    13     return a;
    14   };
    15 }

    另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错

     1 // 不报错
     2 'use strict';
     3 if (true) {
     4   function f() {}
     5 }
     6 
     7 // 报错
     8 'use strict';
     9 if (true)
    10   function f() {}

    let注意

    1. 没有预解析,不存在变量提升,在代码块内,只要let定义变量,在之前使用,都是报错,先定义完,再使用
    2. 同一个作用域里,不能重复定义变量
    3. for循环,for循环里面是父级作用域,里面的是子级作用域

    const命令的基本用法

    const声明一个只读的常量。一旦声明,常量的值就不能改变。

    1 const PI = 3.1415;
    2 PI // 3.1415
    3 
    4 PI = 3;
    5 // TypeError: Assignment to constant variable.

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

     1 const foo; 2 // SyntaxError: Missing initializer in const declaration 

    const的作用域与let命令相同:只在声明所在的块级作用域内有效。

    const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

    const声明的常量,也与let一样不可重复声明。

    const本质

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

    1 const foo = {};
    2 
    3 // 为 foo 添加一个属性,可以成功
    4 foo.prop = 123;
    5 foo.prop // 123
    6 
    7 // 将 foo 指向另一个对象,就会报错
    8 foo = {}; // TypeError: "foo" is read-only

    顶层对象的属性

    ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。

    1 var a = 1;
    2 // 如果在 Node 的 REPL 环境,可以写成 global.a
    3 // 或者采用通用方法,写成 this.a
    4 window.a // 1
    5 
    6 let b = 1;
    7 window.b // undefined

    global对象

    在语言标准的层面,引入global作为顶层对象。也就是说,在所有环境下,global都是存在的,都可以从它拿到顶层对象。

    1 // CommonJS 的写法
    2 require('system.global/shim')();
    3 
    4 // ES6 模块的写法
    5 import shim from 'system.global/shim'; shim();
  • 相关阅读:
    Python 类中方法的内部变量,命名加'self.'变成 self.xxx 和不加直接 xxx 的区别
    用foreach遍历 datagridView 指定列所有的内容
    treeView1.SelectedNode.Level
    YES NO 上一个 下一个
    正则 单词全字匹配查找 reg 边界查找 精确匹配 只匹配字符 不含连续的字符
    抓取2个字符串中间的字符串
    sqlite 60000行 插入到数据库只用不到2秒
    将多行文本以单行的格式保存起来 读和写 ini
    将秒转换成时间格式
    richtextbox Ctrl+V只粘贴纯文本格式
  • 原文地址:https://www.cnblogs.com/wanghao123/p/9282304.html
Copyright © 2011-2022 走看看