ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。es6
let 命令,用来声明变量。它的用法相似于 var,区别在于 var 声明的变量全局有效,let 声明的变量只在它所在的代码块内有效。算法
// 变量i储存的值是10,因此执行a[2]()后输出10 var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[2](); // 10 // 修正方法 // 闭包会使得函数中的变量都被保存在内存中,因此执行a[2]()后输出2 var a = []; for (var i = 0; i < 10; i++) { (function (i) { a[i] = function () { console.log(i) } })(i); } a[2](); // 2 // es6 // let声明的i只在当前的代码块有效,因此每次for循环至关于用let从新声明一次i var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[2](); // 2 // 注:JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
let 不存在变量提高,必须先声明后使用,不然报错;var 存在变量提高,未声明前使用输出 undefined。数组
let 存在暂时性死区,在代码块内,使用 let 命令声明变量以前,该变量都是不可用的。浏览器
let 不容许重复声明。安全
const 声明一个只读的常量。一旦声明,常量的值就不能改变。不能只声明不赋值。数据结构
const a = 10; a = 20; // 报错 const b; // 报错
const 的做用域与 let 相同。闭包
if(true) { const num = 5; } console.log(num); // 报错
const 声明对象,常量对象内存地址,所以对象自己可改,可是给常量从新赋值就会报错。函数
const obj = {}; obj.a = 'a'; obj = {}; // 报错
ES5 规定,函数只能在顶层做用域和函数做用域之中声明,不能在块级做用域声明。可是在 ES6 中,函数能够在块级做用域中声明。可是,市面上不少浏览器都不支持 ES6,因此应该避免在块级做用与中声明函数。优化
ES6 容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。this
模式匹配赋值,若是解构不成功,变量的值就等于 undefined。
let [a, [[b], c]] = [1, [[2], 3]]; console.log(a,b,c); // 1, 2, 3 let [x, , y, z] = [1, 2, 3]; console.log(x); // 1 console.log(y); // 3 console.log(z); // undefined
不彻底解构赋值,等号左边的模式,只匹配一部分的等号右边的数组。
let [x, [y], z] = [1, [2, 3], 4]; console.log(x); // 1 console.log(y); // 2 console.log(z); // 4
数组结构赋值右边必须是数组,模式不匹配则报错。
let [a] = {}; // 报错
解构赋值能够添加默认值,而且能够引用解构赋值的其余变量。
let [a = 1, b = 2] = [, 3]; console.log(a); // 1 console.log(b); // 3 let [x = 1, y = x] = []; // x = 1; y = 1 let [x = 1, y = x] = [2]; // x = 1; y = 2
数组解构赋值可用于交换变量的值。
let [a, b] = [1, 2]; console.log(a, b); // 1, 2 [b, a] = [a, b]; console.log(a, b); // 2, 1
变量必须与属性同名
let { a, b, c } = { a: 'aaa', b: 'bbb' }; console.log(a); // 'aaa' console.log(b); // 'bbb' console.log(c); // undefined
变量名与属性名不一致
let { a: x, b: y } = { a: 'aaa', b: 'bbb' }; console.log(x); // 'aaa' console.log(y); // 'bbb'
嵌套赋值,若是子对象所在的父属性不存在,会报错,慎用。
let { a, a: {x}, b: y } = { a: {x: 'xxx',y: 'yyy'}, b: "bbb" }; console.log(a); // { x: 'xxx', y: 'yyy' } console.log(x); // 'xxx' let {c: {d: {e}}} = {c: 'ccc'}; // 报错 console.log(e)
变量解构赋值也和数组的解构赋值同样,能够赋默认值,变量解构赋值时,不能将大括号写在行首,否者 JavaScript 引擎将会按代码块执行。
let x; {x} = {x: 1}; // 报错 // 正确写法 let x; ({x} = {x: 1});
字符串解构赋值,将字符串转化成数组对象
const [a,b,c] = '123456789'; const {length} = '123456789'; console.log(a, b, c, length); // 1, 2, 3, 9
const arr = [[1, 2], [3, 4]].map(([a, b]) => a + b); console.log(arr); // [ 3, 7 ]
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。因为 undefined 和 null 没法转为对象,因此对它们进行解构赋值,都会报错。
let {toString: n} = 123; n === Number.prototype.toString // true let {toString: b} = true; b === Boolean.prototype.toString // true let { prop: u } = undefined; // 报错 let { prop: n } = null; // 报错
const map = new Map(); map.set('first', 'hello'); map.set('second', 'world'); for (let [key, value] of map) { console.log(key + ' is ' + value); } // first is hello // second is world
for(let codePoint of 'string'){ console.log(codePoint) } // 's' // 't' // 'r' // 'i' // 'n' // 'g'
三个方法都接收两个参数,第一个参数为检索的值,第二个参数为检索的起始位置,返回布尔值
let s = 'Hello world!'; const [a, b, c] = [ s.startsWith('Hello', 2), s.endsWith('!'), s.includes('o w') ]; console.log(a, b, c); // false true true
repeat 方法返回一个新字符串,表示将原字符串重复 n 次。
'str'.repeat('3') // 'strstrstr'
padStart(),padEnd()有两个参数,第一个参数为字符串补全生效的最大长度,第二个参数为补全的字符串。
第二个参数默认为空格,省略第二个参数时默认用空格补全。
第一个参数小于字符串原长度时,返回原字符串。
若是用来补全的字符串与原字符串,二者的长度之和超过了最大长度,则会截去超出位数的补全字符串。
常见用途:补全指定位数,提示字符串格式。
'123456'.padStart(10, '0') // "0000123456" '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
const str = 'world'; const template = `Hello ${str}`; console.log(template); // Hello world
使用二进制表示法,前缀为 0b,使用八进制表示法,前缀为 0o,ES6 不支持使用 00 前缀表示八进制。
进制转换使用 toString 方法,使用 Number 方法直接转十进制。
0b1100100 === 100; // true 0o144 === 100; // true (0b1100100).toString(8); // 144 (0b1100100).toString(10); // 100 Number('0b1100100'); // 100
Number.isFinite()用来检查一个数值是否为有限的(finite),即不是 Infinity。参数类型不是数值,Number.isFinite 一概返回 false。
Number.isNaN()用来检查一个值是否为 NaN。参数类型不是 NaN,Number.isNaN 一概返回 false。
Number.isFinite(15); // true Number.isFinite(-Infinity); // false Number.isNaN(15) // false Number.isNaN(9/0) // true
ES6 将全局方法 parseInt()和 parseFloat(),移植到 Number 对象上面,行为彻底保持不变。
Number.isInteger()用来判断一个数值是否为整数。
Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false
Number.isSafeInteger() 用来判断一个整数是否落在安全整数范围以内。
Number.isSafeInteger(9007199254740993) // false Number.isSafeInteger(990) // true Number.isSafeInteger(9007199254740993 - 990) // true
Math.trunc() 除去一个数的小数部分,返回整数部分。参数不是数值,内部会先调用 Nunber()专为数值,对于空值和没法截取整数的值,返回 NaN。(Math 对象的扩展的方法对于非数值的处理方法都同样)
Math.trunc(5.9) // 5 Math.trunc(-4.9) // -4 Math.trunc(null) // 0 Math.trunc('foo'); // NaN
Math.sign() 判断一个数是正数、负数、仍是零。
Math.sign(-5) // -1 负数 Math.sign(5) // +1 正数 Math.sign(0) // +0 零 Math.sign(-0) // -0 零 Math.sign(NaN) // NaN
Math.cbrt() 计算一个数的立方根。
Math.cbrt(2) // 1.2599210498948734 // Math.sqrt(x) 计算平方根 Math.sqrt(2) // 1.4142135623730951 // 幂运算 Math.pow(x,y) Math.pow(2, 3)
Math.hypot() 返回全部参数的平方和的平方根。
Math.hypot(3, 4); // 5 Math.hypot(3, 4, 5); // 7.0710678118654755
ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。只能是最后一个参数,函数的 length 属性,不包括 rest 参数。
function sum1(x, y, ...args) { let sum = 0; for (let arg of args) { sum += arg; } return sum; } console.log(sum1(1, 2, 3, 4)) // 7 function sum2(...args) { return args.reduce((prev, curr) => { return prev + curr }, 0) } console.log(sum2(1, 2, 3)); // 6
函数的 name 属性,返回该函数的函数名。对于匿名函数,ES5 返回'',ES6 返回变量名;
Function 构造函数返回的函数实例,name 属性的值为 anonymous;bind 返回的函数,name 属性值会加上 bound 前缀。
function fn() {} fn.name // 'fn' function foo() {}; foo.bind({}).name // 'bound foo' (function(){}).bind({}).name // 'bound '
const fn = v => v; // 等同于 const fn = function (v) { return v; };
注意要点
尾调用指函数的最后一步是调用另外一个函数。
function f(x){ 'use strict'; return g(x); }
函数调用会在内存造成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。若是在函数 A 的内部调用函数 B,那么在 A 的调用帧上方,还会造成一个 B 的调用帧。等到 B 运行结束,将结果返回到 A,B 的调用帧才会消失。若是函数 B 内部还调用函数 C,那就还有一个 C 的调用帧,以此类推。全部的调用帧,就造成一个“调用栈”(call stack)。
尾调用因为是函数的最后一步操做,因此不须要保留外层函数的调用帧,由于调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就能够了,这样能够防止内存溢出,达成尾调用优化。
ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。
扩展运算符(spread)是三个点(...)。它比如 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
const arr = [1, 2, 3]; arr.push(...[4, 5, 6]);
扩展运算符的应用
const arr = [1, 2, 3]; ...arr // 1, 2, 3
const a1 = [1, 2]; // 写法一 const a2 = [...a1]; // 写法二 const [...a2] = a1; // 至关于 const a1 = [1, 2]; const a2 = a1.concat();
const arr1 = ['a', 'b']; const arr2 = ['c']; const arr3 = ['d', 'e']; // ES5 的合并数组 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6 的合并数组 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
const list = [1, 2, 3]; [a, ...b] = list; console.log(a) // 1 console.log(b) // [2, 3] [...'hello'] // ['h', 'e', 'l', 'l', 'o']
Array.from 方法用于将两类对象转为真正的数组:相似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
常见的相似数组的对象有 DOM 操做返回的 NodeList 集合,以及函数内部的 arguments 对象。
let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; // ES5的写法 var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c'] // ES6的写法 let arr2 = Array.from(arrayLike); // ['a', 'b', 'c'] Array.from('hello'); // ['h', 'e', 'l', 'l', 'o'] let namesSet = new Set(['a', 'b']); Array.from(namesSet); // ['a', 'b']
Array.from 还能够接受第二个参数,做用相似于数组的 map 方法,用来对每一个元素进行处理,将处理后的值放入返回的数组。
let arrayLike = { '0': 1, '1': 2, '2': 3, length: 3 }; Array.from(arrayLike, x => x * x); // [ 1, 4, 9 ]
Array.of 方法用于将一组值,转换为数组。这个方法的主要目的,是弥补数组构造函数 Array()的不足。由于参数个数的不一样,会致使 Array()的行为有差别。
Array.of() // [] Array.of(undefined) // [undefined] Array.of(1) // [1] Array.of(1, 2) // [1, 2]
参数:
这三个参数都应该是数值,若是不是,会自动转为数值。
[1, 2, 3, 4, 5].copyWithin(0, 3)
数组实例的 find 方法,用于找出第一个符合条件的数组成员,若是没有符合条件的成员,则返回 undefined。
findIndex 方法返回第一个符合条件的数组成员的位置,若是全部成员都不符合条件,则返回-1。
[1, 4, -5, 10].find(n => n < 0); // -5 [1, 4, -5, 10].findIndex(n => n < 0); // 2
两个方法均可以接受第二个参数,用来绑定回调函数的 this 对象。
function f(v){ return v > this.age; } let person = {name: 'John', age: 20}; [10, 12, 26, 15].find(f, person); // 26
这两个方法均可以发现 NaN,弥补了数组的 indexOf 方法的不足。
fill 方法使用给定值,填充一个数组。fill 方法能够接受第二个和第三个参数,用于指定填充的起始位置和结束位置。若是填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象,改变数组中的一项,则全部项都改变。
let arr = Array.of(1, 2, 3).fill({ num: 20 }); console.log(arr); // [ { num: 20 }, { num: 20 }, { num: 20 } ] arr[0].num = 10; console.log(arr); // [ { num: 10 }, { num: 10 }, { num: 10 } ]
for (let index of ['a', 'b'].keys()) { console.log(index); } // 0 // 1 for (let elem of ['a', 'b'].values()) { console.log(elem); } // 'a' // 'b' for (let [index, elem] of ['a', 'b'].entries()) { console.log(index, elem); } // 0 "a" // 1 "b"
includes 方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的 includes 方法相似。该方法的第二个参数表示搜索的起始位置,第二参数是负数,取它的倒数,第二参数大于数组长度,取 0。
[1, 2, 3].includes(3, -1); // true
flat()默认只会“拉平”一层,若是想要“拉平”多层的嵌套数组,能够将 flat()方法的参数写成一个整数,表示想要拉平的层数,默认为 1。
flat()的参数为 2,表示要“拉平”两层的嵌套数组。若是无论有多少层嵌套,都要转成一维数组,能够用 Infinity 关键字做为参数。
[1, [2, [3]]].flat(Infinity); // [1, 2, 3]
flatMap()先遍历数组,再“拉平”一层,也只能拉平一层。参数鱼 map()方法相似。
ß
[2, 3, 4].flatMap(x => [x, x * 2]); // [2, 4, 3, 6, 4, 8] // 至关于 [2, 3, 4].map(x => [x, x * 2]).flat(); // [2, 4, 3, 6, 4, 8]
const a = 1; const b = 2; const c = {a, b}; // 等同于 const c = {a: a, b: b}; const o = { method() { return "Hello!"; } }; // 等同于 const o = { method: function() { return "Hello!"; } }; function f(x, y) { return {x, y}; } // 等同于 function f(x, y) { return {x: x, y: y}; }
对象扩展符相似数组扩展符,主要用于解构赋值。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x // 1 y // 2 z // { a: 3, b: 4 } let ab = { ...a, ...b }; // 等同于 let ab = Object.assign({}, a, b);
Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.is('str', 'str'); // true Object.is({}, {}); // false
不一样之处只有两个:一是+0不等于-0,二是NaN等于自身。
+0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
Object.assign方法用于对象的合并,将源对象(source)的全部可枚举属性,复制到目标对象(target)。
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。若是目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
因为undefined和null没法转成对象,因此若是它们做为首参数,就会报错。
const target = { a: 1, b: 1 }; const source1 = { b: 2, c: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
常见用途:
本文参考《ECMAScript 6 入门》,了解更多请点击跳转点击跳转。