14.架构设计与解决方案—2.ECMAScript 6

1、 ECMAScript 6 简介

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语⾔的下⼀代标准,已经在 2015 年 6 ⽉正式发布了。

其⽬标是使得 JavaScript 语⾔可以⽤来编写复杂的⼤型应⽤程序,成为企业级开发语⾔。

① ECMAScript 和 JavaScript 的关系

  • ECMAScript是JavaScript的规格
  • JavaScript是ECMAScript的⼀种实现

② ES6 与 ECMAScript 2015 的关系

  • 2011年,ECMAScript5.1版发布后,就开始制定 6.0 版了。因此,ES6这个词就是指 JavaScript 语⾔的下⼀个版本。
  • 2015年6⽉发布了ES6的第⼀个版本,正式名称就是《ECMAScript 2015 标准》(简称ES2015)。
  • 2016年6⽉⼩幅修订的ES6.1版本发布,简称ES2016标准,同样2017年6⽉发布 ES2017 标准。因此ES6既是⼀个历史名词,也是⼀个泛指,含义是5.1版以后的JavaScript的下⼀代标准,涵盖了ES2015、ES2016、ES2017等等。

③ ECMAScript 的历史

  • 1997年发布了ECMAScript 1.0版本
  • 1998年6⽉发布了ECMAScript 2.0
  • 1999年12⽉发布了ECMAScript 3.0,在业界得到⼴泛⽀持,成为通⾏标准,奠定了JavaScript语⾔的基本语法。
  • 2000年ECMAScript 4.0开始酝酿,其⼤部分内容被ES6继承了。因此,ES6制定的起点其实是2000年。
  • 2007年10⽉,ECMAScript4.0版草案发布。
  • 2008年7⽉由于各⽅分歧太⼤,争论过于激烈,ECMA开会决定,中⽌ECMAScript 4.0的开发,并发布ECMAScript3.1(改名为 ECMAScript 5)。
  • 2009 年 12 ⽉,ECMAScript 5.0 版正式发布。
  • 2011 年 6 ⽉,ECMAScript 5.1 版发布,并且成为 ISO 国际标准(ISO/IEC 16262:2011)
  • 2013 年 3 ⽉,ECMAScript 6 草案冻结,不再添加新功能。新的功能设想将被放到 ECMAScript7。
  • 2013 年 12 ⽉,ECMAScript 6 草案发布。然后是 12 个⽉的讨论期,听取各⽅反馈。
  • 2015 年 6 ⽉,ECMAScript 6 正式通过,成为国际标准。从 2000 年算起,这时已经过去了 15年。
  • 2016年 ES2016(ES7) 新功能主要包括:
    •  Array.prototype.includes检查数组中是否存在值;(区别ES6字符串的includes⽅法)
    • Exponentiation Operator 求幂运算 (a ** b等价于Math.pow(a,b))
  • 2017年 ES2017(ES8) 部分功能:
    • 1.Object.values/entries/getOwnPropertyDescriptors 2.String.prototype.padStart/padEnd 3.函数参数列表和调⽤中的尾逗号(Trailing commas) 4.Async Functions 异步函数(async/await)

推荐在线⽂档:

详⻅⽹址:https://es6.ruanyifeng.com/

2、let和const命令

  • ES6新增了let和const来声明变量,主要是解决var声明变量所造成的困扰和问题:
    • var存在变量提升
    • var可以重复声明变量
    • var不⽀持块级作⽤域
    • var不能⽤于定义常量
  • let命令,⽤来声明变量。它的⽤法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
  • const声明⼀个只读的常量。⼀旦声明,常量的值就不能改变。
  • const声明的变量不得改变值,这意味着,const⼀旦声明变量,就必须⽴即初始化,不能留到以后赋值。

① 变量的提升问题

  • 由var声明的变量存在变量提升
  • var声明的变量可以在声明之前使⽤,相当于默认为其声明其值为undefined
