es6语法特性学习

ES6简介
历时将近6年的时间来制定的新 ECMAScript 标准 ECMAScript 6(亦称 ECMAScript Harmony,简称 ES6)终于在 2015 年 6 月正式发布。自从上一个标准版本 ES5 在 2009 年发布以后,ES6 就一直以新语法、新特性的优越性吸引著众多 JavaScript 开发者,驱使他们积极尝鲜。
​ 由于ES6是在2015年发布的,所以也叫ES2015。
以后ESCMAScript标准一年一更新,统一使用年份命名:ES2016、ES2017、….

块级作用域绑定
在ES5之前,不存在块级作用域,在编程的时候很多时候会带来很多的不便,ES6新增了块级作用域,补足了这方面的缺陷。
块级声明指的是该声明的变量无法被代码块外部访问。块作用域,又被称为词法作用域(lexical scopes),可以在如下的条件下创建:
1.函数内部
2.在代码块(即 { })内部
块级作用域是很多类C语言的工作机制,ECMAScript 6 引入块级声明的目的是增强 JavaScript 的灵活性,同时又能与其它编程语言保持一致。
let声明
使用let声明变量的语法和使用var声明的语法是一样的。但是let声明的变量的作用域会限制在当前的代码块中。这是let与var的最大区别。

1
2
3
4
5
6
7
let a = 10;
if(a > 5){
console.log(b); //用let声明的变量没有声明提前这一特性,所以此处也访问不到(报错)
let b = 20;
console.log(b);
}
console.log(b); //由于b是在if块中使用let声明的,所以此处无法访问到。(报错)

注意:
1.用 let 声明的变量具有块级作用域,只能在声明的块中访问,在块外面无法访问
2.用let声明的变量也没有声明提前这一特性。
3.在同一个块中,let声明的变量也不能重复声明。
4.在声明变量的时候尽量使用let,慢慢的抛弃var
const声明
在 ES6 使用const来声明的变量称之为常量。这意味着它们不能再次被赋值。由于这个原因,所有的 const 声明的变量都必须在声明处初始化。
const声明的常量和let变量一样也是具有块级作用域的特性。

1
2
3
4
5
6
var a = 20;
if (true) {
const b = 20;
b = 30; //错误! 常量不能重新赋值
const c; //错误! 常量声明的同时必须赋值。
}

注意:
1.const的特性除了声明的是常量为,其他与let一样。
2.在let和const声明前的这段区域称之为暂存性死区(The Temporal Dead Zone —TDZ)。
3.使用let和const声明的变量和常量不再是window的属性。 也就是说通过window.a是无法访问到的。
循环中的块级绑定
使用var声明的循环变量在循环结束后仍然可以访问到。 使用let声明的循环变量,在循环结束之后会立即销毁。

1
2
3
4
for(let i = 0; i < 3; i++){ // 循环结束之后会立即销毁 i
console.log(i);
}
console.log(i); //此处无法访问到 i 。

循环中的函数
看下面的代码,是输出10个10,而不是0,1,2,…

1
2
3
4
5
6
7
8
9
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i);
});
}
funcs.forEach(function (func) {
func(); // 输出 "10" 共10次
});

解决办法需要使用函数的自执行特性。

1
2
3
4
5
6
7
8
9
10
11
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
funcs.forEach(function(func) {
func(); // 输出 0,1,2 ... 9
});

如果使用let声明变量,则完全可以避免前面的问题。 这是ES6规范中专门定义的特性。在for … in和for … of循环中也适用;

1
2
3
4
5
6
7
8
9
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i);
});
}
funcs.forEach(function (func) {
func(); // 输出 0,1,2 ... 9
})

说明:
let 声明使得每次迭代都会创建一个变量 i,所以循环内部创建的函数会获得各自的变量 i 的拷贝。每份拷贝都会在每次迭代的开始被创建并被赋值。

函数的新增特性

JavaScript函数的最大的一个特点就是在传递参数的时候,参数的个数不受限制的。为了健壮性考虑,一般在函数内部需要做一些默认值的处理。

1
2
3
4
function makeRequest(url, timeout, callback) {
timeout = timeout || 2000;
callback = callback || function() {};
}

其实上面的默认值方法有个bug:当timeout是0的时候也会当做假值来处理,从而给赋值默认值2000.
ES6从语言层面面上增加了 默认值的 支持。看下面的代码:

1
2
3
4
5
//这个函数如果只传入第一个参数,后面两个不传入,则会使用默认值。如果后面两个也传入了参数,则不会使用默认值。
function makeRequest(url, timeout = 2000, callback = function() {}) {
// 其余代码
}

默认参数对 arguments 对象的影响
在非严格模式下,arguments总是能反映出命名参数的变化。看下面的代码:

1
2
3
4
5
6
7
8
9
10
function foo(a, b) {
//非严格模式
console.log(arguments[0] === a); //true
console.log(arguments[1] === b); //true
a = 10;
b = 20;
console.log(arguments[0] === a); //true
console.log(arguments[1] === b); //true
}
foo(1, 2);

