ES6简单总结

41 阅读7分钟

第1点

简介

ECMAScript 6 (以下简称 ES6 )是 JavaScript 语言的下一代标准,己于 2015 年6月正式发布。

它的目标是使 JavaScript 语言可以用于编写复杂的大型应用程序,成为企业级开发语言。

第2点 let和const命令

2.1 let命令

ES6新增,用来声明变量,用法类似var,与var区别:

1、作用域不同:

let声明的变量只在代码块内(块级作用域)有效

for循环计数器就非常适合用let命令

        function test(){
            for(let i=0; i<4; i++) {
            }
            alert(i);
        }
        test();

2、不存在变量提示:

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

这种现象多少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

为了纠正这种现象, let 命令改变了语法行为,它所声明的变量一定要在声明后使用,否则便会报错

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

3、不允许重复声明:

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

        // 报错
        function fn() { 
            let a = 10 ; 
            var a = 1;
        }
        // a 1
        function fn() { 
            var a = 10 ; 
            var a = 1;
        }

2.2 const命令

ES6 新增,const 声明一个只读的常量。一旦声明,常量的值就不能改变,let具有的特性,const也有。

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

对于简单类型的数据(数值、字符串)而言,值就保存在变量指向的内存地址中,因此等同于常量。

但对于复合类型的数据(主要是对象和数组)而言,变量指向的内存地址保存的只是个指针。

const 只能保证这个指针是固定的,至于它指向的数据结构是不是可变的。

所以,将一个对象声明为常量时必须非常小心。

    const foo = {}; 
    //为 foo 加一个属性,可以成功
    foo.prop = 123; 
    foo.prop // 123 
    //将 foo 指向另一个对象,就会报错
    foo = {}; // TypeError foo is read-only

上面的代码中,常量 foo 储存的是 一个地址,这个地址指向一个对象。

不可变的只是这个地址,即不能把 foo 指向另一个地址。

但对象本身是可变的,所以依然可以为其添加新属性。

第3点 变量的解构赋值

3.1 数组的解构赋值

ES6 允许按照一定模式从数组和对象中提取值,然后对变量进行赋值,这被称为解构。

以前,为变量赋值只能直接指定值。

    let a = 1; 
    let b = 2; 
    let c = 3; 

ES6 允许写成下面这样。

let [a, b, c] = [1, 2 , 3];

本质上,这种写法属于 “模式匹配” ,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

let [x, y, ... z] = ['a'] ; 
x // "a"
y // undefined 
z // [ ]

如果解构不成功,变量的值就等于 undefined

let [x, y] = [1, 2, 3]; 
x // 1 
y // 2

这种情况属于不完全解构,即等号左边的模式只匹配等号右边部分的的数组。这种情况下,解构依然可以成功。

3.2 对象的解构赋值

对象的解构与数组有个重要的不同。数组的元素是按次序排列的,变量的取值是由它的位置决定的:而对象的属性没有次序,变量必须与属性同名才能取到正确的值。

let { bar, foo } = { foo :"aaa", bar :"bbb"}; 
foo // "aaa"
bar //"bbb"
let { baz } = { foo :"aaa" bar :"bbb" };
baz // undefined

上面代码的第一个例子中,等号左边的两个变量的次序与等号右边两个同名属性的次序不一致,

但是对取值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于 undefined。

3.3 字符串的解构赋值

字符串也可以解构赋值。这是因为此时字符串被转换成了一个类似数组的对象。

const [a , b, c , d , e] = 'hello'; 
a // ” h ” 
b // ” e ” 
c // ” l ” 
d // ” l ” 
e // " o "

类似数组的对象都有一个 length 属性,因此还可以对这个属性进行解构赋值

let {length : len} = 'hello'; 
len // 5 

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined null 无法转为对象,所以对它们进行解构赋值时会报错。

3.4 函数参数的解构赋值

函数的参数也可以使用解构赋值。

