ES6 入门
本文参考阮一峰老师的《es6 入门》
前言
如今,距离 ES6 的正式发布时间已经过去了 3 年,随着时间的推移,各大浏览器对 ES6 的支持度已经越来越高了,超过 90%的 ES6 语法特性都实现。掌握 ES6 已经是一名前端工程师必备的能力。
ES6 let 和 const 命令
let 命令
基本用法
ES6 新增了let
命令,用来声明变量,但是let
所声明的变量只能在自己的代码块内有效,也就是说,原本没有块级作用域的javascript
现在也有了块级作用域。
{ let a = 1; }console.log(a); //a is not defined复制代码
正因为产生块级作用域这个特点,let
十分适用于 for 循环
var a = [];for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); };}a[6](); // 6复制代码
上面代码中,变量i
是 let
声明的,当前的 i
只在本轮循环有效,所以每一次循环的 i
其实都是一个新的变量,所以最后输出的是 6。
如果使用var
命令,最后输出的是 10
var a = [];for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); };}a[6](); // 10复制代码
不存在变量提升
let
不像 var
那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。
console.log(a); // 输出 undefinedconsole.log(b); // 报错 ReferenceErrorvar a = 2;let b = 2;复制代码
上面代码中,变量a
用 var
命令声明,会发生变量提升,即脚本开始运行时,变量 a
已经存在了,但是没有值,所以会输出undefined
。变量 b
用let
命令声明,不会发生变量提升。这表示在声明它之前,变量b
是不存在的,这时如果用到它,就会抛出一个错误。
暂时性死区
只要块级作用域内存在 let
命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
ES6 明确规定,如果区块中存在 let
和 const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用 let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)
。
if (true) {// TDZ 开始tmp = 'abc'; // ReferenceErrorconsole.log(tmp); // ReferenceErrorlet tmp; // TDZ 结束console.log(tmp); // undefinedtmp = 123;console.log(tmp); // 123}复制代码
上面代码中,在 let
命令声明变量 tmp
之前,都属于变量 tmp
的“死区”。
ES6 规定暂时性死区和 let、const
语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量。
// 报错function () { let a = 10; var a = 1;}// 报错function () { let a = 10; let a = 1;}复制代码
因此,不能在函数内部重新声明参数。
do 表达式
本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。
{ let t = f(); t = t * t + 1;}复制代码
上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到 t 的值,因为块级作用域不返回值,除非t
是全局变量。
现在有一个提案,使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上do
,使它变为do
表达式。
let x = do { let t = f(); t * t + 1;};复制代码
上面代码中,变量x
会得到整个块级作用域的返回值。
const 命令
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;PI // 3.1415PI = 3;// TypeError: Assignment to constant variable.复制代码
上面代码表明改变常量的值会报错。
const
命令大致特点和let
相似,不再一一列举
ES6 数组的扩展
Array.from()
这个方法是 Array 构造器的静态方法
作用:将把类数组对象转成真正的数组。
格式:
Array.from(类数组对象)Array.from(类数组对象,function(item,index){ return;//})复制代码
代码如下
var lis = document.getElementsByTagName("li")var rs = Array.from(lis)console.log(Array.isArry(rs))//true复制代码
Array.of()
作用:将一组值转换为数组。与 Array.from 功能相似,理解用来创建数组。 主要目的是弥补构造器 Array()的不足
var arr1 = new Array.of(3) //[3]var arr2 = new Array.of("3")//['3']复制代码
find 和 findIndex
find:用于找出第一个符合条件的数组元素。找不到则是 undefined
。注意,它是不会返回多 个,只找一个,找到了就返回。如果你要把所有的满足条件的数组元素素都找出来,你应该用filter()
findIndex
:返回第一个符合条件的数组元素的索引。找不到则是-1;
格式:
arr.find(function(item,index){ return 条件;//})复制代码
var arr = [ {name:"zs1",score:90} {name:"zs2",score:90} {name:"zs3",score:90}]var rs = Array.find(item =>item.name=="zs1")// {name:"zs1",score:90}复制代码
includes
作用:判断元素是否在数组中存在。返回值是 true|false
代码如下:
var myarr = [1,2,3]myarr.includes(1)//truemyarr.includes(0)//flase复制代码
fill
作用:给数组填充指定值。fill
方法用于空数组的初始化非常方便。已有数据会被覆盖。 fill
方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
格式:
arr.fill(值)arr.fill(值,起点,终点) 包括起点,不包括终点复制代码
var arr = new Array(5)arr.fill("*") //["*","*","*","*","*"]复制代码
数组的扩展运算符
功能:把数据结构转成数组。
代码如下:
var arr1 = [1,2,3]var arr2 = [...arr1] //[1,2,3]复制代码
函数的扩展
函数参数的默认值
基本用法
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') { console.log(x, y);}log('Hello') // Hello Worldlog('Hello', 'China') // Hello Chinalog('Hello', '') // Hello复制代码
箭头函数
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;};复制代码
箭头函数使得表达更加简洁。如果不用箭头函数,可能就要占用多行,而且还不如现在这样写醒目。
箭头函数的一个用处是简化回调函数。
// 正常函数写法[1,2,3].map(function (x) { return x * x;});// 箭头函数写法[1,2,3].map(x => x * x);复制代码
注意
(1)函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
(3)不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 Rest 参数代替。
(4)不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
ES6 对象的扩展
属性的简洁表示法
ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
var foo = 'bar';var baz = {foo};baz // {foo: "bar"}// 等同于var baz = {foo: foo};复制代码
上面代码表明,ES6允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值。下面是另一个例子。
function f(x, y) { return {x, y};}// 等同于function f(x, y) { return {x: x, y: y};}f(1, 2) // Object {x: 1, y: 2}复制代码
除了属性简写,方法也可以简写。
var o = { method() { return "Hello!"; }};// 等同于var o = { method: function() { return "Hello!"; }};复制代码
()
比较两个值是否相等
Object.is(+0, -0) // falseObject.is(NaN, NaN) // true复制代码
Object.assign()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象
注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
var str = { a: 1, b: 1 };var str1 = { b: 2, c: 2 };var str2 = { c: 3 };Object.assign(str, str1, str2);str // {a:1, b:2, c:3}复制代码
var v1 = 'abc';var v2 = true;var v3 = 10;var obj = Object.assign({}, v1, v2, v3);console.log(obj); // { "0": "a", "1": "b", "2": "c" }复制代码
上面代码中,v1、v2、v3分别是字符串、布尔值和数值,结果只有字符串合入目标对象(以字符数组的形式),数值和布尔值都会被忽略。这是因为只有字符串的包装对象,会产生可枚举属性。
Object.getOwnProertyDescriptor();
获取一个对象中某个属性的详细信息。
var a = 1 console.log(object.getOwnProertyDescriptor(window,"a")复制代码
getOwnPropertyNames()
获取自的属性,以数组的格式返回的。
var obj = { name:xiaoli age:22}console.log(object.getOwnPropertyNames(obj))//["name","age"]复制代码
Object.keys()
var obj = { name:xiaoli age:22}console.log(object.keys(obj))//["name","age"]复制代码
使用Object.getOwnPropertyNames()和Object.keys()都可以得到一个对象的属性名,属性名是放在一个数组中的。
对于对象的遍历目前有三种方式:
for in
Object.keys() Object.getOwnPropertyNames()for in : 会输出自身以及原型链上可枚举的属性。
Object.keys() : 用来获取对象自身可枚举的属性键 Object.getOwnPropertyNames() : 用来获取对象自身的全部属性名__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
(1)__proto__属性
隐式原型属性,用来设计对象的隐式原型
(2)Object.setPrototypeOf()
Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象。它是ES6正式推荐的设置原型对象的方法。
// 格式Object.setPrototypeOf(object, prototype)// 用法var o = Object.setPrototypeOf({}, null);复制代码
let proto = {};let obj = { x: 10 };Object.setPrototypeOf(obj, proto);proto.y = 20;proto.z = 40;obj.x // 10obj.y // 20obj.z // 40复制代码
(3)Object.getPrototypeOf()
该方法与setPrototypeOf方法配套,用于读取一个对象的prototype对象。
Object.getPrototypeOf(obj);复制代码
ES6 变量的解构赋值
数组的解构赋值
基本用法
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
以前,为变量赋值,只能直接指定值。
var a = 1;var b = 2;var c = 3;复制代码
ES6 允许写成下面这样。
var [a, b, c] = [1, 2, 3];复制代码
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
默认值
解构赋值允许指定默认值。
var [f = true] = [];f // true[x, y = 'b'] = ['a']; // x='a', y='b'[x, y = 'b'] = ['a', undefined]; // x='a', y='b'复制代码
注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined
,默认值是不会生效的。
对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
var { f, b } = { f: "aaa", b: "bbb" };f // "aaa"b // "bbb"复制代码
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
var { b, f } = { f: "aaa", b: "bbb" };f // "aaa"b // "bbb"var { baz } = { f: "aaa", b: "bbb" };baz // undefined复制代码
字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';a // "h"b // "e"c // "l"d // "l"e // "o"复制代码
类似数组的对象都有一个length
属性,因此还可以对这个属性解构赋值。
let {length : len} = 'hello';len // 5复制代码
函数参数的解构赋值
函数的参数也可以使用解构赋值。
function add([x, y]){ return x + y;}add([1, 2]); // 3复制代码
上面代码中,函数add
的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x
和y
。对于函数内部的代码来说,它们能感受到的参数就是x
和y
。
ES6 字符串的扩展
repeat()
repeat
方法返回一个新字符串,表示将原字符串重复n
次。
'x'.repeat(3) // "xxx"'hello'.repeat(2) // "hellohello"'a'.repeat(0) // ""复制代码
参数如果是小数,会被取整。
'a'.repeat(2.9) // "aa"复制代码
如果repeat
的参数是负数或者Infinity
,会报错。
'na'.repeat(Infinity)// RangeError'na'.repeat(-1)// RangeError复制代码
trim()
除去字符串空格的。
trim 左右空格都是去掉
trimLeft 左空格去掉
trimRight 右空格去掉
var str = " ab ab abc "str.trim = "ab ab abc"str.trimLeft = "ab ab abc "str.trimRight = " ab ab abc"复制代码
includes
var str = "abc"str.includes("a")//truestr.includes("e")//flase复制代码
startsWith
var str = "abc def"str.startsWith("abc")//truestr.startsWith("abcd")//false复制代码
padStart
var str = "abc def"str.padStart(15,"*")//"********abc def"复制代码
padEnd
var s = 4s.toFixed(2) "4.00"s.toFixed(2).padStart"04.00" 复制代码
模板字符串
传统的JavaScript
语言,输出模板通常是这样写的。
$('#result').append( 'There are ' + basket.count + ' ' + 'items in your basket, ' + '' + basket.onSale + ' are on sale!');复制代码
上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。
$('#result').append(` There are ${basket.count} items in your basket, ${basket.onSale} are on sale!`);复制代码
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
模板字符串中嵌入变量,需要将变量名写在${}之中。
function authorize(user, action) { if (!user.hasPrivilege(action)) { throw new Error( // 传统写法为 // 'User ' // + user.name // + ' is not authorized to do ' // + action // + '.' `User ${user.name} is not authorized to do ${action}.`); }}复制代码
ES6 数值的扩展
Number.parseInt(), Number.parseFloat()
ES6 将全局方法parseInt()
和parseFloat()
,移植到Number
对象上面,行为完全保持不变。
// ES5的写法parseInt('12.34') // 12parseFloat('123.45#') // 123.45// ES6的写法Number.parseInt('12.34') // 12Number.parseFloat('123.45#') // 123.45复制代码
这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
Number.isInteger()
Number.isInteger()用来判断一个值是否为整数。需要注意的是,在JavaScript
内部,整数和浮点数是同样的储存方法,所以 3 和 3.0 被视为同一个值。
Number.isInteger(25) // trueNumber.isInteger(25.0) // trueNumber.isInteger(25.1) // falseNumber.isInteger("15") // falseNumber.isInteger(true) // false复制代码
Number.EPSILON
ES6 在Number
对象上面,新增一个极小的常量Number.EPSILON
。
Number.EPSILON// 2.220446049250313e-16Number.EPSILON.toFixed(20)// '0.00000000000000022204'复制代码
引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。
Math 对象的扩展
ES6 在Math
对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。
Math.trunc()
Math.trunc
方法用于去除一个数的小数部分,返回整数部分。
Math.trunc(4.1) // 4Math.trunc(4.9) // 4Math.trunc(-4.1) // -4Math.trunc(-4.9) // -4Math.trunc(-0.1234) // -0复制代码
Math.cbrt()
Math.cbrt
方法用于计算一个数的立方根。
Math.cbrt(-1) // -1Math.cbrt(0) // 0Math.cbrt(1) // 1Math.cbrt(2) // 1.2599210498948734复制代码
Math.clz32()
JavaScript 的整数使用 32 位二进制形式表示,Math.clz32
方法返回一个数的 32 位无符号整数形式有多少个前导 0。
Math.clz32(0) // 32Math.clz32(1) // 31Math.clz32(1000) // 22Math.clz32(0b01000000000000000000000000000000) // 1Math.clz32(0b00100000000000000000000000000000) // 2复制代码
Math.imul()
Math.imul
方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
Math.imul(2, 4) // 8Math.imul(-1, 8) // -8Math.imul(-2, -2) // 4复制代码
Math.fround()
Math.fround
方法返回一个数的单精度浮点数形式。
Math.fround(1.337) // 1.3370000123977661复制代码
对于整数来说,Math.fround 方法返回结果不会有任何不同,区别主要是那些无法用 64 个二进制位精确表示的小数。这时,Math.fround 方法会返回最接近这个小数的单精度浮点数。
Math.hypot()
Math.hypot
方法返回所有参数的平方和的平方根。
Math.hypot(3, 4); // 5Math.hypot(3, 4, 5); // 7.0710678118654755Math.hypot(); // 0Math.hypot(NaN); // NaNMath.hypot(3, 4, 'foo'); // NaNMath.hypot(3, 4, '5'); // 7.0710678118654755Math.hypot(-3); // 3复制代码
上面代码中,3 的平方加上 4 的平方,等于 5 的平方。
如果参数不是数值,Math.hypot 方法会将其转为数值。只要有一个参数无法转为数值,就会返回 NaN。
ES6 set和map数据结构
Set
基本用法 ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构。
var s = new Set();[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x));for (let i of s) { console.log(i);}// 2 3 5 4复制代码
上面代码通过add方法向Set结构加入成员,结果表明Set结构不会添加重复的值。
Set
函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。
// 例一var set = new Set([1, 2, 3, 4, 4]);[...set]// [1, 2, 3, 4]// 例二var items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);items.size // 5// 例三function divs () { return [...document.querySelectorAll('div')];}var set = new Set(divs());set.size // 56// 类似于divs().forEach(div => set.add(div));set.size // 56复制代码
上面代码中,例一和例二都是Set函数接受数组作为参数,例三是接受类似数组的对象作为参数。
// 去除数组的重复成员[...new Set(array)]复制代码
class
从形式上,向主流的面向对象的语言靠拢。
我们之前写对象方式可以用 Class 用进行优化
前面我们都是创建构造器,然后去new构造器,构造器就相当于一个类,在ES6中,就可以使用class来创建对象了。
1,class创建对象 格式:
class 类名{ constructor(参数){ this.属性 = 参数; } method(){ }}复制代码
注意:
1.class 是关键字,后面紧跟类名,类名首字母大写,采取的是大驼峰命名法则。类名之后是
2.在{}中,不能直接写语句,只能写方法,方法不需要使用关键字
3.方法和方法之间没有逗号。不是键值对
class NBAPlayer{ constructor(name,age,height){ this.name = name; this.age = age; this.height = height; } say(){ console.log("`我是${this.name},我今年${this.age}岁,身高${this.height}`") } }var p1 = new NBAPlayer("库里","25","190")复制代码
使用extends实现继承
格式:
class 子类 extend 父类 { constructor (参数){ super(参数) this.属性=值 }}复制代码
注意:
使用 extends 关键字来实现继承,在子类中的构造器 constructor 中,必须要显式调用父类的 super 方法,如果不调用,则 this 不可用
class NBAPlayer{ constructor(name,age,height){ this.name = name; this.age = age; this.height = height; } say(){ console.log("`我是${this.name},我今年${this.age}岁,身高${this.height}`") } }class MVP extends NBAPlayer{ constructor(name,age,height,year){ super(name,age,height) this.year = year; } showMvp(){ console.log("`我是${this.name},是${this.year}年的MVP`") } }var p1 = new MVP("库里","25","190",2016)复制代码
类的静态方法 static
直接通过类名来访问的方法就是静态方法。如:Math.abs();这里的 abs()是静态方法。 Array.isArray();isArray()是静态方法. 在方法名前加 static 就可以了
后记
花一天的时间总结了阮一峰老师的《es6 入门》,其中还是有很多不懂的地方,还要继续学习。