function text1(){
 console.log(name); //undefined
 console.log(age); //undefined
 var name = "zhangsan";
 var age = 20;
 console.log(name); //zhangsan
 console.log(age); //20
}
text1();
//等价于如下
function text2(){
 var name,age;
 console.log(name); //undefined
 console.log(age); //undefined
 name = "zhangsan";
 age = 20;
 console.log(name); //zhangsan
 console.log(age); //20
}
text2();
//注意:在函数内加var为局部变量,不加var则是全局变量(在执⾏当前函数之后)
  • let声明的变量⼀旦⽤let声明,那么在声明之前,此变量都是不可⽤的,术语称为 “暂时性死区”
console.log(a); //undefined
//console.log(b); //引⽤错误ReferenceError: Cannot access 'b' before
initialization
var a = 10;
let b = 20;
console.log(a); //10
console.log(b); //20

对'暂时性死区'的理解

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

ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从⼀开始就形成了封闭作⽤域。凡是在声明之前就使⽤这些变量,就会报错。

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

var tmp ="aaa";
if(true){
 // TDZ开始
 //tmp = 'bbb'; // ReferenceError
 //console.log(tmp); // ReferenceError
 let tmp; // TDZ结束
 console.log(tmp); // undefined
 tmp = "ccc";
 console.log(tmp); // ccc
}

 重复声明变量

  • var可以重复声明变量
  • let不允许在相同作⽤域内,重复声明同⼀个变量。
function demo(c){
 var a = 10;
 var a = 20; //可以使⽤var重复声明var已经声明过的变量a
 //let a = 30; //报错,不可以使⽤let声明已经被var声明过的变量a
 //错误信息:SyntaxError: Identifier 'a' has already been declared
 let b = 30;
 //let b = 40; //报错,不可以使⽤let重复声明变量b
 //var b = 50; //报错,不可以使⽤var声明已经被let声明过的变量
 //SyntaxError: Identifier 'b' has already been declared
 //let c = 70; //报错,不可以使⽤let重复声明已存在的参数c
//SyntaxError: Identifier 'c' has already been declared
}
demo(60);

③ 块级作⽤域

  • let命令所在的代码块内有效,并且所⽤域也仅限于当前代码有效
//案例1:使⽤let声明变量只在代码块中有效
{
    var a = 10;
    let b = 20;
}
console.log(a); //10
//console.log(b); //报错 ReferenceError: b is not defined
//案例2: for循环的计数器,就很合适使⽤let命令。
for (var i = 0; i < 10; i++) {}
console.log(i); //10
for (let j = 0; j < 10; j++) {}
//console.log(j); //报错:ReferenceError: j is not defined
//案例3
var m = [];
for (var i = 0; i < 10; i++) {
    m[i] = function () {
        console.log(i);
    };
}
m[6](); // 10
//数组m成员⾥⾯的i,都指向的是同⼀个i,导致运⾏时输出的是最后⼀轮的i的值,也就是 10
var n = [];
for (let i = 0; i < 10; i++) {
    n[i] = function () {
        console.log(i);
    };
}
n[6](); // 6
//for循环变量的这部分是⼀个⽗作⽤域,⽽循环体内部是⼀个单独的⼦作⽤域
//⽽let,声明的变量仅在块级作⽤域内有效,最后输出的是6
/*
类似于如下格式
var aa = [];
{
 let i = 1;
 {
     let k = i
     aa[k] = function(){
     console.log(k)
     }
 }
 i++;
 {
     let k = i
     aa[k] = function(){
     console.log(k)
     }
 }
 ...
}
*/

④ 定义常量--const命令

  • const声明⼀个只读的常量。⼀旦声明,常量的值就不能改变。类似于java中的final关键字。
  • const声明的变量不得改变值,这意味着,const⼀旦声明变量,就必须⽴即初始化,不能留到以后赋值。
  • const的作⽤域与let命令相同:只在声明所在的块级作⽤域内有效。
  • const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后⾯使⽤。
  • const声明的常量,也与let⼀样不可重复声明。