function add ( [x, y] ) { 
	return x + y; 
}
add ( [ 1, 2 ] ) ; // 3

上面的代码中,函数 add 的参数表面上是一个数组,但在传入参数的那 刻,数组参数就被解构成变量 。

对于函数内部的代码来说,它们能感受到的参数就是x和y。

3.5 用途

变量的解构赋值用途很多

  1. 交换变量的值

    let x = 1; 
    let y = 2 ; 
    [x, y] = [y, x];
    

    这样的写法不仅简洁,而且易读,语义非常清晰。

  2. 从函数返回多个值

    函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。

    //返回一个数纽
    function example() { 
    	return [1, 2 , 3] ; 
    }
    let [a, b, c] =example() ; 
        
    //返回一个对象
    function example() { 
    	return { 
    		foo : 1, 
    		bar: 2 
        }
    }
    let { foo , bar } =example() ;
    
  3. 提取JSON数据

    解构赋值对提取 JSON 对象中的数据尤其有用。

    let  jsonData = { 
    	id: 42, 
    	status :"OK", 
    	data: [867, 5309]  
    }
    let { id, status, data: number } = jsonData;
    console.log(id,status,number);
    // 42 ,”OK”,[867, 5309] 
    

    上面的代码可以快速提取 JSON 数据的值。

  4. 输入模块的指定方法

    加载模块时,往往需要指定输入的方法,解构赋值使得输入语句非常清晰。

    const {SourceMapConsumer, SourceNode } = require ("source-map");
    

第4点 字符串的扩展

ES6 加强了对 Unicode 的支持,并且扩展了字符串对象

4.1 字符串的遍历器接口

ES6 为字符串添加了遍历器接口,使得字符串可以用 for ... of 循环遍历。

for (let codePoint of 'foo') { 
	console.log(codePoint) 
}
// "f" 
// "o"
// "o"

除了遍历字符串,这个遍历器最大的优点是可以识别大于 OxFFFF 的码点,传统的 for循环无法识别这样的码点。

var text = String.fromCodePoint(Ox20BB7);
for (let i = 0; i < text.length; i++) { 
	console.log(text[i]); 
}
// " "
// " "
for (let i of text) { 
	console.log(i);
}
// "吉"

上面的代码中,字符串 text 只有一个字符,但是 for 循环会认为它包含2个字符(都不可打印),for ... of 循环会正确识别出这个字符。

4.2 repeat()

repeat 方法返回一个新字符串,表示将原字符串重复n次。

'x'.repeat (3)  // "xxx"
'hello'.repeat(2)  // "hellohello"
'na'.repeat(O)  // ""

如果 repeat 的参数是负数或者 Infinity(无穷) ,会报错。

参数如果是小数,会被取整。

如果 repeat 的参数是字符串,则会先转换成数字。

但如果参数是0到-1之间的小数,则等同于0,这是因为会先进行取整运算。

4.3 padStart()、padEnd()

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补

padStart() 用于头部补全, padEnd() 用于尾部补全。

'x'.padStart (5, 'ab')  // ababx' 
'x'.padStart(4, 'ab')  // 'abax' 
'x'.padEnd(5, 'ab')  // 'xabab' 
'x'.padEnd(4 , 'ab')  // 'xaba'                   

上面的代码中 padStart 和 padEnd 接受两个参数,第一个参数用来指定符串的最小长度,第二个参数则是用来补全的字符串。

如果原字符串的长度等于或大于指定的最小长度,则返回原字符串。

'xxx'.padStart(2, 'ab')  // 'xxx'
'xxx'.padEnd(2, 'ab')  // 'xxx'

如果用来补全的字符串与原字符串长度之和超过了指定的最小长度,则会截去超出位数的补全字符串。

'abc'.padStart (10, '0123456789') 
// '0123456abc'

padStart 的常见用途是为数值补全指定位数。下面的代码将生成 10 位的数值字符串。

