历时将近6年的时间来制定的新 ECMAScript 标准 ECMAScript 6(亦称 ECMAScript Harmony,简称 ES6)终于在 2015 年 6 月正式发布。自从上一个标准版本 ES5 在 2009 年发布之后,ES6 就一直以新语法、新特性的优越性吸引著众多 JavaScript 开发者,驱使他们积极尝鲜。javascript
因为ES6是在2015年发布的,因此也叫ES2015。java
之后ESCMAScript标准一年一更新,统一使用年份命名:ES201六、ES201七、….node
下面开始介绍ES6经常使用的一些新特性:es6
在ES5以前,不存在块级做用域,在编程的时候不少时候会带来不少的不便,ES6新增了块级做用域,补足了这方面的缺陷。算法
块级声明指的是该声明的变量没法被代码块外部访问。块做用域,又被称为词法做用域(lexical scopes),能够在以下的条件下建立:express
块级做用域是不少类C语言的工做机制,ECMAScript 6 引入块级声明的目的是加强 javascript 的灵活性,同时又能与其它编程语言保持一致。编程
使用let声明变量的语法和使用var声明的语法是同样的。可是let声明的变量的做用域会限制在当前的代码块中。这是let与var的最大区别。数组
<script type="text/javascript"> let a = 10; if(a > 5){ console.log(b); //用let声明的变量没有声明提早这一特性,因此此处也访问不到(报错) let b = 20; console.log(b); } console.log(b); //因为b是在if块中使用let声明的,因此此处没法访问到。(报错) </script>
注意:安全
在 ES6 使用const来声明的变量称之为常量。这意味着它们不能再次被赋值。因为这个缘由,全部的 const 声明的变量都必须在声明处初始化。const声明的常量和let变量同样也是具备块级做用域的特性。数据结构
<script type="text/javascript"> var a = 20; if (true) { const b = 20; b = 30; //错误! 常量不能从新赋值 const c; //错误! 常量声明的同时必须赋值。 } </script>
注意:
使用var声明的循环变量在循环结束后仍然能够访问到。 使用let声明的循环变量,在循环结束以后会当即销毁。
<script type="text/javascript"> for(let i = 0; i < 3; i++){ // 循环结束以后会当即销毁 i console.log(i); } console.log(i); //此处没法访问到 i 。 </script>
看下面的代码,是输出10个10,而不是0,1,2,…
<script type="text/javascript"> var funcs = []; for (var i = 0; i < 10; i++) { funcs.push(function () { console.log(i); }); } funcs.forEach(function (func) { func(); // 输出 "10" 共10次 }); </script>
解决办法须要使用函数的自执行特性。
var funcs = []; for (var i = 0; i < 10; i++) { funcs.push((function(value) { return function() { console.log(value); } }(i))); } funcs.forEach(function(func) { func(); // 输出 0,1,2 ... 9 });
若是使用let声明变量,则彻底能够避免前面的问题。 这是ES6规范中专门定义的特性。在for … in和for … of循环中也适用
<script type="text/javascript"> var funcs = []; for (let i = 0; i < 10; i++) { funcs.push(function () { console.log(i); }); } funcs.forEach(function (func) { func(); // 输出 0,1,2 ... 9 }) </script>
说明:
JavaScript函数的最大的一个特色就是在传递参数的时候,参数的个数不受限制的。为了健壮性考虑,通常在函数内部须要作一些默认值的处理。
function makeRequest(url, timeout, callback) { timeout = timeout || 2000; callback = callback || function() {}; }
其实上面的默认值方法有个bug:当timeout是0的时候也会当作假值来处理,从而给赋值默认值2000.
ES6从语言层面面上增长了 默认值的 支持。看下面的代码:
//这个函数若是只传入第一个参数,后面两个不传入,则会使用默认值。若是后面两个也传入了参数,则不会使用默认值。 function makeRequest(url, timeout = 2000, callback = function() {}) { // 其他代码 }
在非严格模式下,arguments老是能反映出命名参数的变化。看下面的代码:
<script type="text/javascript"> function foo(a, b) { //非严格模式 console.log(arguments[0] === a); //true console.log(arguments[1] === b); //true a = 10; b = 20; console.log(arguments[0] === a); //true console.log(arguments[1] === b); //true } foo(1, 2); </script>
在ES5的严格模式下,arguments只反映参数的初始值,而再也不反映命名参数的变化!
<script type="text/javascript"> function foo(a, b) { //严格模式 "use strict" console.log(arguments[0] === a); //true console.log(arguments[1] === b); //true a = 10; b = 20; console.log(arguments[0] === a); //false。 修改a的值不会影响到arguments[0]的值 console.log(arguments[1] === b); //false } foo(1, 2); </script>
当使用ES6参数默认值的时候,无论是不是在严格模式下,都和ES5的严格模式相同。看下面的代码:
<script type="text/javascript"> function foo(a, b = 30) { console.log(arguments[0] === a); //true console.log(arguments[1] === b); //true a = 10; b = 20; console.log(arguments[0] === a); //false。 因为b使用了默认值。虽然a没有使用默认值,可是仍然表现的和严格模式同样。 console.log(arguments[1] === b); //false。 b使用了默认值,因此表现的和严格模式同样。 } foo(1, 2); </script>
注意:若是这样调用foo(1),则 a == 1, b == 30, arguments[0] == 1, arguments[1] == undefined。也就是说默认值并不会赋值给arguments参数。
参数的默认值,也能够是一个表达式或者函数调用等。看下面的代码
<script type="text/javascript"> function getValue() { return 5; } function add(first, second = getValue()) { //表示使用getValue这个函数的返回值做为second的默认值。 return first + second; } console.log(add(1, 1)); // 2. 调用add函数的时候,传入了第二个参数,则以传入的参数为准。 console.log(add(1)); // 6。 调用add函数的时候,没有传入第二个参数,则会调用getValue函数。 </script>
有一点须要要注意:getValue()只会在调用add且不传入第二个参数的时候才会去调用。不是在解析阶段调用的。
<script type="text/javascript"> let value = 5; function getValue() { return value++; } function add(first, second = getValue()) { // return first + second; } console.log(add(1, 1)); // 2 console.log(add(1)); // 6。 console.log(add(1)); // 7 console.log(add(1)); // 8 </script>
因为默认值能够表达式,因此咱们甚至可使用前面的参数做为后面参数的默认值。
function add(first, second = first) { // 使用第一个参数做为第二个参数的默认值 return first + second; }
注意:能够把前面的参数做为后面参数的默认值,可是不能把后面的参数做为第一个参数的默认值。这能够前面说的let和const的暂存性死区一个意思。
function add(first = second, second)) { // 这种写法是错误的 return first + second; }
Javascript并不限制传入的参数的数量。在调用函数的时候,传入的实参的个数超过形参的个数的时候,超过的部分就成为了未命名参数。在ES5以前,咱们通常能够经过arguments对象来获取到未命名参数的值。可是罗显繁琐。
<script type="text/javascript"> function foo(a) { console.log(a); console.log(arguments[1]) //取得传入的多余的参数。 } foo(2, 3); </script>
ES6,提供了一种更加优雅处理未命名参数的问题:剩余参数( Rest Parameters )
语法:function a(a, … b){ }
剩余参数使用三个点( … )和变量名来表示。
<script type="text/javascript"> function foo(a, ...b) { console.log(a); console.log(b instanceof Array); //true .多余的参数都被放入了b中。b其实就是一个数组。 } foo(2, 3, 4, 6); </script>
注意:
例如:Math中的max函数能够返回任意多个参数中的最大值。可是若是这些参数在一个数组中,则没有办法直接传入。之前通用的作法是使用applay方法。
看下面的代码:
<script type="text/javascript"> let values = [25, 50, 75, 100] console.log(Math.max.apply(Math, values)); // 100 </script>
上面这种方法虽然可行,可是老是不是那么直观。
使用ES6提供的扩展运算符能够很容易的解决这个问题。在数组前加前缀 … (三个点)。
<script type="text/javascript"> let values = [25, 50, 75, 100] console.log(Math.max(...values)); //使用扩展运算符。至关于拆解了数组了。 console.log(Math.max(...values, 200)); //也可使用扩展运算符和参数的混用,则这个时候就有 5 个数参与比较了。 </script>
注意:剩余参数和扩展运算符都是 使用三个点做为前缀。可是他们使用的位置是不同的。
- **剩余参数是用在函数的声明的时候的参数列表中,并且必须在参数列表的后面
- 扩展运算符是用在函数调用的时候做为实参来传递的,在实参中的位置没有限制。
ECMAScript 6 最有意思的部分之一就是箭头函数。正如其名,箭头函数由 “箭头”(=>)这种新的语法来定义。
其实在别的语言中早就有了这种语法结构,不过他们叫拉姆达表达式。
基本语法以下:
(形参列表)=>{
//函数体 }
箭头函数能够赋值给变量,也能够像匿名函数同样直接做为参数传递。
<script type="text/javascript"> var sum = (num1, num2) =>{ return num1 + num2; } console.log(sum(3, 4)); //前面的箭头函数等同于下面的传统函数 var add = function (num1, num2) { return num1 + num2; } console.log(add(2, 4)) </script>
若是函数体内只有一行代码,则包裹函数体的 大括号 ({ })彻底能够省略。若是有return,return关键字也能够省略。
若是函数体内有多条语句,则 {} 不能省略。
<script type="text/javascript"> var sum = (num1, num2) => num1 + num2; console.log(sum(5, 4)); //前面的箭头函数等同于下面的传统函数 var add = function (num1, num2) { return num1 + num2; } console.log(add(2, 4)); //若是这一行代码是没有返回值的,则方法的返回自也是undefined var foo = (num1, num2) => console.log("aaa"); console.log(foo(3,4)); //这个地方的返回值就是undefined </script>
若是箭头函数只有一个参数,则包裹参数的小括号能够省略。其他状况下都不能够省略。固然若是不传入参数也不能够省略
<script type="text/javascript"> var foo = a=> a+3; //由于只有一个参数,因此()能够省略 console.log(foo(4)); // 7 </script>
若是想直接返回一个js对象,并且还不想添加传统的大括号和return,则必须给整个对象添加一个小括号 ()
<script type="text/javascript"> var foo = ()=>({name:"lisi", age:30}); console.log(foo()); //等同于下面的; var foo1 = ()=>{ return { name:"lisi", age : 30 }; } </script>
<script type="text/javascript"> var person = (name => { return { name: name, age: 30 } } )("zs"); console.log(person); </script>
在ES5以前this的绑定是个比较麻烦的问题,稍不注意就达不到本身想要的效果。由于this的绑定和定义位置无关,只和调用方式有关。
在箭头函数中则没有这样的问题,在箭头函数中,this和定义时的做用域相关,不用考虑调用方式
箭头函数没有 this 绑定,意味着 this 只能经过查找做用域链来肯定。若是箭头函数被另外一个不包含箭头函数的函数囊括,那么 this 的值和该函数中的 this 相等,不然 this 的值为 window。
<script type="text/javascript"> var PageHandler = { id: "123456", init: function () { document.addEventListener("click", event => this.doSomething(event.type), false); // 在此处this的和init函数内的this相同。 }, doSomething: function (type) { console.log("Handling " + type + " for " + this.id); } }; PageHandler.init(); </script>
看下面的一段代码:
<script type="text/javascript"> var p = { foo:()=>console.log(this) //此处this为window } p.foo(); //输出为 window对象。 并非我想要的。因此在定义对象的方法的时候应该避免使用箭头函数。 //箭头函数通常用在传递参数,或者在函数内部声明函数的时候使用。 </script>
说明:
虽然箭头函数没有本身的arguments对象,可是在箭头函数内部仍是可使用它外部函数的arguments对象的。
<script type="text/javascript"> function foo() { //这里的arguments是foo函数的arguments对象。箭头函数本身是没有 arguments 对象的。 return ()=>arguments[0]; //箭头函数的返回值是foo函数的第一个参数 } var arrow = foo(4, 5); console.log(arrow()); // 4 </script>
在JavaScript中,几乎全部的类型都是对象,因此使用好对象,对提示JavaScript的性能很重要。
ECMAScript 6 给对象的各个方面,从简单的语法扩展到操做与交互,都作了改进。
ECMAScript 6 规范明肯定义了每种对象类别。理解该术语对于从总体上认识该门语言显得十分重要。对象类别包括:
<script type="text/javascript"> function createPerson(name, age) { //返回一个对象:属性名和参数名相同。 return { name:name, age:age } } console.log(createPerson("lisi", 30)); // {name:"lisi", age:30} //在ES6中,上面的写法能够简化成以下形式 </script>
在ES6中,上面的写法能够简化成以下的形式:
<script type="text/javascript"> function createPerson(name, age) { //返回一个对象:属性名和参数名相同。 return { name, //当对象属性名和本地变量名相同时,能够省略冒号和值 age } } console.log(createPerson("lisi", 30)); // {name:"lisi", age:30} </script>
当对象字面量中的属性只有属性名的时候,JavaScript 引擎会在该做用域内寻找是否有和属性同名的变量。在本例中,本地变量 name 的值被赋给了对象字面量中的 name 属性。
该项扩展使得对象字面量的初始化变得简明的同时也消除了命名错误。对象属性被同名变量赋值在 JavaScript 中是一种广泛的编程模式,因此这项扩展的添加很是受欢迎。
<script type="text/javascript"> var person = { name:'lisi', sayHell:function () { console.log("个人名字是:" + this.name); } } person.sayHell() </script>
在ES6中,上面的写法能够简化成以下的形式:
<script type="text/javascript"> var person = { name:'李四', sayHell() { console.log("个人名字是:" + this.name); } } person.sayHell() </script>
省略了冒号和function看起来更简洁
在ES5以前,若是属性名是个变量或者须要动态计算,则只能经过 对象.[变量名] 的方式去访问。并且这种动态计算属性名的方式 在字面量中 是没法使用的。
<script type="text/javascript"> var p = { name : '李四', age : 20 } var attName = 'name'; console.log(p[attName]) //这里 attName表示的是一个变量名。 </script>
而下面的方式使用时没有办法访问到attName这个变量的。
<script type="text/javascript"> var attName = 'name'; var p = { attName : '李四', // 这里的attName是属性名,至关于各级p定义了属性名叫 attName的属性。 age : 20 } console.log(p[attName]) // undefined </script>
在ES6中,把属性名用[ ]括起来,则括号中就能够引用提早定义的变量。
<script type="text/javascript"> var attName = 'name'; var p = { [attName] : '李四', // 引用了变量attName。至关于添加了一个属性名为name的属性 age : 20 } console.log(p[attName]) // 李四 </script>
ECMAScript 从第五版开始避免在 Object.prototype 上添加新的全局函数或方法,转而去考虑具体的对象类型如数组)应该有什么方法。当某些方法不适合这些具体类型时就将它们添加到全局 Object 上 。
ECMAScript 6 在全局 Object 上添加了几个新的方法来轻松地完成一些特定任务。
在 JavaSciprt 中当你想比较两个值时,你极有可能使用比较操做符(==)或严格比较操做符(===)。许多开发者为了不在比较的过程当中发生强制类型转换,更倾向于后者。但即便是严格等于操做符,它也不是万能的。例如,它认为 +0 和 -0 是相等的,虽然它们在 JavaScript 引擎中表示的方式不一样。一样 NaN === NaN 会返回 false,因此必须使用 isNaN() 函数才能判断 NaN 。
ECMAScript 6 引入了 Object.is() 方法来补偿严格等于操做符怪异行为的过失。该函数接受两个参数并在它们相等的返回 true 。只有二者在类型和值都相同的状况下才会判为相等。以下所示:
console.log(+0 == -0); // true console.log(+0 === -0); // true console.log(Object.is(+0, -0)); // false console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(Object.is(NaN, NaN)); // true console.log(5 == 5); // true console.log(5 == "5"); // true console.log(5 === 5); // true console.log(5 === "5"); // false console.log(Object.is(5, 5)); // true console.log(Object.is(5, "5")); // false
不少状况下 Object.is() 的表现和 === 是相同的。它们之间的区别是前者 认为 +0 和 -0 不相等而 NaN 和 NaN 则是相同的。不过弃用后者是彻底没有必要的。什么时候选择 Object.is() 与 == 或 === 取决于代码的实际状况。
使用assign主要是为了简化对象的混入(mixin)。混入是指的在一个对象中引用另外一个对象的属性或方法。
assing能够把一个对象的属性和访问完整的转copy到另一个对象中。
<script type="text/javascript"> var p = { name : "lisi", age : 20, friends : ['张三', '李四'] } var p1 = {}; Object.assign(p1, p); //则p1中就有了与p相同的属性和方法. p1是接受者,p是提供者 console.log(p1); //这种copy是浅copy,也就是说若是属性值是对象的话,只是copy的对象的地址值(引用) console.log(p1.friends == p.friends); //true p1和p的friends同事指向了同一个数组。 p.friends.push("王五"); console.log(p1.friends); //['张三', '李四', '王五'] </script>
assign方法能够接受任意多的提供者。意味着后面提供者的同名属性和覆盖前面提供者的属性值。
<script type="text/javascript"> var p = { name : "lisi", age : 20, friends : ['张三', '李四'] } var p1 = { name : 'zs', } var p2 = {}; Object.assign(p2, p, p1); //p和p1都是提供者 console.log(p2.name); // zs </script>
在之前在字符串中查找字符串的时候,都是使用indexOf方法。
ES6新增了三个方法来查找字符串。
每一个方法都接收两个参数:须要搜索的文本和可选的起始索引值。当提供第二个参数后,includes() 和 startsWith() 会以该索引为起始点进行匹配,而 endsWith() 将字符串的长度与参数值相减并将获得的值做为检索的起始点。若第二个参数未提供,includes() 和 startsWith() 会从字符串的起始中开始检索,endsWith() 则是从字符串的末尾。实际上,第二个参数减小了须要检索的字符串的总量。如下是使用这些方法的演示:
var msg = "Hello world!"; console.log(msg.startsWith("Hello")); // true console.log(msg.endsWith("!")); // true console.log(msg.includes("o")); // true console.log(msg.startsWith("o")); // false console.log(msg.endsWith("world!")); // true console.log(msg.includes("x")); // false console.log(msg.startsWith("o", 4)); // true console.log(msg.endsWith("o", 8)); // true console.log(msg.includes("o", 8)); // false
ECMAScript 6 还向字符串添加了 repeat() 方法,它接受一个数字参数做为字符串的重复次数。该方法返回一个重复包含初始字符串的新字符串,重复次数等于参数。例如:
console.log("x".repeat(3)); // "xxx" console.log("hello".repeat(2)); // "hellohello" console.log("abc".repeat(4)); // "abcabcabcabc"
模板字面量是 ECMAScript 6 针对 JavaScript 直到 ECMAScript 5 依然缺失的以下功能的回应:
- 多行字符串 针对多行字符串的形式概念(formal concept)。
- 基本的字符串格式化 将字符串中的变量置换为值的能力。
- 转义 HTML 能将字符串进行转义并使其安全地插入到 HTML 的能力。
模板字面量以一种全新的表现形式解决了这些问题而不须要向 JavaScript 已有的字符串添加额外的功能。
使用一对反引号 “(tab正上方的按键)来表示模板字面量。
let message = `Hello world!`; //使用模板字面量建立了一个字符串 console.log(message); // "Hello world!" console.log(typeof message); // "string" console.log(message.length); // 12
注意:若是模板字符串中使用到了反引号,则应该转义。可是单双引号不须要转义
在ES5以前JavaScript是不支持多行字符串的。(可是在之前的版本中有一个你们都认为是bug的方式能够写出多行字符串,就是在尾部添加一个反斜杠 \)
<body> <script type="text/javascript"> var s = "abc \ aaaaaa"; console.log(s); //可是输出的结果中不包括换行 </script> </body>
可是在ES6中字符串的模板字面量轻松的解决了多行字符串的问题,并且没有任何新的语法
<script type="text/javascript"> var s = `abc aaaaa dsalfja dfadfja`; console.log(s); </script>
可是要注意: 反引号中的全部空格和缩进都是有效字符。
置换容许你将 JavaScript 表达式嵌入到模板字面量中并将其结果做为输出字符串中的一部分。
语法:${变量名、表达式、任意运算、方法调用等}
能够嵌入任何有效的JavaScript代码
<script type="text/javascript"> var name = "李四"; var msg = `欢迎你${name}同窗`; console.log(msg) </script>
模板字面量真正的强大之处来源于模板标签。一个模板标签能够被转换为模板字面量并做为最终值返回。标签在模板的头部,即左 ` 字符以前指定,以下所示:
let message = myTag`Hello world`;
在上面的代码中,myTag就是模板标签。
myTag实际上是一个函数,这个函数会被调用来处理这个模板字符串。
一个标签仅表明一个函数,他接受须要处理的模板字面量。标签分别接收模板字面量中的片断,且必须将它们组合以得出结果。函数的首个参数为包含普通 JavaScript 字符串的数组。余下的参数为每次置换的对应值。
标签函数通常使用剩余参数来定义,以便轻松地处理数据。以下:
<script type="text/javascript"> let name = '张三', age = 20, message = show`我来给你们介绍${name}的年龄是${age}.`; /* 应该定义一个函数show: 参数1:一个字符串数组。在本例中包含三个元素。 0:"我来给你们介绍" 1:"的年龄是" 2:"." 参数2和参数3:表示须要置换的字符串的值。 */ function show(stringArr, value1, value2) { console.log(stringArr); // console.log(value1); // 张三 console.log(value2); // 20 return "abc"; } console.log(message); //abc </script>
为了简化书写,通常把Value1和Value2写成剩余字符串的形式
function show(stringArr, ...values){ }
在 ECMAScript 5 或更早的版本中,从对象或数组中获取特定的数据并赋值给本地变量须要书写不少而且类似的代码。例如:
let options = { repeat: true, save: false }; // 从对象中提取数据 let repeat = options.repeat, save = options.save;
这段代码反复地提取在 options 上存储地属性值并将它们传递给同名的本地变量。虽然这些看起来不是那么复杂,不过想象一下若是你的一大批变量有着相同的需求,你就只能一个一个地赋值。并且,若是你须要从对象内部嵌套的结构来查找想要的数据,你极有可能为了一小块数据而访问了整个数据结构。
这也是 ECMAScript 6 给对象和数组添加解构的缘由。当你想要把数据结构分解为更小的部分时,从这些部分中提取数据会更容易些。不少语言都能使用精简的语法来实现解构操做。ECMAScript 6 解构的实际语法或许你已经很是熟悉:对象和数组字面量。
对象结构的语法就是在赋值语句的左侧使用相似对象字面量的结构。
let node = { type: "Identifier", name: "foo" }; //这里就至关于声明了两个变量: type = node.type; name:node.name let { type, name } = node; console.log(type); // "Identifier" console.log(name); // "foo"
在上面的结构中必需要初始化。不然会出现语法错误。
// 语法错误! var { type, name }; // 语法错误! let { type, name }; // 语法错误! const { type, name };
若是声明的变量想改变他们的值,也可使用解构表达式。
<script type="text/javascript"> let node = { type: "Identifier", name: "foo" }, type = "Literal", name = 5; //注意:此处必需要在圆括号内才能使用解构表达式 ({type, name} = node); console.log(type); // "Identifier" console.log(name); // "foo"" </script>
若是赋值号右边的对象中没有与左边变量同名的属性,则左边的变量会是 undefined
let node = { type: "Identifier", name: "foo" }; //由于node中没有叫value的属性,因此valued的值将会是undefined let { type, name, value } = node; console.log(type); // "Identifier" console.log(name); // "foo" console.log(value); // undefined
不过咱们也能够手动指定他的默认值。(这个和函数的参数默认值很像)
<script type="text/javascript"> let node = { type: "Identifier", name: "foo" }; //手动添加value的默认值为3 let { type, name, value = 3} = node; console.log(type); // "Identifier" console.log(name); // "foo" console.log(value); // 3 </script>
在前面的操做中,都是把对象的属性值,赋值给同名变量。
其实也能够赋值给不一样名的变量。
<script type="text/javascript"> let node = { type: "Identifier", name: "foo" }; // localType才是要定义的新的变量。 type是node的属性 let {type: localType, name: localName} = node; console.log(localType); // "Identifier" console.log(localName); // "foo" </script>
注意:冒号后面才是要定义的新的变量,这个能够咱们的对象字面量不太同样!
这个地方也可使用默认值。
let node = { type: "Identifier" }; let { type: localType, name: localName = "bar" } = node; console.log(localType); // "Identifier" console.log(localName); // "bar"
数据解构的语法和对象解构看起来相似,只是将对象字面量替换成了数组字面量,并且解构操做的是数组内部的位置(索引)而不是对象中的命名属性,例如:
let colors = [ "red", "green", "blue" ]; let [ firstColor, secondColor ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
若是只想取数组中的某一项,则能够不用命名。
let colors = [ "red", "green", "blue" ]; //只取数组中的第三项。 let [ , , thirdColor ] = colors; console.log(thirdColor); // "blue"
你能够想要赋值的状况下使用数组的解构赋值表达式,可是和对象解构不一样,不必将它们包含在圆括号中,例如:
let colors = [ "red", "green", "blue" ], firstColor = "black", secondColor = "purple"; [ firstColor, secondColor ] = colors; //能够不用加括号。固然添加也不犯法 console.log(firstColor); // "red" console.log(secondColor); // "green"
数组解构表达式有一个很经常使用的地方,就是交换两个变量的值。在之前通常定义一个第三方变量进行交换,例以下面的代码:
<script type="text/javascript"> let a = 3, b = 4, temp; temp = a; a = b; b = temp; console.log(a); console.log(b) </script>
那么在ES6中彻底能够抛弃第三方变量这种方式,使用咱们的数组解构表达式
<script type="text/javascript"> let a = 3, b = 4; //左侧和前面的案例是同样的,右侧是一个新建立的数组字面量。 [a, b] = [b, a]; console.log(a); console.log(b) </script>
之前咱们有5种基本数据类型:Number、String、Boolean、Null、Undefined
ES6新增了一种新的数据类型:Symbol
在ES5以前咱们都没办法建立私有变量,只能想办法去封装。symbol 来建立私有成员,这也是 JavaScript 开发者长久以来期待的一项特性。
Symbol在基本数据类型中是比较特别的。咱们之前的均可以用字面量去建立基本数据类型的数据,可是Symbol却不可使用字面量的是形式去建立。
咱们可使用symbol全局函数来建立Symbol。
<script type="text/javascript"> let firstName = Symbol(); //建立一个Symbol let person = {}; person[firstName] = "张三"; console.log(person[firstName]); // "张三" </script>
说明:上面的代码中,firstName 做为 symbol 类型被建立并赋值给 person 对象以做其属性。每次访问这个属性时必须使用该 symbol 。
在建立Symbol的时候,也能够传入字符串,这个字符串也仅仅是在调试输出的时候方便,实际没有啥用处。
<script type="text/javascript"> var s1 = Symbol("abc"); var s2 = Symbol("abc"); console.log(s1 == s2); //false </script>
注意:任意两个Symbol都不会相等,即便建立他们的时候使用了相同的参数。
既然 symbol 是基础类型,你可使用 typeof 操做符来判断变量是否为 symbol 。ECMAScript 6 拓展了 typeof 使其操做 symbol 时返回 “symbol”。例如:
let symbol = Symbol(); console.log(typeof symbol); // "symbol"
因为每个Symbol值都是不相等的,这意味着Symbol值能够做为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的状况很是有用,能防止某一个键被不当心改写或覆盖。
var mySymbol = Symbol(); // 第一种写法 var a = {}; a[mySymbol] = 'Hello!'; // 第二种写法 var a = { [mySymbol]: 'Hello!' }
以上两种写法都是相同的结果
注意:
symbol做为对象的属性的时候,只能使用 [ ] 去访问,不能使用点去访问。
symbol做为对象的属性名使用的时候,该属性仍是公开属性,不是私有属性。可是这个时候使用for… in和for…of
时没法遍历到这个symbol属性的。
Symbol 做为属性名,该属性不会出如今for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。可是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,能够获取指定对象的全部 Symbol 属性名。
看下面的代码
<script type="text/javascript"> var obj = {}; var a = Symbol('a'); var b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; // 返回obj对象全部Symbol类型的属性名组成的数组。 var objectSymbols = Object.getOwnPropertySymbols(obj); console.log(objectSymbols) //[Symbol(a), Symbol(b)] </script>
看下面的代码
var obj = {}; var foo = Symbol("foo"); obj[foo] = "lisi"; for (var i in obj) { console.log(i); // 无输出 。 由于遍历不到Symbol型的属性 } Object.getOwnPropertyNames(obj);// [] 只能拿到非Symbol类型的属性 Object.getOwnPropertySymbols(obj) //[Symbol(foo)]
还有一个新API能够拿到全部类型的属性,包括常规和Symbol型的。
Reflect.ownKeys
let obj = { [Symbol('my_key')]: 1, enum: 2, nonEnum: 3 }; Reflect.ownKeys(obj);// ["enum", "nonEnum", Symbol(my_key)]
说明:
1、Symbol.for(字符串参数):在全局环境中搜索 以该字符串做为参数的Symbol值,若是搜到则返回这个sybol,若是搜不到则建立一个Symbol,并把它注册在全局环境中。
<script type="text/javascript"> //第一次搜不到,则新建立一个返回,并在全局环境(window)中注册 var a = Symbol.for("foo"); //第二次搜到上次建立的 var b = Symbol.for("foo"); console.log(a === b); //由于两次搜到的是同一个Symbol,因此此处是true </script>
Symbol.for()和Symbol()均可以建立Symbol类型的数据。
两者区别:
- Symbol.for()对一样的字符串,每次获得结果确定是同样的。由于都是从全局环境中搜索。
- Symbol()则不会有搜索的过程,每次都是一个全新的不一样的symbol,并且也不会向全局环境中注册。
看下面的代码
<script type="text/javascript"> var a = Symbol("foo"); var b = Symbol.for("foo"); console.log(a == b); //false </script>
2、Symbol.keyFor(symbol):返回一个已经注册的symbol的”key”。
<script type="text/javascript"> var a = Symbol("foo"); var b = Symbol.for("foo"); console.log(Symbol.keyFor(a)); // undefined. 由于a没有想全局环境中登记,因此是undefinded console.log(Symbol.keyFor(b)); // foo </script>
JavaScript 在绝大部分历史时期内只有一种集合类型,那就是数组。数组在 JavaScript 中的使用方式和其它语言很类似,可是其它集合类型的缺少致使数组也常常被看成队列(queues)和栈(stacks)来使用。
由于数组的索引只能是数字类型,当开发者以为非数字类型的索引是必要的时候会使用非数组对象。这项用法促进了以非类数组对象为基础的 set 和 map 集合类型的实现。
Set是相似数组的一种结构,能够存储数据,与数组的区别主要是 Set中的元素不能重复,而数组中的元素能够重复。
一句话总结:Set类型是一个包含无重复元素的有序列表
Set自己是一个构造函数。
<script type="text/javascript"> //建立Set数据结构对象。 var s = new Set(); //调用set对象的add方法,向set中添加元素 s.add("a"); s.add("c"); s.add("b"); //set的size属性能够获取set中元素的个数 console.log(s.size) </script>
<script type="text/javascript"> var s = new Set(); s.add("a"); s.add("c"); s.add("b"); s.add("a"); //重复,因此添加失败。注意这个地方并不会保存。 console.log(s.size); // 长度是3 </script>
看下面的代码:
<script type="text/javascript"> var s = new Set(); s.add(5); s.add("5"); console.log(s.size); // 长度是2 </script>
在上面的代码中,数字5和字符串5都会添加成功。为何呢?
Set是使用什么机制来判断两个元素是否相等的呢?
是经过咱们前面说过的 Object.is(a, b) 来判断两个元素是否相等。
回忆一下:这个方法除了 +0和-0、NaN和NaN认为相等,其他和三个 === 是彻底同样的。
<script type="text/javascript"> var s = new Set(); s.add(+0); s.add(-0); //重复添加不进去 s.add(NaN); s.add(NaN); //重复添加不进去 s.add([]); s.add([]); //两个空数组不相等,因此能够添加进去 s.add({}); s.add({}); // 两个空对象也不重复,因此也能够添加进去 console.log(s.size); // 长度是6 </script>
<script type="text/javascript"> //使用数组中的元素来初始化Set,固然碰到重复的也不会添加进去。 var s = new Set([2, 3, 2, 2, 4]); console.log(s.size) </script>
使用Set的 has() 方法能够判断一个值是否在这个set中。
<script type="text/javascript"> let set = new Set(); set.add(5); set.add("5"); console.log(set.has(5)); // true console.log(set.has(6)); // false </script>
delete(要删除的值) :删除单个值
clear():清空全部的值
<script type="text/javascript"> let set = new Set(); set.add(5); set.add("5"); console.log(set.has(5)); // true set.delete(5); console.log(set.has(5)); // false console.log(set.size); // 1 set.clear(); console.log(set.has("5")); // false console.log(set.size); // 0 </script>
数组有个方法forEach能够遍历数组。
- Set也有forEach能够遍历Set。
使用Set的forEach遍历时的回调函数有三个参数:
function (value, key, ownerSet){
}
参数1:遍历到的元素的值
参数2:对set集合来讲,参数2的值和参数1的值是彻底同样的。
参数3:这个set本身
<script type="text/javascript"> let set = new Set(["a", "c", "b", 9]); set.forEach(function (v, k, s) { console.log(v + " " + (v === k) + " " + (s === set)); // 永远是true }) </script>
- for…of也能够遍历set。
for(var v of set){ console.log(v) }
将数组转换为Set至关容易,你只须要在建立Set集合时把数组做为参数传递进去便可。
把Set转换为数组使用前面讲到的扩展运算符也很容易
<script type="text/javascript"> let set = new Set([1, 2, 3, 3, 3, 4, 5]), arr = [...set]; //使用扩展运算符。那么新的数组中已经没有了重复元素。注意,此对set并无什么影响 console.log(arr); // [1,2,3,4,5] </script>
这种状况在须要去数组中重复元素的时候很是好用。
<script type="text/javascript"> function eliminateDuplicates(items) { return [...new Set(items)]; } let numbers = [1, 2, 3, 3, 3, 4, 5, 5, 2, 1, 1], //返回的是新的没有重复元素的数组。 noDuplicates = eliminateDuplicates(numbers); console.log(noDuplicates); // [1,2,3,4,5] </script>
Set提供了处理一系列值的方式,不过若是想给这些值添加一些附加数据则显得力不从心,因此又提供了一种新的数据结构:Map
ECMAScript 6 中的 map 类型包含一组有序的键值对,其中键和值能够是任何类型。
键的比较结果由 Object.is() 来决定,因此你能够同时使用 5 和 “5” 作为键来存储,由于它们是不一样的类型。
这和使用对象属性作为值的方法截然不同,由于 对象的属性会被强制转换为字符串类型。
- Map建立也是使用Map构造函数
- 向Map存储键值对使用set(key, value);方法
- 可使用get(key),来获取指定key对应的value
<script type="text/javascript"> var map = new Map(); map.set("a", "lisi"); map.set("b", "zhangsan"); map.set("b", "zhangsan222"); // 第二次添加,新的value会替换掉旧的 console.log(map.get("a")); console.log(map.get("b")); //zhangsan222 console.log(map.get("c")); //undefined.若是key不存在,则返回undefined console.log(map.size); //2 </script>
建立Map的时候也能够像Set同样传入数组。可是传入的数组中必须有两个元素,这个两个元素分别是一个数组。
也就是传入的实际是一个二维数组!
<script type="text/javascript"> //map接受一个二维数组 var map = new Map([ //每个数组中,第一个是是map的能够,第二个是map的value。若是只有第一个,则值是undefined ["name", "lisi"], ["age", 20], ["sex", "nan"] ]); console.log(map.size); console.log(map.get("name")) </script>
<script type="text/javascript"> var map = new Map([ ["name", "李四"], ["age", 20], ["sex", "nan"] ]); /* 回调函数有函数: 参数1:键值对的value 参数2:键值对的key 参数3:map对象自己 */ map.forEach(function (value, key, ownMap) { console.log(`key=${key} ,vlue=${value}`); console.log(this); }) </script>
var colors = ["red", "green", "blue"]; for (var i = 0, len = colors.length; i < len; i++) { console.log(colors[i]); }
上面的代码写起来简单,可是实际使用的过程当中,咱们需求本身去控制变量,若是有嵌套的状况下,还要控制多个变量,很容易出错。
迭代器就是为了解决这个问题的。
迭代器只是带有特殊接口(方法)的对象。全部迭代器对象都带有 next() 方法并返回一个包含两个属性的结果对象。这些属性分别是 value 和 done,前者表明下一个位置的值,后者在没有更多值可供迭代的时候为 true 。迭代器带有一个内部指针,来指向集合中某个值的位置。当 next() 方法调用后,指针下一位置的值会被返回。
若你在末尾的值被返回以后继续调用 next(),那么返回的 done 属性值为 true,value 的值则由迭代器设定。该值并不属于数据集,而是专门为数据关联的附加信息,如若该信息并未指定则返回 undefined 。迭代器返回的值和函数返回值有些相似,由于二者都是返回给调用者信息的最终手段。
咱们能够用ES5以前的知识手动建立一个迭代器:
function createIterator(items) { var i = 0; return { next: function() { var done = (i >= items.length); var value = !done ? items[i++] : undefined; return { done: done, value: value }; } }; } //建立一个能够在指定数组上面迭代的迭代器对象。 var iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // for all further calls console.log(iterator.next()); // "{ value: undefined, done: true }"
从以上的示例来看,根据 ECMAScript 6 规范模拟实现的迭代器仍是有些复杂。
幸运的是,ECMAScript 6 还提供了生成器,使得迭代器对象的建立容易了许多。
生成器函数就是返回迭代器的函数!
生成器函数由 function 关键字和以后的星号(*)标识,同时还能使用新的 yield 关键字。
看下面代码:
<script type="text/javascript"> //生成器函数。 注意中间的 * 不能丢 function * createIterator() { //每一个yield的后面的值表示咱们迭代到的值。 yield也定义了咱们迭代的顺序。 yield 3; yield 4; yield 2; } var it = createIterator(); console.log(it.next().value); // 2 console.log(it.next().value); // 4 console.log(it.next().value); // 2 console.log(it.next().value); //undefined </script>
迭代器函数也是函数,因此他能够像正常的函数同样调用,可是生成器函数会自动返回一个迭代器对象。
每调用一次迭代器的next方法,若是碰到yield都会返回一个迭代到的一个对象,而后中止继续执行,直到下次调用next方法,会从上次中止的地方继续执行。
//这个迭代器函数返回的迭代器能够迭代传入的数组中的全部元素。 function *createIterator(items) { for (let i = 0; i < items.length; i++) { //每调用一次next,碰到yild程序就会中止,并返回迭代到的对象 {value : items[i], done : true} yield items[i]; } } let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // 进一步调用 console.log(iterator.next()); // "{ value: undefined, done: true }"
注意:
你可使用函数表达式来建立生成器,只需在 function 关键字和圆括号之间添加星号(*)。例如:
let createIterator = function *(items) { for (let i = 0; i < items.length; i++) { yield items[i]; } }; let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // 进一步调用 console.log(iterator.next()); // "{ value: undefined, done: true }"
注意:没法使用箭头函数来建立生成器。
迭代器的主要工做就是迭代数据,可是不是全部的数据都是能够迭代的。
与迭代器紧密相关的是,可迭代类型是指那些包含 Symbol.iterator 属性的对象。
该 symbol 类型定义了返回迭代器的函数。在 ECMAScript 6 中,全部的集合对象(数组,set 和 map)与字符串都是可迭代类型,所以它们都有默认的迭代器。可迭代类型是为了 ECMAScript6 新添加的 for-of 循环而设计的。
换句话说,默认状况下只有 数组、set、Map和字符串才可使用迭代器去迭代。 (也就可使用for…of了)
for…of循环只迭代出来的元素,根本无论索引!无论索引!无论索引!重要的问题重复三遍!
使用 for…of 迭代数组:
<script type="text/javascript"> var arr = ["a", "c", "b", "d"]; for(var item of arr){ console.log(item) } </script>
使用 for…of 迭代Set:
<script type="text/javascript"> var set = new Set(["a", "c", "b", "d"]); for(var item of set){ console.log(item) } </script>
使用 for…of 迭代Map:
<script type="text/javascript"> var map = new Map([["name", "lisi"],["sex", "男"],["age", 20]]); map.set("aaa", "bbb") for(var item of map){ console.log(item); //注意:这里迭代到的是由key和value组成的数组。 } </script>
使用for … of迭代字符串
<script type="text/javascript"> var s = "abcd"; for(let c of s){ console.log(c) } </script>
注意:for…of 只能迭代能够迭代的对象,对于非可迭代对象使用for…of会抛出异常
说明:以数组为例。
for-of 循环首先会调用 values 数组的 Symbol.iterator 方法来获取迭代器(Symbol.iterator 方法由幕后的 JavaScript 引擎调用)。以后再调用 iterator.next() 并将结果对象中的 value 属性值,即 1,2,3,依次赋给 num 变量。当检测到结果对象中的 done 为 true,循环会退出,因此 num 不会被赋值为 undefined 。
若是你只想简单的迭代数组或集合中的元素,那么 for-of 循环比 for 要更好。for-of 通常不容易出错,由于要追踪的条件更少。因此仍是把 for 循环留给复杂控制条件的需求吧。
Symbol.iterator是可迭代类型的一个方法,调用这个方法就能够获取到他的默认迭代器。
<script type="text/javascript"> let s = "abcd"; let it = s[Symbol.iterator](); //调用字符串的Symbol.iterator方法 console.log(it.next()); //返回迭代器迭代到的第一个对象 </script>
由于Symbol能够返回一个对象的默认迭代器,因此咱们可使用它来判断一个对象是否可迭代
<script type="text/javascript"> function isIterable(object) { return typeof object[Symbol.iterator] === "function"; } console.log(isIterable([1, 2, 3])); // true console.log(isIterable("Hello")); // true console.log(isIterable(new Map())); // true console.log(isIterable(new Set())); // true console.log(isIterable({"name":"李四"})); // false。普通对象不可迭代 </script>
开发者自定义的对象默认是不可迭代类型,可是你能够为它们建立 Symbol.iterator 属性并指定一个生成器来使这个对象可迭代。例如:
let collection = { items: [], *[Symbol.iterator]() { for (let item of this.items) { yield item; } } }; collection.items.push(1); collection.items.push(2); collection.items.push(3); for (let x of collection) { console.log(x); }
和大多数面向对象的语言(object-oriented programming language)不一样,JavaScript 在诞生之初并不支持使用类和传统的类继承并做为主要的定义方式来建立类似或关联的对象。
这很令开发者困惑,并且在早于 ECMAScript 1 到 ECMAScript 5 这段时期,不少库都建立了一些实用工具(utility)来让 JavaScript 从表层上支持类。
尽管一些 JavaScript 开发者强烈主张该语言不须要类,但因为大量的库都对类作了实现,ECMAScript 6 也顺势将其引入。
在 ECMAScript 5 或更早的版本中,JavaScript 没有类。和类这个概念及行为最接近的是建立一个构造函数并在构造函数的原型上添加方法,这种实现也被称为自定义的类型建立,例如:
function PersonType(name) { this.name = name; } PersonType.prototype.sayName = function() { console.log(this.name); }; let person = new PersonType("Nicholas"); person.sayName(); // 输出 "Nicholas" console.log(person instanceof PersonType); // true console.log(person instanceof Object); // true
说明:
前面的PersonType咱们之前一直叫作构造函数,其实他就是一个类型,由于他确实表示了一种类型。
在ES6直接借鉴其余语言,引入了类的概念。因此再实现上面那种模拟 的类就容易了不少。
//class关键字必须是小写。 后面就是跟的类名 class PersonClass { // 等效于 PersonType 构造函数。 constructor(name) { //这个表示类的构造函数。constuctor也是关键字必须小写。 this.name = name; //建立属性。 也叫当前类型的自有属性。 } // 等效于 PersonType.prototype.sayName. 这里的sayName使用了咱们前面的简写的方式。 sayName() { console.log(this.name); } } let person = new PersonClass("Nicholas"); person.sayName(); // 输出 "Nicholas" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function"
说明:
虽然类和之前的使用构造函数+原型的方式很像,可是仍是有一些不太相同的地方,并且要牢记
函数有函数表达式,类也有类表达式。
类表达式的功能和前面的类的声明是同样的。
let PersonClass = class { // 等效于 PersonType 构造函数 constructor(name) { this.name = name; } // 等效于 PersonType.prototype.sayName sayName() { console.log(this.name); } }; let person = new PersonClass("Nicholas"); person.sayName(); // 输出 "Nicholas" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function"
let PersonClass = class PersonClass2{ // 等效于 PersonType 构造函数 constructor(name) { this.name = name; } // 等效于 PersonType.prototype.sayName sayName() { console.log(this.name); } };
注意:具名类表达式中PersonClass2这个类名只能在类的内部访问到,在外面是访问不到的.
在JavaScript中,函数是做为一等公民存在的。(也叫一等函数)。
类也是一等公民。
function createObject(classDef) { return new classDef(); } let obj = createObject(class { sayHi() { console.log("Hi!"); } }); obj.sayHi(); // "Hi!"
let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); } }("Nicholas"); person.sayName(); // "Nicholas"
类的成员,也能够像咱们前面的对象的属性同样能够动态计算.( 使用[ ] 来计算)
let methodName = "sayName"; class PersonClass { constructor(name) { this.name = name; } [methodName]() { console.log(this.name); } } let me = new PersonClass("Nicholas"); me.sayName(); // "Nicholas"
在ES5中,咱们能够直接给构造函数添加属性或方法来模拟静态成员。
function PersonType(name) { this.name = name; } // 静态方法。 直接添加到构造方法上。 (实际上是把构造函数当作一个普通的对象来用。) PersonType.create = function(name) { return new PersonType(name); }; // 实例方法 PersonType.prototype.sayName = function() { console.log(this.name); }; var person = PersonType.create("Nicholas");
在上面的create方法在其余语言中通常都是做为静态方法来使用的。
下面高能,请注意:
ECMAScript 6 的类经过在方法以前使用正式的 static 关键字简化了静态方法的建立。例如,下例中的类和上例相比是等效的:
class PersonClass { // 等效于 PersonType 构造函数 constructor(name) { this.name = name; } // 等效于 PersonType.prototype.sayName sayName() { console.log(this.name); } // 等效于 PersonType.create。 static create(name) { return new PersonClass(name); } } let person = PersonClass.create("Nicholas");
注意:静态成员经过实例对象不能访问,只能经过类名访问!!!
经过和ES5模拟静态方法的例子你应该知道为啥了吧
在ES6以前要完成继承,须要写不少的代码。看下面的继承的例子:
<script type="text/javascript"> function Father(name) { this.name = name; } Father.prototype.sayName = function () { console.log(this.name); } function Son(name,age) { Father.call(this, name); this.age = age; } Son.prototype = new Father(); Son.prototype.constructor = Son; Son.prototype.sayAge = function () { console.log(this.age); } var son1 = new Son("儿子", 20); son1.sayAge(); //20 son1.sayName(); //儿子 </script>
若是在ES6经过类的方式完成继承就简单了不少。
须要用到一个新的关键字:extends
<script type="text/javascript"> class Father{ constructor(name){ this.name = name; } sayName(){ console.log(this.name); } } class Son extends Father{ //extents后面跟表示要继承的类型 constructor(name, age){ super(name); //至关于之前的:Father.call(this, name); this.age = age; } //子类独有的方法 sayAge(){ console.log(this.age); } } var son1 = new Son("李四", 30); son1.sayAge(); son1.sayName(); console.log(son1 instanceof Son); // true console.log(son1 instanceof Father); //true </script>
这种继承方法,和咱们前面提到的构造函数+原型的继承方式本质是同样的。可是写起来更简单,可读性也更好。
关于super的使用,有几点须要注意:
若是在子类中声明与父类中的同名的方法,则会覆盖父类的方法。(这种状况在其余语言中称之为 方法的覆写、重写 )
<script type="text/javascript"> class Father{ constructor(name){ this.name = name; } sayName(){ console.log(this.name); } } class Son extends Father{ //extents后面跟表示要继承的类型 constructor(name, age){ super(name); //至关于之前的:Father.call(this, name); this.age = age; } //子类独有的方法 sayAge(){ console.log(this.age); } //子类中的方法会屏蔽到父类中的同名方法。 sayName(){ super.syaName(); //调用被覆盖的父类中的方法。 console.log("我是子类的方法,我屏蔽了父类:" + name); } } var son1 = new Son("李四", 30); son1.sayAge(); son1.sayName(); </script>
若是在子类中又确实须要调用父类中被覆盖的方法,能够经过super.方法()来完成。
注意:
- 若是是调用构造方法,则super不要加点,并且必须是在子类构造方法的第一行调用父类的构造方法
- 普通方法调用须要使用super.父类的方法() 来调用。
原文地址:http://blog.csdn.net/u012468376/article/details/54565068<script type="text/javascript"> class Father{ static foo(){ console.log("我是父类的静态方法"); } } class Son extends Father{ } Son.foo(); //子类也继承了父类的静态方法。 这种方式调用和直接经过父类名调用时同样的。 </script>