//案例1:常量不可修改,重复声明
const PI = 3.1415926;
console.log(PI)
//PI = 3.14 //常量不可再次赋值:
// 原因:TypeError: Assignment to constant variable.
//const PI = 3.14 //不可重复声明
// 原因:SyntaxError: Identifier 'PI' has already been declared
//案例2:常量声明只在块级别所⽤域内有效
{
    const CEO = "⾸席执⾏官"
    console.log(CEO)
}
//console.log(CEO) //报错:ReferenceError: CEO is not defined
//案例3:常量也是不可提升,及必须在声明后使⽤常量
//console.log(CTO) //ReferenceError: Cannot access 'CTO' before initialization
const CTO = "⾸席技术官"
console.log(CTO)

本质

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

//定义⼀个⼈这个常量对象
const person = {name:'zhangsan',age:20};
//尝试修改⼈对象,是不可以修改的。
//person = {name:"lisi",age:22}
//TypeError: Assignment to constant variable. 分配给常量变量
//但是修改person对象中的⼀个属性值,可以成功
person.age = 30;
console.log(person); //{name: "zhangsan", age: 30}
//若是连属性都不可以修改的话,可以使⽤ES5中的Object.freeze()
const p = Object.freeze(person);
p.age = 25; //没有报错,但是修改不了属性值
console.log(person); //{name: "zhangsan", age: 30}

3、变量的解构赋值

  • ES6 允许按照⼀定模式,从数组和对象中提取值,对变量进⾏赋值,这被称为 解构(Destructuring)

1. 数组的解构赋值

2. 对象的解构赋值

3. 字符串的解构赋值

4. 数值和布尔值的解构赋值

5. 函数参数的解构赋值

6. 圆括号问题

7. ⽤途

① 数组的解构赋值

  • 属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
//声明多个变量并赋值
let a = 10;
let b = 20;
let c = 30;
//ES6 允许写成下⾯这样
let [x,y,z]=[10,20,30];
console.log(x); //10
console.log(y); //20
console.log(z); //30
//从数组中提取值,按照对应位置,对变量赋值
let [x1,[[y1],z1]]=[10,[[20],30]]; //嵌套数组
console.log(x1); //10
console.log(y1); //20
console.log(z1); //30
  • 其它数组的解构赋值
//解构赋部分数值
let [m, n] = [10,20,30];
console.log(m); //10
console.log(n); //20
let [i,[j],k] = [10,[20,30],40];
console.log(i); //10
console.log(j); //20
console.log(k); //40
let [x, , y] = [1, 2, 3];
console.log(x); //1
console.log(y); //3
let [ , , z] = ["one", "two", "three"];
console.log(z); //three
//其中...为ES6的扩展运算符,即d加上...可以接收多个值
let [a, ...d] = [1, 2, 3, 4];
console.log(a); // 1
console.log(d); // [2, 3, 4]
let [x1, y1, ...z1] = ['a'];
console.log(x1); // "a"
console.log(y1); // undefined
console.log(z1); // []
//没有接收到值得变量,默认为undefined
let [z2] = []; //z2:undefined
let [x2, y2] = [1]; //x2:1,y2:undefined
  • 如果等号的右边不是数组(或者严格地说,不是可遍历的结构(Iterator)),那么将会报错。
// 以下都会报类型错误
let [foo] = 1; //TypeError: 1 is not iterable 不是可迭代的
let [foo] = false; //TypeError: false is not iterable
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
  • 默认值:
//当⼀个数组成员严格等于undefined,默认值才会⽣效。
let [b = true] = [];
console.log(b); //true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x1, y1 = 'b'] = ['a', undefined]; // x1='a', y1='b'
console.log(x); //a
console.log(y); //b
let [m = 1] = [undefined];
console.log(m) // 1
let [n = 1] = [null]; //值不是undefined,所以没有使⽤默认值
console.log(n) // null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值