在ES5的严格模式下,arguments只反映参数的初始值,而不再反映命名参数的变化!

1
2
3
4
5
6
7
8
9
10
11
12
function foo(a, b) {
//严格模式
"use strict"
console.log(arguments[0] === a); //true
console.log(arguments[1] === b); //true
a = 10;
b = 20;
console.log(arguments[0] === a); //false。 修改a的值不会影响到arguments[0]的值
console.log(arguments[1] === b); //false
}
foo(1, 2);

当使用ES6参数默认值的时候,不管是否是在严格模式下,都和ES5的严格模式相同。看下面的代码:

1
2
3
4
5
6
7
8
9
function foo(a, b = 30) {
console.log(arguments[0] === a); //true
console.log(arguments[1] === b); //true
a = 10;
b = 20;
console.log(arguments[0] === a); //false。 由于b使用了默认值。虽然a没有使用默认值,但是仍然表现的和严格模式一样。
console.log(arguments[1] === b); //false。 b使用了默认值,所以表现的和严格模式一样。
}
foo(1, 2);

注意:如果这样调用foo(1),则 a == 1, b == 30, arguments[0] == 1, arguments[1] == undefined。也就是说默认值并不会赋值给arguments参数。

默认参数表达式 (Default Parameter Expressions)
参数的默认值,也可以是一个表达式或者函数调用等。看下面的代码

1
2
3
4
5
6
7
8
function getValue() {
return 5;
}
function add(first, second = getValue()) { //表示使用getValue这个函数的返回值作为second的默认值。
return first + second;
}
console.log(add(1, 1)); // 2. 调用add函数的时候,传入了第二个参数,则以传入的参数为准。
console.log(add(1)); // 6。 调用add函数的时候,没有传入第二个参数,则会调用getValue函数。

有一点需要要注意:getValue()只会在调用add且不传入第二个参数的时候才会去调用。不是在解析阶段调用的。

1
2
3
4
5
6
7
8
9
10
11
let value = 5;
function getValue() {
return value++;
}
function add(first, second = getValue()) { //
return first + second;
}
console.log(add(1, 1)); // 2
console.log(add(1)); // 6
console.log(add(1)); // 7
console.log(add(1)); // 8

由于默认值可以表达式,所以我们甚至可以使用前面的参数作为后面参数的默认值。

1
2
3
function add(first, second = first) { // 使用第一个参数作为第二个参数的默认值
return first + second;
}

注意:可以把前面的参数作为后面参数的默认值,但是不能把后面的参数作为第一个参数的默认值。这可以前面说的let和const的暂存性死区一个意思。

1
2
3
function add(first = second, second)) { // 这种写法是错误的
return first + second;
}

未命名参数问题
Javascript并不限制传入的参数的数量。在调用函数的时候,传入的实参的个数超过形参的个数的时候,超过的部分就成为了未命名参数。在ES5之前,我们一般可以通过arguments对象来获取到未命名参数的值。但是略显繁琐。

1
2
3
4
5
function foo(a) {
console.log(a);
console.log(arguments[1]) //取得传入的多余的参数。
}
foo(2, 3);

ES6,提供了一种更加优雅处理未命名参数的问题:剩余参数( Rest Parameters )
语法:function a(a, … b){ }
剩余参数使用三个点( … )和变量名来表示。

1
2
3
4
5
function foo(a, ...b) {
console.log(a);
console.log(b instanceof Array); //true .多余的参数都被放入了b中。b其实就是一个数组。
}
foo(2, 3, 4, 6);

注意:
1.函数最多只能有一个剩余参数b。而且这个剩余参数必须位于参数列表的最后位置。
2.虽然有了剩余参数,但是arguments仍然存在,但是arguments完全无视了剩余参数的存在。
3.剩余参数是在函数声明的时候出现的。

函数中的扩展运算符

例如:Math中的max函数可以返回任意多个参数中的最大值。但是如果这些参数在一个数组中,则没有办法直接传入。以前通用的做法是使用applay方法。
看下面的代码:

1
2
let values = [25, 50, 75, 100]
console.log(Math.max.apply(Math, values)); // 100

上面这种方法虽然可行,但是总是不是那么直观。
使用ES6提供的扩展运算符可以很容易的解决这个问题。在数组前加前缀 … (三个点)。

1
2
3
let values = [25, 50, 75, 100]
console.log(Math.max(...values)); //使用扩展运算符。相当于拆解了数组了。
console.log(Math.max(...values, 200)); //也可以使用扩展运算符和参数的混用,则这个时候就有 5 个数参与比较了。

注意:剩余参数和扩展运算符都是 使用三个点作为前缀。但是他们使用的位置是不一样的。
1.剩余参数是用在函数的声明的时候的参数列表中,而且必须在参数列表的后面
2.扩展运算符是用在函数调用的时候作为实参来传递的,在实参中的位置没有限制。

访客的ip和所在地址: 访问时间: 当前时间:

殖民 wechat
欢迎您扫一扫上面的微信公众号
打赏不在多少,请鼓励一下!