什么是ECMA6?ES6常用语法
ecma6
1. 什么是ecma6
ecmascript 6.0(简称 es6)是 javascript 语言的下一代标准,在2015年6月正式发布。它的目标,是使得 javascript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
2. ecmascript 和 javascript 的关系
ecmascript是javascript的规格,javascript是ecmascript的一种实现。日常场合,这两个词是可以互换的。
另外,actionscript也是 ecmascript的实现
1996年11月,javascript 的创造者 网景公司,将 javascript 提交给国际标准化组织ecma,希望这种语言能够成为国际标准。次年,ecma 发布262号标准文件(ecma-262)的第一版,规定了脚本语言的标准,并将这种语言称为 ecmascript,这个版本就是1.0版。
该标准从一开始就是针对 javascript 语言制定的,但是之所以不叫 javascript,有两个原因。一是商标,java 是 sun 公司的商标,根据授权协议,只有网景公司可以合法地使用 javascript 这个名字,且 javascript 本身也已经被网景公司注册为商标。二是想体现这门语言的制定者是 ecma,不是网景,这样有利于保证这门语言的开放性和中立性。
3. ecma2015
es6 的第一个版本,在2015年6月发布,正式名称是《ecmascript 2015标准》(简称 es2015)。
2016年6月,小幅修订的《ecmascript 2016标准》(简称 es2016)发布,这个版本可以看作是 es6.1 版。
2017年6月会发布 es2017 标准。
因此,es6 既是一个历史名词,也是一个泛指,含义是5.1版以后的 javascript 的下一代标准,涵盖了es2015、es2016、es2017等等,而es2015 则是正式名称,特指该年发布的正式版本的语言标准。
4. es6的兼容性问题
目前各大浏览器对es6的支持度已经越来越高了,超过90%的 es6 语法特性都实现了。
nodejs 微信开发 完全支持es6的语法
5. babel 转码器
该转码器可以吧es6语法转码为es5语法,意味着,可以借助于babel转码器提高es6的兼容性。
es6常用语法
1. 变量扩展
1.1. let 关键字
用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效
{ let a = 10; var b = 1; } a // referenceerror: a is not defined. b // 1
for循环的计数器,就很合适使用let命令
for (let i = 0; i < 10; i++) {} console.log(i); //referenceerror: i is not defined
不存在变量提升
// var 的情况 console.log(foo); // 输出undefined var foo = 2; // let 的情况 console.log(bar); // 报错referenceerror let bar = 2;
不允许重复声明
let a = 100; let a = 200; //报错 let b = 100; b = 200; //正确
1.2 const 关键字
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const pi = 3.1415; pi // 3.1415 pi = 3; //报错
同,let一样,也不存在变量提升
1.3 顶层对象和全局变量
es6中将顶层对象(浏览器中是window对象,node中是global对象)与全局变量不再挂钩。
但是,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。
2. 对象的简化写法
2.1 属性的简化写法
var foo = 'name1'; var bar = 'name2'; var obj = {foo,bar}; //等同于 var obj = {foo:foo, bar:bar}
2.2 方法的简化写法
{ fn:function(){ } } //可简化为 { fn(){ } }
3. 变量解构赋值
es6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(destructuring)。
3.1 数组的解构赋值
以前,变量赋值,只能这么写
var a = 1; var b = 2; var c = 3;
现在,也可以这么写
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。
let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3 let [ , , third] = ["foo", "bar", "baz"]; third // "baz" let [x, , y] = [1, 2, 3]; x // 1 y // 3 let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] let [x, y, ...z] = ['a']; x // "a" y // undefined z // []
如果解构不成功,变量的值就等于undefined
如果等号左边的形式是数组,而右边不是数组,将会报错
解构变量可以有默认值
let [foo = true] = []; foo // true let [x, y = 'b'] = ['a']; // x='a', y='b' let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
3.2 对象的解构
解构不仅可以用于数组,还可以用于对象
let { foo, bar } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
如果变量名与属性名不一致,必须写成下面这样。
var { foo: baz } = { foo: ‘aaa’, bar: ‘bbb’ };
baz // “aaa”
let obj = { first: 'hello', last: 'world' }; let { first: f, last: l } = obj; f // 'hello' l // 'world'
这实际上说明,对象的解构赋值是下面形式的简写
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
和数组一样,解构也可以用于嵌套解构的对象
let obj = { p: [ 'hello', { y: 'world' } ] }; let { p: [x, { y }] } = obj; x // "hello" y // "world"
注意,这时p是模式,不是变量,因此不会被赋值。
对象的解构也可以指定默认值
var {x, y = 5} = {x: 1}; x // 1 y // 5 var { message: msg = 'something went wrong' } = {}; msg // "something went wrong"
3.3 特殊对象的解构
javascript中一切皆对象,向字符串、布尔值、数值都可以被解构,而且字符串可以当做字符组成的数组
const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o" let {length : len} = 'hello'; //因为字符串具有属性length len // 5 let {tostring: s} = 123; s === number.prototype.tostring // true
3.4 实际用途
交换变量的值
let x = 1; let y = 2; [x, y] = [y, x];
提取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]
函数参数的默认值
jquery.ajax = function (url, { async = true, beforesend = function () {}, cache = true, complete = function () {}, crossdomain = false, global = true, // ... more config }) { // ... do stuff };
输入模块的指定方法
const { sourcemapconsumer, sourcenode } = require("source-map");
4. 扩展运算符(…)
扩展运算符(…)可以将某些数据结构转为数组。
可以将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括es6新增的数据结构set和map)。
// arguments对象 function foo() { var args = [...arguments]; } // nodelist对象 [...document.queryselectorall('p')]
5. for…of 循环
es6 借鉴 c++、java、c# 和 python 语言,引入了for…of循环,作为遍历所有数据结构的统一的方法。
for…of循环可以使用的范围包括数组、set 和 map 结构、某些类似数组的对象(比如arguments对象、dom nodelist 对象)以及字符串
const arr = ['red', 'green', 'blue']; for(let v of arr) { console.log(v); // red green blue }
6. class关键字
6.1 定义类
function point(x, y) { this.x = x; this.y = y; } point.prototype.tostring = function () { return '(' + this.x + ', ' + this.y + ')'; }; var p = new point(1, 2);
js传统方法中定义对象是通过构造函数,这种写法跟传统的面向对象语言(比如c++和java)差异很大,很容易让新学习这门语言的程序员感到困惑。
es6提供了更接近传统语言的写法,引入了class(类)这个概念。
class bar { dostuff() { console.log('stuff'); } } var b = new bar(); b.dostuff() // "stuff"
如果你了解过java或者php或者c++或者.net 你将会对这种写法非常熟悉
6.2 constructor 构造方法
class bar{ constructro() { } }
在类被实例化的时候, 构造函数会被自动调用
6.3 extends 继承
class之间可以通过extends关键字实现继承,这比es5的通过修改原型链实现继承,要清晰和方便很多。
class colorpoint extends point {}
上面代码定义了一个colorpoint类,该类通过extends关键字,继承了point类的所有属性和方法。
6.4 super
class colorpoint extends point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } tostring() { return this.color + ' ' + super.tostring(); // 调用父类的tostring() } }
上面代码中,constructor方法和tostring方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
7. 箭头函数
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 = (num1, num2) => { let sum = num1 + num2; return sum; }
箭头函数的一个用处是简化回调函数
// 正常函数写法
var result = values.sort(function (a, b) {
return a - b;
});
// 箭头函数写法 var result = values.sort((a, b) => a - b);
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
function foo() { settimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // id: 42
上面代码中,settimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到100毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。
箭头函数 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
箭头函数 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数代替。
8. es6函数参数问题
8.1 函数默认值
es6 允许为函数的参数设置默认值,即直接写在参数定义的后面。不必那es5那样,采用些特殊手段
//es6写法 function log(x, y = 'world') { console.log(x, y); } //es5写法 function log(x, y) { if (y === undefined) { y = 'world'; } console.log(x,y); }
8.2 rest参数
es6 引入 rest 参数(形式为“…变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10
9. 模板字符串
传统的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)是增强版的字符串,用反引号 ` 标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
模板字符串中嵌入变量,需要将变量名写在${}之中。
大括号内部可以放入任意的javascript表达式,可以进行运算,以及引用对象属性。
`user ${user.name} is not authorized to do ${action}.` `${x} + ${y} = ${x + y}` `foo ${fn()} bar`
如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
10. symbol
es6引入了一种新的原始数据类型symbol,表示独一无二的值。它是javascript语言的第七种数据类型,前六种是:undefined、null、布尔值(boolean)、字符串(string)、数值(number)、对象(object)
symbol值通过symbol函数生成。
symbol函数可以接受一个字符串作为参数,表示对symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
let s = symbol(); typeof s // "symbol" var s1 = symbol('foo'); var s2 = symbol('bar'); s1 // symbol(foo) s2 // symbol(bar) s1.tostring() // "symbol(foo)" s2.tostring() // "symbol(bar)"
注意,symbol函数前不能使用new命令,否则会报错。这是因为生成的symbol是一个原始类型的值,不是对象。
任意两个symbol类型的数据都不想等
// 没有参数的情况 var s1 = symbol(); var s2 = symbol(); s1 === s2 // false // 有参数的情况 var s1 = symbol('foo'); var s2 = symbol('foo'); s1 === s2 // false
symbol值不能与其他类型的值进行运算,会报错。
由于每一个symbol值都是不相等的,这意味着symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
var mysymbol = symbol();
// 第一种写法 var a = {}; a[mysymbol] = 'hello!'; // 第二种写法 var a = { }; // 第三种写法 var a = {}; object.defineproperty(a, mysymbol, { value: 'hello!' }); // 以上写法都得到同样结果 a[mysymbol] // "hello!"
注意,symbol值作为对象属性名时,不能用点运算符。
11. set 数据结构
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 函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。
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
12. map 数据结构
es6提供了map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,object结构提供了“字符串—值”的对应,map结构提供了“值—值”的对应,是一种更完善的hash结构实现。如果你需要“键值对”的数据结构,map比object更合适。
var m = new map(); var o = {p: 'hello world'}; m.set(o, 'content') m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false
作为构造函数,map也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
var map = new map([ ['name', '张三'], ['title', 'author'] ]); map.size // 2 map.has('name') // true map.get('name') // "张三" map.has('title') // true map.get('title') // "author"
以上介绍的是es6中最为常用的也较为容易掌握一些新语法。 查看更为详细的es6语法,请访问阮一峰老师 ecmascript6入门
(完)