'1'.padStart(10, '0') // '0000000001' 
'12'.padStart(10, '0')// '0000000012'

另一个用途是提示字符串格式。

'12'. padStart(10, 'YYYY-MM-DD') // 'YYYY- MM- 12' 
'09-12'. padStart(10, 'YYYY-MM-DD')// 'YYYY-09-12'

第5点 正则的扩展

5.1 flags 属性

ES6 为正则表达式新增了 flags 属性,会返回正则表达式的修饰符。

// ES6 flags 属性
// 返回正则表达式的修饰符
/abc/ig.flags 
// 'ig'

5.2 RegExp构造函数

在ES5 中,RegExp 构造函数的参数有两种情况。

第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符。

var regex = new RegExp ('xyz', 'i') ; 
// 等价于
var regex = /xyz/i;

第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。

var regex = new RegExp (/xyz/i);
// 等价于
var regex = /xyz/i;

但是, ES5 不允许此时使用第二个参数添加修饰符,否则会报错。

var regex = new RegExp(/xyz/, 'i'); 
// Uncaught TypeError: Cannot supply flags 
// when constructing one RegExp from another

ES6 改变了这种行为 如果 RegExp 构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有正则表达式的修饰符,只使用新指定的修饰符。

new RegExp (/abc/ig, 'i').flags 
// 'i'

上面的代码中,原有正则对象的修饰符是ig,它会被第二个参数 i 覆盖。

第6点 数值的扩展

6.1 二进制和八进制表示法

ES6 提供二进制和八进制数值的新写法,分别用前缀 Ob 或(OB )和 0o(或 0O )表示。

Ob11111O111 === 503 
Oo767 === 503 
// true 
// true

如果要将使用 Ob Ox 前缀的字符串数值转为十进制数值,要使用 Number 方法。

Number ( 'Ob111' ) // 7 
Number ( 'Oo1O' ) // 8

6.2 Number.parselnt()、 Number. parseFloat()

ES6将全局方法 parseInt () 和 parseFloat() 移植到了 Number 对象上面,行为完全保持不变。

