1.let 和 const 命令
在 es5 时, 只有两种变量声明, var 和 function. 在 es6 中新增了四种 let 和 const, 以及另外两种声明 import 和 class. 我们先讲解 let 和 const, 后续会补充 import 和 class
(1)let
我们先来看基本语法
- {
- let a = 10;
- var b = 1;
- }
- b // 1
- a // ReferenceError: a is not defined.
复制代码
我们在代码块中声明了 a,b. 然后 a is not defined. 这是因为 let 命令只在对应的代码块中有效, 我们在外部去引用它, 就会报错. 这就是 let 的块级作用域的效果, 如果不太清楚什么是块级作用域. 我们来看下面的例子
- var a = [];
- for (var i = 0; i <10; i++) {
- a[i] = function () {
- console.log(i);
- };
- }
- a[0]();//10
- a[6](); // 10
复制代码
这是一个老生常谈的问题了. i 在 for 循环中定义的是全局变量. 我们在调用函数时. 函数内部的 i 引用的是全局的 i, 所以打印出来的 是 10. 我们之前是这样解决的.
- var a = [];
- for (var i = 0; i < 10; i++) {
- a[i] = (function (a) {
- return function(){
- console.log(a);
- }
- }(i));
- }
- a[0]();//0
- a[6](); // 6
复制代码
我们使用了立即执行函数将 i 的值传入了内部的函数, 这样内部的函数就能够获取到对应的 i.
我们用 let 来代替 var, 最后输出的是 6.
- var a = [];
- for (let i = 0; i < 10; i++) {
- a[i] = function () {
- console.log(i);
- };
- }
- a[6](); // 6
复制代码
这是因为我们每一次的循环都生成了一个新的块级作用域, 内部保存着 i 的值, 所以就会打印出 6.
let 不存在变量提升
- console.log(a);
- console.log(b);
- var a=0;//undefined
- let b=0;//ReferenceError: b is not defined
复制代码
TDZ(暂时性死区) let 命令在块级作用域中, 即使不存在变量提升, 它也会影响当前块级作用域, 即绑定在了当前作用域. 在作用域中引用外部的变量将会报错.
- var a=10;
- {
- console.log(a); //ReferenceError: a is not defined
- let a=10;
- }
复制代码
同时, 我们在 TDZ 中使用 typeof 也会报错.
console.log( typeof a);//undefined
复制代码
- console.log( typeof a);//ReferenceError: a is not defined
- let a=10;
复制代码
let 不允许重复声明变量
- {
- var a=0;
- let a=1;//SyntaxError: Identifier 'a' has already been declared
- }
复制代码
(2) const 常量
const 常量声明一个只读属性的变量, 不可更改, 不可先声明后赋值, 生成块级作用域.
- const a;//SyntaxError: Missing initializer in const declaration(const 声明中缺少初始值设定项)
- a=10;
复制代码
它同 let 有很多相似的地方. . 不可重复声明
- const a=10;
- var a=10;//SyntaxError: Identifier 'a' has already been declared
复制代码
. 变量不提升
- console.log(a)//ReferenceError: a is not defined
- const a=10;
复制代码
. 同样存在暂时性死区
- var a=10;
- {
- console.log(a);//ReferenceError: a is not defined
- const a=10;
- }
复制代码
另外, const 保证常量的值不被修改的原理是什么呢? const 实际上无法改变的只是常量在栈区的值不变, 如果这个值是一个基本数据类型, 那么 const 能够保障常量的值不变, 但如果是引用类型的数据, 栈区保存的其实是对应常量的地址. 地址无法改变, 但是对应地址的堆区内容却可以改变.
- const a=[1,2,3]
- a.push(4)
- console.log(a); //[1, 2, 3, 4]
复制代码
很显然, 我们通过 push, 直接修改了堆区的内容, 间接改变了 const 常量的值.
如果要真正做到常量的功能, 可以使用 Object.freeze()
- var a=[1,2,3];
- Object.freeze(a)
- a.push(4) //Cannot add property 3, object is not extensibleat Array.push
- console.log(a);
复制代码
2. 字符串扩展方法
(1)for of 字符串遍历
使用 for of 可以遍历字符串, 并将字符串分解为单独的字符串
- var a='lang'
- for (item of a){
- console.log(item);
- }
- // l
- // a
- // n
- // g
复制代码
(2) codePointAt 字符串查询.
根据下标, 查询对应字符串. 在 Es5 时, 就有一个 charAt()方法. 但 charAt()显然没想到, 随着 Unicode 编码的扩展, 原先的 0x0000~0xFFFF 已经无法满足表示所有字符了.
- var text = "";
- text.charAt(0); //''text.charAt(1); //''
复制代码
所以 es6 中新增了 codePointAt(), 查询, codePointAt()有着更好的 unicode 支持, 能够查询 > 0xFFFF 的字符.
- var text = "";
- console.log(text.codePointAt(0)); //134071
复制代码
(3)includes()值存在判断
在 es6 之前使用 indexof 也可以进行值存在判断. includes 与 indexof 既可以进行字符串的判断, 也可以进行数组值的判断, 但是 indexof 在对 NaN 进行判断时会出现不准确.
- var text = [1,NaN]
- console.log(text.indexOf(NaN));//-1
复制代码
另外, indexof 的返回结果为 - 1||0,includes 为 true||false.
(4) repeat()字符串重复
str=repeat(n)返回的是新的字符串, 并且会将 str 的字符串重复 n 次.
- var a='lang'
- console.log(a.repeat(3));//langlanglang
复制代码
其中 n 会自动取整. n<=-1||n==infinaty 将会报错.
(5)startwith,endwith.
startWith('str',n): 返回布尔值, 表示参数字符串是否在原字符串的头部.
endsWith('str',n): 返回布尔值, 表示参数字符串是否在原字符串的尾部. 其中 str 表示要判断的值, n 表示从目标字符串的第几个元素开始.
- var str='hello world'
- console.log(str.startsWith('hello',0)); //true
- console.log(str.startsWith('world',6)); //true
- console.log(str.startsWith('world',0)); //false
复制代码
(6)padStart(),padEnd()
es6 提供了两个字符串追加的方法 String.prototype.padStart 和 String.prototype.padEnd, 方便我们将一个新的字符串追加到某个字符串的头尾.
我们常常使用 padstart 来使字符串输出时保持格式.
- var a='abc'
- var b='abcd'
- var c='abcd'
- console.log(a.padStart('10',0));
- console.log(b.padStart('10',0));
- console.log(c.padStart('10',0));
- //0000000abc
- //000000abcd
- //00000abcde
复制代码
但有时候使用 endstart 显然会更好.
- var a='abc'
- var b='abcd'
- var c='abcde'
- console.log(a.padEnd(10,'-------'));
- console.log(b.padEnd(10,'-------'));
- console.log(c.padEnd(10,'-------'));
- //abc-------
- //abcd------
- //abcde-----
复制代码
也可以组合使用达到效果
- var obj={
- name:'wangcai',
- car:'BMW',
- wife:'fatgirl'
- }
- for(var item in obj){
- console.log(item.padEnd(10,'-')+'value:'+obj[item].padStart(10,'**'));
- }
- //name------value:***wangcai
- //car-------value:*******BMW
- //wife------value:***fatgirl
复制代码
(7)** 模板字符串
模板字符串的引入是 es6 的一大亮点, 它使得输出模板变得简洁而方便. 模板采用反引号(``), 中间支持各种格式的输出. 包括换行, 空格, 变量, 表达式, 调用函数等. 我们可以在一个模板中组合使用它们
- var age=22;
- var name='lang'
- var say=()=>{
- return 'hello'
- }
- var str=`myname is${name} my age is ${age} and i can say ${say()}`
- console.log(str); //myname islang my age is 22 and i can say hello
复制代码
在模板字符串的 ${} 中可以写任意表达式, 但是同样的, 对 if / else 判断, 循环语句无法处理.
但在很多时候我们需要去使用 if 或者循环. 我们可以先在外部使用逻辑处理语句, 然后生成一个我们想要的模板, 在用 `` 进行转义
- var age=22;
- var name='lang'
- var name2='Lang'
- var str=''
- var say=()=>{
- return 'hello'
- }
- if(age>=18){str=name2}
- var str=`myname is${str} my age is ${age} and i can say ${say}`
- console.log(str); //myname isLang my age is 22 and i can say hello
复制代码
当然, 我们也可以使用数组, 将各个模板片段存入数组之中, 最后通过 Array.join('')将其拼接为最终的模板.
3. 数值扩展方法
(1)二进制和八进制表示法
之前在 es5 时的严格模式中,, 我们已经无法使用二进制和八进制. 在 es6 中提供了两个新的二进制和八进制写法.
二进制 (0bxxx||0Bxxx) 八进制(0oxxx||0Oxxx)
- 'use strict'
- var a=0b11;
- var b=0o11
- console.log(a);//3
- console.log(b);//9
复制代码
(2)Number.isNaN()
isNAN 可以用来检测数据是否是 NAN 类型, 只有 NAN 才会返回 true, 其余类型皆返回 false.
- var x=NaN*2
- console.log(Number.isNaN(x));//true
- console.log(NaN==NaN);//false
- console.log(NaN===NaN);//false
复制代码
(3)Number.parseInt(), Number.parseFloat()
这两个方法在 es5 中已经存在, es6 将其从全局对象中提取放入了 Number 对象中
- (4)Number.isIntger()
- var a=3.00
- var b=10;
- var c=false;
- var d=4.00000000000000000000000000000002
- console.log(Number.isInteger(a));//true
- console.log(Number.isInteger(b));//true
- console.log(Number.isInteger(c));//false
- console.log(Number.isInteger(d));//true
复制代码
只有整数, 以及类似 3.0 这样的浮点数才会被认为是整数, 返回 true, 除此之外, js 运算具有不准确性, 超出精度范围的值, 会默认为 o, 所以 4.0000000000000000000000002 会被看做是 4. (5)Math.trunc()
取整函数, 会对值进行取整, 会对传入的值先进行 Number()处理, 正数将会进行 Math.floor(), 若为负数则进行 Math.ceil();
- console.log(Math.trunc(4.1));//4
- console.log(Math.trunc(4.9));//4
- console.log(Math.trunc(-1.2));//-1
- console.log(Math.trunc(-1.9));//-1
- console.log(Math.trunc(true));//1
- console.log(Math.trunc('lang'));//NaN
复制代码
3. 函数扩展
(1)函数指定默认值 可以为函数传入的参数指定默认值, 函数内部可以覆盖使用.
- function say(x,y=5){
- console.log(x,y); //1,5
- y=10;
- console.log(x,y); //1,10
- }
- say(1)
复制代码
需要注意以下两点
. 使用参数默认值时, 函数不能有同名参数.
- // 不报错
- function foo(x, x, y) {
- // ...
- }
- // 报错
- function foo(x, x, y = 1) {
- // ...
- }
- // SyntaxError: Duplicate parameter name not allowed in this context
复制代码
. 不可使用 let,const 重复声明
- function say(x,y=5){
- let y=10;
- console.log(x,y); //SyntaxError: Identifier 'y' has already been declared
- }
- say(1)
复制代码
(2)rest 参数 在函数形参中使用... 扩展运算符, 可以将不定形参传入 rest 数组中.
- function say(...res) {
- for (var item of res) {
- console.log(item);
- }
- }
- say(1, 2, 3)
- //1
- //2
- //3
复制代码
(3)箭头函数(重点)
基本使用方法
- var f=(x,y)=>{ return x+y}
- console.log(f(1,2)); //3
复制代码
假如 (x,y) 只有一个参数, 我们可以省略(), 同样返回语句中, 若只有一条语句, 也可以省略.
- var f=x=>x+10
- console.log(f(1)); //11
复制代码
使用注意点 箭头函数有几个使用注意点.
(1)函数体内的 this 对象, 就是定义时所在的对象, 而不是使用时所在的对象.
(2)不可以当作构造函数, 也就是说, 不可以使用 new 命令, 否则会抛出一个错误.
(3)不可以使用 arguments 对象, 该对象在函数体内不存在. 如果要用, 可以用 rest 参数代替.
(4)不可以使用 yield 命令, 因此箭头函数不能用作 Generator 函数.
对于需要注意的第一点, 我们经常会因为维护回调函数的 this 而烦恼, 而在箭头函数中则不存在这个问题, 箭头函数内部的 this 是固定的, 不会因为函数调用而改变.
在 ES5, 我们通常采用外部保存 this 的方法, 来维护 this,
- // ES6
- function foo() {
- setTimeout(() => {
- console.log('id:', this.id);
- }, 100);
- }
- // ES5
- function foo() {
- var that= this;
- setTimeout(function () {
- console.log('id:', that.id);
- }, 100);
- }
复制代码
argumet 在箭头函数中不存在, 那么使用 argument 将会使用外部函数的 argument
- function foo() {
- setTimeout(() => {
- console.log('args:', arguments);
- }, 100);
- }
- foo(2, 4, 6, 8)
- // args: [2, 4, 6, 8]
复制代码
(4)尾调用
在函数的最后一步调用一个函数, 这叫做尾调用. 解释很简单, 但却很容易混淆. . 最后一步? 什么是最后一步, 在函数中, return 就是最后一步, 没有 return 的都不是尾调用.
- function g(){}
- function f(){
- g() // 这不是尾调用
- }
- f()
复制代码
. 即使 return 后面还有表达式, 但这些函数不起作用, 那么它依旧是尾调用.
- function g(){}
- function f(){
- return g() // 这是尾调用
- console.log('121');
- }
- f()
复制代码
. 返回的必须是函数, 不能是表达式. 下面额例子中, 返回了 g()+1; g()+1; 不是一个函数, 而是一个表达式.
- function g(){}
- function f(){
- return g()+1 // 这不是尾调用
- }
- f()
复制代码
(5)尾递归
尾递归是尾调用的一种特殊情况, 但尾递归是在最后一步调用自身.
我们在使用递归时, 必须给一个终止条件, 不然就会产生死循环
- var a=0;
- function f(){
- a++
- return f() // 这显然是一个死循环
- }
- f()
复制代码
我们常常使用递归等方法来求阶乘等, 但递归很容易发生栈溢出的情况.
非尾递归的 Fibonacci 数列实现如下.
- function Fibonacci (n) {
- if ( n <= 1 ) {return 1};
- return Fibonacci(n - 1) + Fibonacci(n - 2);
- }
- Fibonacci(10) // 89
- Fibonacci(100) // 堆栈溢出
- Fibonacci(500) // 堆栈溢出
复制代码
尾递归优化过的 Fibonacci 数列实现如下.
- function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
- if( n <= 1 ) {return ac2};
- return Fibonacci2 (n - 1, ac2, ac1 + ac2);
- }
- Fibonacci2(100) // 573147844013817200000
- Fibonacci2(1000) // 7.0330367711422765e+208
- Fibonacci2(10000) // Infinity
复制代码
4. 数组扩展
(1)扩展运算符(...)
使用扩展运算符可以 能够直接深拷贝一个数组. 修改一个数组内的引用值, 不会改变另一个值
- var arr=[1,2,3,[4,5],6]
- var a=[...arr];
- console.log(a);//[1, 2, 3, Array(2), 6]
复制代码
扩展运算符可以用于数组拼接
- var arr=[1,2,3]
- var arr2=[4,5,6]
- var str='12121'
- var a=[1,...arr,2,...arr2];
复制代码
另外...arr 返回的并不是一个数组, 而是各个数组的值. 只有 [...arr] 才是一个数组, 所以...arr. 可以用来对方法进行传值
- var arr=[1,2,3]
- function f(x,y,z){
- return x+y+z
- }
- console.log(f(...arr)); //6
复制代码
(2)Array.from()
Array.from()可以将某些伪数组转换成真正的数组结果, 什么是伪数组呢, 我们在实际开发中, 有两种常见的伪数组, arguments 和 Dom 中获取的元素集合.
- <body>
- <p></p>
- <p></p>
- <p></p>
- <p></p>
- <p></p>
- </body>
- <script>
- var arr = document.getElementsByTagName('p')
- var arr2 = Array.from(arr)
- console.log(Array.isArray(arr2));//true
复制代码
同样能够将伪数组转换数组的还有两种方法 . 之前提到的扩展运算符
- var arr = document.getElementsByTagName('p')
- var arr2 = [...arr]
- console.log(Array.isArray(arr2));//true
复制代码
. 使用 call,apply 方法.
- var arr = document.getElementsByTagName('p')
- var newarr=Array.prototype.slice.call(arr,0)
- var newarr=Array.prototype.slice.apply(arr,[0])
- console.log(Array.isArray(newarr));//Var 新数组 = 旧数组. slice(起点下标, 终点下标)返回值: 数组, 是旧数组中的一个部分.
- console.log(newarr);
复制代码
(3)Array.of()
也是用于将一组值, 转换为数组. Array.of 并不是用于将转换伪数组的, 它的作用是为了弥补 Array 构造器的不足, 之前我们在想要构建一个长度为一的数组, 且值为 number 类型是无法用 Array 构建的
- var arr=new Array(3)
- console.log(arr);//[empty*3]
复制代码
很显然, 我们想要构建一个[3], 可以使用 Array.of()
- var arr=Array.of(3)
- console.log(arr); //[3]
复制代码
(4)find 和 findIndex 查询第一个符合条件值 / 下标
find: 用于找出第一个符合条件的数组元素. 找不返回 undefined .
findIndex: 返回第一个符合条件的数组元素的索引. 找不到返回 - 1;
- var arr=[1,2,3,4,5]
- var newarr1=arr.find(function(item,index){return item>2})
- var newarr2=arr.findIndex(function(item,index){return item>2})
- console.log(newarr1); //3
- console.log(newarr2); //2
复制代码
基本语法如上: find 和 findindex 内部是一个回调函数, 需要返回一个查询条件, find 则会执行这个返回条件, 查询第一个满足条件的值. findindex 则会返回下标. 我们可以直接用箭头函数进行简写
- var arr=[1,2,3,4,5]
- var newarr1=arr.find(item=>item>2)
- var newarr2=arr.findIndex(item=>item>2)
- console.log(newarr1); //3
- console.log(newarr2); //2
复制代码
(5)fill 填充初始化数组
作用: 给数组填充指定值. fill 方法用于空数组的初始化非常方便. 已有数据会被覆盖. fill 方法还可以接受第二个和第三个参数, 用于指定填充的起始位置和结束位置
- var arr=[1,2,3,4,5]
- arr.fill('*',1,3)
- console.log(arr);//[1, "*", "*", 4, 5]
复制代码
5. 对象扩展
(1). 属性的简写
属性名是可以简写的, 但是有前提条件: 属性的值是一个变量 变量名称和键名是一致的.
- var name ='lang'
- var age=22;
- var obj={
- name:name,
- age:age
- }
复制代码
像这样的对象, 我们就可以进行简写
- var name ='lang'
- var age=22;
- var obj={
- name,
- age,
- }
复制代码
前提是属性名和属性值必须一致.
(2)方法的简写
- var obj={
- say:function(){}
- }
复制代码
简写为
- var obj={
- say(){}
- }
复制代码
(3)精细化设置对象的属性
属性的四个特征:
1.configurable: 是否可以删除. 默认为 true 可以删除:
2.writable: 是否可以修改. 默认为 ture, 可以修改:
3.enumerable:是否可以枚举. 可以使用 for in 默认为 ture, 可以枚举:
4.value: 值. 默认值为 undefined
格式 1:Object.defineProperty(对象名,"属性名",{上面的四个特征});
- var obj={
- name:'lang',
- age:22
- }
- Object.defineProperty('obj','name',{
- configurable:false,
- writable:false,
- enumerable:false
- })
复制代码
格式 2:Object.defineProperties(对象名,{属性名{四个特征}},{属性名{四个特征}}});
- var obj = {
- name: 'lang',
- age: 22
- }
- Object.defineProperties(obj, {
- name: {
- configurable: false,
- writable: false,
- enumerable: false
- },
- age: {
- configurable: false,
- writable: false,
- enumerable: false
- }
- })
复制代码
(4)获取精细化设置对象的属性
Obect.getOwnProertyDescriptor(对象, 属性名) 可以获取对象的精细化属性里面的值.
- var obj={
- name:'lang',
- age:22
- }
- Object.defineProperty(obj,'name',{
- configurable:false,
- writable:false,
- enumerable:false
- })
- console.log(Object.getOwnPropertyDescriptor(obj,'name'));
复制代码
(5)Object.keys()
使用 Object.getOwnPropertyNames()和 Object.keys()都可以得到一个对象的属性名, 属性名是放在一个数组中的.
- var obj={
- name:'lang',
- age:22
- }
- console.log(Object.keys(obj)); //["name", "age"]
复制代码
那么我们目前有三种遍历对象的方法了 对于对象的遍历目前有三种方式:
- for in
- Object.keys()
- Object.getOwnPropertyNames()
复制代码
for in : 会输出自身以及原型链上可枚举的属性.
Object.keys() : 用来获取对象自身可枚举的属性键
Object.getOwnPropertyNames() : 用来获取对象自身的全部属性名
(6)Object.values();
获取对象的值, 放入数组中.
- var obj={
- name:'lang',
- age:22
- }
- console.log(Object.values(obj)); //["lang", 22]
复制代码
6. 解构赋值
解构赋值语法是一个 Javascript 表达式, 这使得可以将值从数组或属性从对象提取到不同的变量中.
对象字面量和数组字面量提供了一种简单的定义一个特定的数据组的方法.
let x = [1, 2, 3, 4, 5];
复制代码
解构赋值使用了相同的语法, 不同的是在表达式左边定义了要从原变量中取出什么变量.
- var x = [1, 2, 3, 4, 5];
- var [y, z] = x;
- console.log(y); // 1
- console.log(z); // 2
复制代码
(1)数组的解构
变量声明并赋值时的解构
- var foo = ["one", "two", "three"];
- var [one, two, three] = foo;
- console.log(one); // "one"
- console.log(two); // "two"
- console.log(three); // "three"
Link to section 变量先声明后赋值时的解构
复制代码
通过解构分离变量的声明, 可以为一个变量赋值.
- var a, b;
- [a, b] = [1, 2];
- console.log(a); // 1
- console.log(b); // 2
Link to section 默认值
复制代码
为了防止从数组中取出一个值为 undefined 的对象, 可以为这个对象设置默认值.
- var a, b;
- [a=5, b=7] = [1];
- console.log(a); // 1
- console.log(b); // 7
Link to section 交换变量
复制代码
在一个解构表达式中可以交换两个变量的值.
没有解构赋值的情况下, 交换两个变量需要一个临时变量(或者用低级语言中的 XOR-swap 技巧).
- var a = 1;
- var b = 3;
- [a, b] = [b, a];
- console.log(a); // 3
- console.log(b); // 1
Link to section 解析一个从函数返回的数组
复制代码
从一个函数返回一个数组是十分常见的情况.. 解构使得处理返回值为数组时更加方便.
在下面例子中,[1, 2] 作为函数的 f() 的输出值, 可以使用解构用一句话完成解析.
- function f() {
- return [1, 2];
- }
- var a, b;
- [a, b] = f();
- console.log(a); // 1
- console.log(b); // 2
复制代码
(2)对象的解构
基本赋值
- var o = {p: 42, q: true};
- var {p, q} = o;
- console.log(p); // 42
- console.log(q); // true
复制代码
给新的变量名赋值
可以从一个对象中提取变量并赋值给和对象属性名不同的新的变量名.
- var o = {p: 42, q: true};
- var {p: foo, q: bar} = o;
- console.log(foo); // 42
- console.log(bar); // true
复制代码
默认值
变量可以先赋予默认值. 当要提取的对象没有对应的属性, 变量就被赋予默认值.
- var {a = 10, b = 5} = {a: 3};
- console.log(a); // 3
- console.log(b); // 5
复制代码
混合解构(嵌套对象和数组) 解构嵌套对象和数组
- var metadata = {
- title: "Scratchpad",
- translations: [
- {
- locale: "de",
- localization_tags: [ ],
- last_edit: "2014-04-14T08:43:37",
- url: "/de/docs/Tools/Scratchpad",
- title: "JavaScript-Umgebung"
- }
- ],
- url: "/en-US/docs/Tools/Scratchpad"
- };
- var { title: englishTitle, translations: [{ title: localeTitle }] } = metadata;
- console.log(englishTitle); // "Scratchpad"
- console.log(localeTitle); // "JavaScript-Umgebung"
复制代码
7.class 类
之前在 es5 中, 我们如何去实现一个类的功能呢? 我们通常采用构造器的方法去实现, 然而, 使用构造器去模仿类的实现并不方便, 不仅需要经常维护 this, 而且在继承的时候更是不仅需要使用 call 拷贝父类的基本数值, 还需要继承父类的原型来继承方法. 我们简单来看看代码就知道了
- function Parent(name) {
- this.name = name;
- }
- Parent.prototype.sayName = function () {
- console.log('parent name:', this.name);
- }
- function Child(name, parentName) {
- Parent.call(this, parentName);
- this.name = name;
- }
- function inheritPrototype(Parent, Child) {
- Child.prototype = Object.create(Parent.prototype); // 修改
- Child.prototype.constructor = Child;
- }
- inheritPrototype(Parent, Child);
- Child.prototype.sayName = function () {
- console.log('child name:', this.name);
- }
- var parent = new Parent('father');
- parent.sayName(); // parent name: father
- var child = new Child('son', 'father');
- child.sayName(); // child name: son
复制代码
在 es6 中, 我们可以直接使用 class 来定义, 如果你有 Java 基础的话, 那么理解起来将非常简单. 基本上, ES6 的 class 可以看作只是一个语法糖, 它的绝大部分功能, ES5 都可以做到, 新的 class 写法只是让对象原型的写法更加清晰, 更像面向对象编程的语法而已.
- // 定义类
- class Point {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- }
- toString() {
- return '(' + this.x + ',' + this.y + ')';
- }
- }
复制代码
我们使用 typeof 去检测 class 的数据类型, 会发现 class 类的本质就是一个方法, 也就是构造器. 我们不仅仅可以使用 new 方法来新建一个类, 我们也可以使用 prototype 来访问类的原型.
(1)constructor
可以看到里面有一个 constructor 方法, 这就是构造方法, 而 this 关键字则代表实例对象. 这里面通常保存着类基本数据类型
(2)定义类方法
我们可以直接在类中添加自己的方法, 前面不需要加上 function 这个关键字, 另外, 为了使类更加的符合大众的写法, 去掉了逗号分隔, 我们不需要在方法之间使用逗号进行分隔.
(3)类的继承
es6 还给类提供了一个 extends 的继承方法. 使用方法与 java 如出一辙.
- class NBAPlayer2 {
- constructor(name, height) {
- this.name = name;
- this.height = height;
- }
- say() {
- console.log(`name is${this.name} height is${this.height}`);
- }
- }
- class MVP2 extends NBAPlayer {
- constructor(name, height, year) {
- super(name, height)
- this.year = year
- }
- say() {
- console.log(`name is${this.name} height is${this.height} mvpyear is${this.year}`);
- }
- }
- var n1 = new MVP2('老库里', '201', '2016')
- var m1 = new NBAPlayer2('老库里', '201')
- n1.say()
- m1.say()
复制代码
注意: 使用 extends 关键字来实现继承在子类中的构造器 constructor 中, 必须要显式调用父类的 super 方法, 如果不调用, 则 this 不可用
我们如果使用原型去查看父子类, 就会发现, 他们其实是通过原型链来进行继承的.
结尾
ES6 的新特性还有很多, 本次只概述了一些较为常用的方法. 适合初识 es6 的人对 es6 进行一个大概的了解.
关于 es6 的更多特性, 将会在后续进行补充. 具体重要内容还有以下
1.Promise 对象
2.Generator
3.async 函数
4.Module 语法
具体深入了解可以参考阮大大的 ECMAScript 6 入门 http://es6.ruanyifeng.com/#docs/set-map
来源: https://juejin.im/post/5b6d6f8ae51d453509566974