嗯,class 的基本用法已经写过了,如果对 class 类的基本语法不太了解的话,可以参考一下ES6 Class 的基本语法
简介
Class 可以通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
![18fb7441cc6f25628fae7776cc53fa1b.png](https://i-blog.csdnimg.cn/blog_migrate/e08c4933e1d4637ab0ca07cb120522aa.jpeg)
上面代码定义了一个 ColorPoint 类,该类通过 extends关键字,继承了 Point 类的所有属性和方法。
基础用法
![1448ba54af758344b242e3814bc65091.png](https://i-blog.csdnimg.cn/blog_migrate/cf920b0a6eb9dbf37c2cadaa7dc80cd2.jpeg)
上面代码中,子类的 constructor 方法中出现了 super 关键字,它在这里表示父类的构造函数,用来新建父类的 this 对象。
子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用 super 方法,子类就得不到 this 对象。
eg:
![7c56babd7ba5c4c961c2b78ee3701bb1.png](https://i-blog.csdnimg.cn/blog_migrate/b5011299d8049875208c01d87e293997.jpeg)
上面代码中,Child 继承了父类Father,但是它的构造函数没有调用 super 方法,导致新建实例时报错。
对于子类和父类方法重复的情况,在子类中调用父类与子类的同名方法同样通过super 关键字:
子类和父类方法不同名时,直接this调用:
![27773d5d0cd640770c1cbb493adcd17c.png](https://i-blog.csdnimg.cn/blog_migrate/565f1d3eb08f0925a0fddd345bdff103.jpeg)
子类与父类方法名重叠时:
![b550556258ce844f911a8c58f18945c3.png](https://i-blog.csdnimg.cn/blog_migrate/5847b1c5d103cc2ea17f3f0fdcdaee35.jpeg)
其实是它调用自身,形成无限递归(因为子类和父类重名时,使用this优先调用子类自身的方法)! 这种情况明显是不行的,所以要解决这种情况,通过super 关键字区分即可,调用子类方法用this , 调用父类方法用super 。
![0f36ac5c4c541bbaefefa1dda717e926.png](https://i-blog.csdnimg.cn/blog_migrate/bfdefdc0141aa3b92f2bc78d9b5df590.jpeg)
虽然说如果父类的方法子类没有的话,可以通过this.方法名调用,但是我个人的话还是建议调用子类自身的方法通过this.方法名,调用父类方法通过super.方法名。这样可以便于区分,同时也不用但是父类和子类方法名重名问题。
继承同ES5区别
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
当子类没有定义constructor时
如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。
![2d67e96da497e34593b4426e8a6283b8.png](https://i-blog.csdnimg.cn/blog_migrate/8772220b00575c2306c13fab4db70375.jpeg)
等同于:
![d9811ab9ecc41e798c28faa280cdb89e.png](https://i-blog.csdnimg.cn/blog_migrate/a77d59a5f695b05683805d4b03d4f58a.jpeg)
子类的constructor只有先调用super,才能使用this关键字
子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。
![578b66baecaa2d5fb38094d2c3547c5b.png](https://i-blog.csdnimg.cn/blog_migrate/69a3577eb493cfca3621847e8c6f868d.jpeg)
正确示范:
![0335fffb2ac5ab8fce5fbdb513f07b99.png](https://i-blog.csdnimg.cn/blog_migrate/1d2c5223f3a2b759420cea0c0fd58ba0.jpeg)
super 关键字
super 这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
![de1361927b066c0face553e8f8de3ca0.png](https://i-blog.csdnimg.cn/blog_migrate/51c87ac3621c6cc87224f1addfa81f6a.jpeg)
上面代码中,子类B的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。
注意,super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)。
即类似于es6之前的构造函数中的这种写法:
![5db2f34fd130de3e14ac11641e72ab04.png](https://i-blog.csdnimg.cn/blog_migrate/1a9f74cbf2b073b884c58ae044f11b9e.jpeg)
eg:
![cc6c6754761f72c05426d38daf14542f.png](https://i-blog.csdnimg.cn/blog_migrate/e0bcad6ce9abbc4a5db4f5dd08d12abc.jpeg)
上面代码中,new.target指向当前正在执行的函数。可以看到,在super()执行时,它指向的是子类Child的构造函数,而不是父类Father的构造函数。也就是说,super()内部的this指向的是Child。
作为函数使用时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
![33091b3b2d5c1114ef0fa3c7db7d940c.png](https://i-blog.csdnimg.cn/blog_migrate/1d0b2c883ccd6b63df7a8ed0b158c597.jpeg)
上面代码中,console.log(super)当中的super,无法看出是作为函数使用,还是作为对象使用,所以 JavaScript 引擎解析代码的时候就会报错。这时,如果能清晰地表明super的数据类型,就不会报错。
eg:
![2d6292dd406c64dcf8ae59ad8647bdec.png](https://i-blog.csdnimg.cn/blog_migrate/e5e2f0e668ee6b080262529d36e0234b.jpeg)
默认情况下, valueOf() 会被每个对象Object继承。每一个内置对象都会覆盖这个方法为了返回一个合理的值,如果对象没有原始值,valueOf() 就会返回对象自身。
类的 prototype 属性和__proto__属性
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype 属性。Class 作为构造函数的语法糖,同时有 prototype 属性和 __proto__ 属性,因此同时存在两条继承链。
子类的__proto__属性,表示构造函数的继承,总是指向父类。
![2d5b2b0ccbecd2dbd0509b51aec61fa8.png](https://i-blog.csdnimg.cn/blog_migrate/7cd77b8fb63597e3a7c8901749489ac3.jpeg)
子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
![e2cc795d2b66d1830a9612d717cccf3b.png](https://i-blog.csdnimg.cn/blog_migrate/1220681c7ca9bfd2174779558043548c.jpeg)
实例的 __proto__ 属性
子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。
![03a4749ea86ad119d0a87e2bf326a446.png](https://i-blog.csdnimg.cn/blog_migrate/a06687b7f4a49761c4aa1cbe15f3cc7d.jpeg)
因此,通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为。
eg:
![c45b74e8a93f12a3e909b36f0236b4b0.png](https://i-blog.csdnimg.cn/blog_migrate/58d67f8ed9e89a5746a8a90639acd69a.jpeg)
后面还有 原生构造函数的继承 ,Mixin 模式的实现 两节 ,但是我理解不够透彻,写出来容易误导大家,所以希望大家看阮一峰大佬的:http://es6.ruanyifeng.com/#docs/class-extends