// ES5 的写法
parseInt ('12.34') // 12 
parseFloat ('123.45#') // 123.45
// ES6 的写法
Number.parseInt ('12.34'// 12 
Nurnber.parseFloat ('123.45#') // 123.45

这样做的目的是逐步减少全局性方法,使得语言逐步模块化。

6.3 Number.islnteger()

Nurnber islnteger() 用来判断一个值是否为整数。需要注意的是,在 JavaScript 内部,

整数和浮点数是同样的储存方法,所以3和3.0 被视为同一个值。

Number.islnteger(25) // true
Number.islnteger(25.0) // true
Number.islnteger(25.1) // false 
Number.isinteger ('15') // false
Number.isinteger(true) // false

6.4 Math 对象的扩展

ES6在 Math 对象上新增了17个与数学相关的方法。所有这些方法都是静态方法,只能在Math 对象上调用。

6.4.1 Math.trunc()

Math.trunc 方法用于去除一个数的小数部分,返回整数部分。

Math.trunc(4.1) // 4 
Math.trunc(4.9) // 4 
Math.trunc(-4.1) // -4 
Math.trunc(-4.9) // -4 
Math.trunc(-0.1234) // -0

对于非数值, Math.trunc 内部使用 Number 方法将其先转为数值。

对于空值和无法截取整数的值,返回 NaN。

Math.trunc ('123.456') // 123
Math.trunc ('foo') //NaN

6.4.2 Math.sign()

Math.sign 方法用来判断一个数到底是正数、负数,还是零,对于非数值,会先将其转换为数值。

其返回值有5种情况。

  • 参数为正数,返回+1;
  • 参数为负数,返回-1;
  • 参数为0,返回0;
  • 参数为-0 ,返回-0;
  • 其他值,返回 NaN;

6.4.3 Math.Cb()

Math.cbrt 方法用于计算一个数的立方根。

Math.cbrt ('8') // 2

......

第7点 函数的扩展

7.1 函数参数的默认值

7.1.1 基本用法

在ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

function log (x, y) { 
	y = y || 'World'; 
	console.log(x, y);
}
log('hello') //hello World
log('hello','China') //hello China
log('hello','') //hello World

上面的代码检查函数 log 的参数y有没有赋值,如果没有,则指定默认值为 World 。

这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为 false ,则该赋值不起作用。

就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再令其等于默认值。

if (typeof y === 'undefined'){
	y ='World';
}

ES6 允许为函数的参数设置默认值,也就是直接写在参数定义的后面。

function log(x , y = 'World') { 
	console.log (x, y) ;
}
log('hello') //hello World
log('hello','China') //hello China
log('hello','') //hello

可以看到, ES6的写法比 ES5 简洁许多,而且非常自然。

7.1.2 与解构赋值默认值结合使用

参数默认值可以与解构赋值的默认值结合起来使用。

function foo({x , y = 5}) { 
	console.log(x , y);
}
foo ({}) // undefined, 5
foo ({x:1} ) // 1, 5
foo ({x:1,y:2}) //1, 2
foo() // TypeError: Cannot read property ’ x ’ of undefined

上面的代码使用了对象的解构赋值默认值,而没有使用函数参数的默认值。

只有当函数 foo的参数是一个对象时,变量 x和y才会通过解构赋值而生成。

如果函数 foo 调用时参数不是对象,变量 x和y就不会生成,从而报错。

只有参数对象没有y属性时, y的默认值5才会生效。

7.1.3 作用域

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。

等到初始化结束,这个作用域就会消失,这种语法行为在不设置参数默认值时是不会出现的。

var x = 1; 
function f (x,y = x) { 
	console.log(y);
}
f(2) //2

上面的代码中,参数y的默认值等于变量x。调用函数 f 的时侯,参数形成一个单独的作用域。

在这个作用域里面,默认值变量x指向第一个参数 ,而不是全局变量 ,所以输出是2。

7.1.4 应用

利用参数默认值可以指定某一个参数不得省略,如果省略就抛出一个错误。

function throwifMissing() { 
	throw new Error ('Missing parameter');
}
function foo (mustBeProvided = throwifMissing() ) { 
	return mustBeProvided; 
}
foo() 
// Error: Missing parameter

如果调用的时候没有参数,以上代码中的 foo 函数就会调用默认值 throwifMissing 函数,从而抛出一个错误。

7.2 rest参数

ES6 引入了 rest 参数(形式为“... 变量名“),用于获取函数的多余参数,这样就不使用 arguments 对象了 。rest 参数搭配的变量是一个数组,该变量将多余的参数放入其中。

function add ( ...values) {
	let sum = 0; 
	for (var val of values) {
		sum += val ; 
		return sum; 
    }
}
add (2 , 5, 3) // 10

rest 参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。

function push(array, ... items} { 
	items.forEach(function(item) { 
		array.push (item);
		console.log(item); 
});
var a= [];
push(a, 1 , 2 , 3)

rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

7.3 箭头函数

7.3.1 基本方法

ES6 允许使用“箭头”(=>)定义函数。

var f = v => v ;

上面的箭头函数等同于以下代码。

var f = function(v) { 
	return v;
}

如果箭头函数不需要参数或需要多个参数,就使用圆括号代表参数部分。

var f = () => 5;
//等同
var f = function () { return 5 };
var sum = (num1 , num2) => num1 + num2; 
//等同
var sum= function(num1, num2) { 
	return num1 + num2;
}

如果箭头函数的代码块部分多于一条语句,就要使用大括号将其括起来,井使用 return语句返回。

var sum = (numl, num2) => { return numl + num2; }

箭头函数可以与变量解构结合使用。

canst full = ({ first , last }) => rst +''+ last;
//等同于
function full(person) { 
	return person.first +''+ person.last;
}

箭头函数使得表达更加简洁。

7.3.2 注意事项

箭头函数有以下几个使用注意事项。

  1. this指向的是调用该函数的对象(this的指向是固定的)。
  2. 不可以当作构造函数。 也就是说,不可以使用 new 命令,否则会抛出一个错误。
  3. 不可以使用 arguments 对象 该对象在函数体内不存在。

其中,第一点尤其值得注意。

function foo () { 
	setTimeout (() => { 
		console.log ('id :', this.id);
	},100) ; 
var id = 21; 
foo.call({ id : 42 });
// id : 42

上面的代码中, setTimeout的参数是一个箭头函数,这个箭头函数的定义是在 foo 函数生成时生效的,

而它真正执行要等到 100ms后,如果是普通函数,执行时 this 应该指向全局对象 window 这时应该输出 21 ,

但是,箭头函数导致 this 总是指向函数定义生效时所在的对象,所以输出的是 42。

第8点 数组的扩展

8.1 扩展运算符

8.1.1 含义

扩展运算符( spread )是三个点( ...),将一个数组转为用逗号分隔的参数序列。

console.log ( ...[ 1 , 2 , 3 ] ) 
// 1 2 3 
console.log(1 ,...(2 , 3 , 4 ], 5 ) 
// 1 2 3 4 5

该运算符主要用于函数调用。

function add(x , y) { 
	return x + y ;
}
var numbers= [4, 38];
add( ...numbers) // 42

扩展运算符后面还可以放置表达式。

const arr = [ 
	...(x > 0 ? [ 'a' ]: []), 
	'b',
];
8.1.2 应用

合并数组

扩展运算符提供了数组合并的新写法。

// ESS 
[1,2].concat (more) 
// ES6 
[1, 2, ...more]

字符串

扩展运算符还可以将字符串转为真正的数组。

[... 'hello'] 
// [ 'h','e','l','l','o']

8.2 Array.from()

Array.from 方法用于将两类对象转为真正的数组:类似数组的对象和可遍历对象(包括ES6 新增的数据结构Set 和Map )。

下面是一个类似数组的对象,Array.from将它转为真正的数组。

let arrayLike = { 
	'0' : 'a', 
	'1': 'b', 
	'2' : 'c', 
	length: 3
}
// ES6 写法
let arr2 = Array.from(arrayLike);  // [ 'a','b','c' ]

只要是部署了 Iterator 接口的数据结构 Array.from 都能将其转为数组。

Array.from ('hello') 
//[ 'h','e','l','l','o']
let namesSet =new Set (['a', 'b'] ) 
Array.from(namesSet) // [ 'a', 'b' ]

上面的代码中,字符串和 Set 结构都具有 Iterator 接口,因此可以被 Array.from 转为真正的数组。

如果参数是一个真正的数组, Array from 会返回一个一模一样的新数组。

8.3 数组实例 find() 和 findIndex()

数组实例的 find 方法用于找出第一个符合条件的数组成员。它的参数是一个回调函数,

所有数组成员依次执行该回调函数 直到找出第一个返回值为 true 的成员,然后返回该成员。

如果没有符合条件的成员,则返回 undefined。

[1 , 4 , - 5 , 10].find( (n) => n < 0) 
// -5
[1 , 5 , 10, 15].find (function (value , index , arr) { 
	return value > 9; 
})
// 10
//上面的代码中 find 方法的回调函数可以接受3个参数,依次为当前的值、当前的位置和原数组。

数组实例的 findIndex 方法的用法与 find 方法非常类似,返回第一个符合条件的数组成员的位置,

如果所有成员都不符合条件,则返回-1。

[1, 5 , 10 , 15].findindex (function (value , index , arr) { 
	return value > 9 ;
})
// 2

8.4 数组实例的 includes()

Array.prototype.includes 方法返回一个布尔值,表示某个数组是否包含给定的值,

与字符串的 includes 方法类似。

[1 , 2 , 3].includes(2) // true 
[1 , 2 , 3].includes(4) // false 
[1 , 2 , NaN].includes (NaN) // true

第9点 对象的扩展

9.1 Object.assign()

Object.assign 方法用于将源对象的所有可枚举属性复制到目标对象( target )。

var target= { a : 1 }; 
var source1 = { b : 2 }; 
var source2 = { c : 3 }; 
Object.assign(target , source1 , source2);
// target {a : 1 , b : 2 , c : 3}
// Object.assign 方法的第一个参数是目标对象,后面的参数都是源对象。

Object.assign 方法实行的是浅复制,而不是深复制。也就是说,如果源对象某个属性的值是对象,那么目标对象复制得到的是这个对象的引用。

var obj1 = {a : {b : 1}} ; 
var obj2 =Object.assign({} , obj1) ;
obj1.a.b = 2;
obj2.a.b //2

上面的代码中,源对象 obj 属性的值是 个对象, Object.assign 复制得到的是这个对象的引用。这个对象的任何变化都会反映到目标对象上面。

9.2 对象的扩展运算待

数组有扩展运算符,ES2017 将这个运算符引入了对象。

解构赋值

对象的解构赋值用于从一个对象取值,相当于将所有可遍历的属性分配到指定的对象上面。所有的键和它们的值都会复制到新对象上面。

let { x , y, ... z } = { x : 1, y : 2, a: 3 , b : 4 }; 
x // 1 
y // 2 
z // {a: 3 , b: 4 }

解构赋值必须是最后一个参数,否则会报错

let { ... x , y , z } =obj ; // 句法错误
let { x , ... y, ... z } = obj ; // 句法错误

上面的代码中,解构赋值不是最后一个参数,所以会报错。

注意:解构赋值的复制是浅复制, 如果一个键的值是复合类型的值(数组、对象、函数),那么解构赋值复制的是这个值的引用,而不是这个值的副本。

扩展运算符

扩展运算符(.. . )用于取出参数对象的所有可遍历属性,并将其复制到当前对象之中。

let z = { a : 3 , b : 4 } ; 
let n = { ... z } ; 
n // { a : 3 , b: 4 }

这等同于使用 Object.assign 方法。

第10点 Set 数据结构

10.1 基本用法

ES6 提供了新的数据结构 一 Set。它类似于数组,但是成员的值都是唯一的,没有重复。

Set 本身是一个构造函数,用来生成 Set 数据结构。

const s =new Set() ;
[2, 3 , 5 , 4 , 5 , 2 , 2] .forEach(x => s.add(x)) ; 
for (let i of s) { 
	console.log(i); 
}
// 2 3 5 4

上面的代码通过 add 方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。

Set 函数可以接受一个数组作为参数,用来初始化。

const set = new Set ( [1, 2 , 3 , 4, 4]); 
[ ... set] 
// [1, 2 , 3 , 4]

去除数组的重复成员。

[ ... new Set (array) ]

Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。

4个操作方法:

  • add (value ):添加某个值,返回 Set 结构本身。

    s.add(1).add(2).add(2);
    // 注意2被加入了两次
    s.size // 2
    
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

    s.delete (2) ; 
    s.has(2) // false
    
  • has (value):返回一个布尔值,表示参数是否为 Set 的成员。

    s.has(1) //true 
    s.has(2) // true 
    s.has(3) // false
    
  • clear(): 清除所有成员,没有返回值。

有4个遍历方法:

  • keys() : 返回键名的遍历器。

  • values() :返回键值的遍历器。

  • entries() :返回键值对的遍历器。

  • forEach() :使用回调函数遍历每个成员。

    //结构实例的 forEach 方法用于对每个成员执行某种操作,没有返回值。
    let set = new Set ( [1, 2, 3]) ; 
    set.forEach ((value , key) => console.log (value * 2) ) 
    // 2 
    // 4 
    // 6