本篇内容所有摘自阮一峰的:ECMAScript 6 入门javascript
阮一峰的这本书,我我的以为写得挺好的,无论是描述方面,仍是例子,都讲得挺通俗易懂,每一个新特性基本都还会跟 ES5 旧标准作比较,说明为何会有这个新特性,这更于理解。html
因此,后续不会再写个关于 ES6 系列的文章了,就在这篇里大概的列举一下,大致清楚都有哪些新特性就行了,之后须要用时再去翻一翻阮一峰的书。前端
ES6 新标准规范相比于 ES5 旧标准规范中,无非就三个方面的改动:新增、更新、废弃。java
因为更新和废弃须要考虑到兼容性问题,因此这两方面的内容应该并很少,那么大部分基本都是新增的特性。对于新增的特性来讲,大致上也还能够再分两类:彻底新增的特性和基于旧标准扩展的特性。程序员
下面就大概来过一下,ES6 中新增的特性。es6
这是新增的一种原始数据类型,ES5 中原始类型有 5 种,在 ES6 中新引入了一种后,如今就是有 6 种原始数据类型了:Number、String、Boolean、null、undefined、Symbol编程
这个单词中文直译是:符号、标志等,但好像并无在书中有这种叫法,书中都是直接使用 Symbol 来描述,可能中文翻译不可以很好的表示出这种原始数据类型的含义吧。数组
之因此新增了这种原始数据类型,是为了解决:浏览器
ES5 的对象属性名都是字符串,这容易形成属性名的冲突。好比,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。若是有一种机制,保证每一个属性的名字都是独一无二的就行了,这样就从根本上防止属性名的冲突。这就是 ES6 引入
Symbol
的缘由。数据结构
var s1 = Symbol('s1'); //参数只是用来描述当前 s1 变量,跟其余 Symbol 数据类型的变量相区分,在调用 toString 或 typeOf 时会输出当前数据类型跟描述信息 //第一种 var o = {}; o[s1] = "dasu"; //第二种 var o = { [s1]: "dasu" }; //第三种 var o = {} Object.defineProperty(o, s1, {value: "dasu"});
给一个对象定义一个属性名用 Symbol 数据类型来表述的方法有上述三种,若是使用 o.s1 = "dasu"
这种方式,是给 o 对象定义了一个属性名为 s1 且数据类型为字符串的属性,字符串就存在相等与否的场景。而使用上述三种方式,是给对象定义了一个属性名为 s1 但数据类型为 Symbol 的属性,若是后期又定义了另外一个属性名为 s1 的 Symbol 原始值的属性,并不会覆盖以前定义的属性。
其余内容,包括关于 Symbol 属性的遍历、Symbol 自带的方法、Symbol 应用场景等见:Symbol
ES5 中变量的做用域只分全局做用域和函数内做用域,且全局变量本质上是全局对象的属性,书中是这么评价的:
顶层对象的属性与全局变量挂钩,被认为是 JavaScript 语言最大的设计败笔之一。这样的设计带来了几个很大的问题,首先是无法在编译时就报出变量未声明的错误,只有运行时才能知道(由于全局变量多是顶层对象的属性创造的,而属性的创造是动态的);其次,程序员很容易不知不觉地就建立了全局变量(好比打字出错);最后,顶层对象的属性是处处能够读写的,这很是不利于模块化编程。另外一方面,
window
对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合适的。
为了解决这些问题,ES6 中新增了块级做用域的变量,经过关键字:let 和 const 声明的变量,做用域是块级做用域,同时,这些变量不属于顶层对象的属性。
const 定义的变量是常量,除此外,这个变量跟用 let 定义的变量没有其余方面的区别了。而经过 let 和 const 定义的变量行为、做用域相似于 Java 语言中定义的变量行为。
换句话说,let 和 const 定义的变量已经不包含 var 定义的变量的各类特性行为了,好比:没有变量的声明提早特性、存在暂时性死区(在定义以前不能使用,只能在定义变量位置以后使用)、不容许重复声明、做用域只有块级做用域等。
更多的参考:let 和 const 命令
ES5 中并无模块机制,常见的方式是:前端里经过 <script> 加载各类不一样的 js 文件代码,在 js 文件代码内部中提供一些全局变量或全局函数供其余 <script> 使用。
说白了,就是不一样 js 文件经过全局对象做为通讯的桥梁来相互访问。而为了解决不一样 js 文件共享全局对象形成的变量冲突问题,一般做为模块的 js 里的代码都会放在一个当即执行的函数内。
而 ES6 中,引入了模块的机制。
当在 HTML 文档中,经过指定 <script> 标签的 type 属性为 module 时,如:
<script type="module" src="./foo.js"></script>
浏览器会按照模块的处理方式来加载这份 js 文件,与模块脚本的处理方式与正常 js 脚本文件处理方式最不一样的地方在于,模块内的代码都是在模块做用域中执行,也就是在模块 js 文件中的全局变量这些并不会被添加到全局对象的属性中,其余 js 文件没法访问。
那么,其余 js 文件如何使用这份模块 js 文件呢?
有三点要求:
import 命令和 export 命令是模块机制的关键,但只两个命令只能在 type=module 的 js 文件内生效,若是某个 js 文件声明了 type=text/javascript,而后代码里又使用到 import 或 export,那么运行期间会报错。
//module.js //... //以上省略模块内部功能代码 //通常对外的接口可放在文件底部,方便尽收眼底 //第一种: export var name = "dasu"; export function getName() {return "dasuAndroidTv"}; //第二种: var name = "dasu" var getName = function() {return "dasuAndroidTv"}; export {name, getName}
//第一种:from后跟js的相对路径或绝对路径 import {name, getName} from 'module.js' //第二种: import * as Module from 'module.js' //使用 Module.name; Module.getName(); //...
最后,记住,模块脚本文件中,自动以严格模式运行,限制也不少,更多用法、细节说明参考:
ES6 容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
之前,为变量赋值,只能直接指定值。
var a = 1; var b = 2; var c = 3;
ES6 容许写成下面这样。
var [a, b, c] = [1, 2, 3];
上面代码表示,能够从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
这种特性能够带来不少便捷:
// 返回一个数组 function example() { return [1, 2, 3]; } let [a, b, c] = example(); // 返回一个对象 function example() { return { foo: 1, bar: 2 }; } let { foo, bar } = example();
//解析服务端返回的数据对象,提取其中的各字段值 var o = { code: 1, msg: "success", content: {...} }; var {code, msg, content} = o;
解构时,还能够设置默认值,更多用法,参考:变量的解构赋值
ES6 中对字符串的处理扩展了不少新特性,让字符串的处理更增强大,下面看一个很强大的特性:
传统的 JavaScript 语言,输出模板一般是这样写的(下面使用了 jQuery 的方法)。
$('#result').append( 'There are <b>' + basket.count + '</b> ' + 'items in your basket, ' + '<em>' + basket.onSale + '</em> are on sale!' );
上面这种写法至关繁琐不方便,ES6 引入了模板字符串解决这个问题。
$('#result').append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `);
模板字符串(template string)是加强版的字符串,用反引号(`)标识。它能够看成普通字符串使用,也能够用来定义多行字符串,或者在字符串中嵌入变量。
更多字符串扩展的特性介绍参见:字符串的扩展
ES6 中,对于函数的处理也增长了不少新特性,让函数变得更强大。
ES6 以前,不能直接为函数的参数指定默认值,只能采用变通的方法。
function log(x, y) { x = x || 'dasu'; y = y || 'Android'; console.log(x, y); }
ES6 容许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x="dasu", y="Android") { console.log(x, y); }
固然,参数默认值还有更多细节,好比默认参数的做用域是单独的、有默认参数值的函数 length 属性含义会变化等等。
ES6 引入 rest 参数(形式为...变量名
),用于获取函数的多余参数,这样就不须要使用arguments
对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10
ES6 容许使用“箭头”(=>
)定义函数。
var f = v => v; // 等同于 var f = function (v) { return v; };
箭头前面是参数,后面的函数体,若是超过 1 个参数,那么用圆括号将多个参数圈起,若是没有参数,就用一个空圆括号 () 表示,若是函数体超过 1 行语句,那么用 {} 大括号包住。
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);
箭头函数内部,还能够再使用箭头函数。下面是一个 ES5 语法的多重嵌套函数。
function insert(value) { return {into: function (array) { return {after: function (afterValue) { array.splice(array.indexOf(afterValue) + 1, 0, value); return array; }}; }}; } insert(2).into([1, 3]).after(1); //[1, 2, 3]
上面这个函数,可使用箭头函数改写。
let insert = (value) => ({into: (array) => ({after: (afterValue) => { array.splice(array.indexOf(afterValue) + 1, 0, value); return array; }})}); insert(2).into([1, 3]).after(1); //[1, 2, 3]
但使用箭头函数有一些注意点:
this
的指向,就是定义时所在的对象,而不是使用时所在的对象。new
命令,不然会抛出一个错误。arguments
对象,该对象在函数体内不存在。若是要用,能够用 rest 参数代替。yield
命令,所以箭头函数不能用做 Generator 函数。其余还有不少扩展,包括在 ES5 中,函数有个 name 属性并在标准规范中,但在 ES6 中加入了标准规范中,还有其余新增的一些特性,具体参考:函数的扩展
JavaScript 语言中,生成实例对象的传统方法是经过构造函数。下面是一个例子。
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);
上面这种写法跟传统的面向对象语言(好比 C++ 和 Java)差别很大,很容易让新学习这门语言的程序员感到困惑。
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,做为对象的模板。经过class
关键字,能够定义类。
基本上,ES6 的class
能够看做只是一个语法糖,它的绝大部分功能,ES5 均可以作到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class
改写,就是下面这样。
//定义类 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var p = new Point();
上面代码定义了一个“类”,能够看到里面有一个constructor
方法,这就是构造方法,而this
关键字则表明实例对象。也就是说,ES5 的构造函数Point
,对应 ES6 的Point
类的构造方法。
Point
类除了构造方法,还定义了一个toString
方法。注意,定义“类”的方法的时候,前面不须要加上function
这个关键字,直接把函数定义放进去了就能够了。另外,方法之间不须要逗号分隔,加了会报错。
ES6 的类,彻底能够看做构造函数的另外一种写法。
class Point { // ... } typeof Point // "function" Point === Point.prototype.constructor // true
上面代码代表,类的数据类型就是函数,类自己就指向构造函数。
使用的时候,也是直接对类使用new
命令,跟构造函数的用法彻底一致。
class Bar { doStuff() { console.log('stuff'); } } var b = new Bar(); b.doStuff() // "stuff"
构造函数的prototype
属性,在 ES6 的“类”上面继续存在。事实上,类的全部方法都定义在类的prototype
属性上面。
class Point { constructor() { // ... } toString() { // ... } toValue() { // ... } } // 等同于 Point.prototype = { constructor() {}, toString() {}, toValue() {}, };
在类的实例上面调用方法,其实就是调用原型上的方法。
class B {} let b = new B(); b.constructor === B.prototype.constructor // true
更多细节内容参考:
阮一峰好歹专门为 ES6 新增的特性写了一整本书,想用一个章节来介绍太不可能了,不少我也没有去细看。
本篇的主旨就在于大致上列出一些新特性,知道原来 ES6 新增了这些东西,后续有时间再去细看这本书,或者当用到的时候再去查。
最后,就列举下,其余上面没有讲到的特性吧:
function A(x) { A.prototype.x = x; A.prototype.y = 1; } function B() {} B.prototype = new A(); var b = new B(); b.y; //1 =
你们好,我是 dasu,欢迎关注个人公众号(dasuAndroidTv),公众号中有个人联系方式,欢迎有事没事来唠嗑一下,若是你以为本篇内容有帮助到你,能够转载但记得要关注,要标明原文哦,谢谢支持~