對(duì)象繼承
1. 理解原型鏈
所有引用類型(函數(shù)、對(duì)象、數(shù)組),都存在對(duì)象特性,即可以自由拓展屬性。(除了null以外)
所有的引用類型(函數(shù)、對(duì)象、數(shù)組),都有一個(gè)
__proto__(我們這里稱他為隱形原型)屬性,屬性值是一個(gè)普通的對(duì)象。所有函數(shù)都有一個(gè)prototype屬性,屬性值也是一個(gè)普通的函數(shù)
所有的引用類型(函數(shù)、對(duì)象、數(shù)組),*proto屬性值指向它的構(gòu)造函數(shù)的 prototype(顯性屬性)屬性值。
當(dāng)試圖得到一個(gè)對(duì)象的某個(gè)屬性時(shí),如果這個(gè)對(duì)象本身沒有這個(gè)屬性,那么會(huì)去它的_proto_*(即他的構(gòu)造函數(shù)的prototype)中尋找。如果沒有,則會(huì)接著往上找,一直上溯到Object.prototype,也就是說所有對(duì)象都繼承Object.prototype的屬性,Object.prototype的原型是null,null沒有任何屬性和方法。
構(gòu)造函數(shù)、原型和實(shí)例的關(guān)系
- 每個(gè)構(gòu)造函數(shù)(函數(shù))都有一個(gè)原型對(duì)象
prototype - 原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針
construtor - 而實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針
__proto__ - 實(shí)例的
__proto__指向構(gòu)造函數(shù)的prototype
把一個(gè)對(duì)象的__proto__指向另一個(gè)原型對(duì)象,而這個(gè)原型對(duì)象的__proto__又會(huì)指向另一個(gè)原型對(duì)象,這些就會(huì)形成原型鏈
特殊的Function
- 函數(shù)都是由Function構(gòu)造出來的,F(xiàn)unction作為函數(shù),是由其自身構(gòu)建出來,故Function的原型指針指向其自身的原型對(duì)象。
console.log(Function.__proto__ === Function.prototype); // true
console.log(Function.prototype.constructor === Function);
function Person (name,age){
this.name = name ;
this.age= age;
this.class = ['en','math'];
this.sayName = function(){
alert(this.name);
}
}
Person.prototype.like = 'fruit';
繼承 我們需要繼承什么?
繼承的最終目的 :用最少的代碼 可以實(shí)現(xiàn)繼承公有屬性和方法的同時(shí),擁有自己的屬性和方法
2.原型鏈繼承
原理 讓新實(shí)例的原型等于父類的實(shí)例
優(yōu)點(diǎn) 實(shí)例可繼承的屬性有:實(shí)例的構(gòu)造函數(shù)的屬性,父類構(gòu)造函數(shù)屬性,父類原型的屬性。(新實(shí)例不會(huì)繼承父類實(shí)例的屬性?。?/p>
缺點(diǎn)
1.新實(shí)例無法向父類構(gòu)造函數(shù)傳參。
2.繼承單一。只能繼承一個(gè)父類
3.所有新實(shí)例都會(huì)共享父類原型的屬性。(原型上的屬性是共享的,一個(gè)實(shí)例修改了原型屬性,另一個(gè)實(shí)例的原型屬性也會(huì)被修改!)
function Per (name) {
this.name = name;
}
Per.prototype = new Person();
var per = new Per("la");
var per2 = new Per("la");
per2.class.push('ss') //
per.class //
per.__proto__ --> (Per.prototype = new Person)
Per.prototype.__proto__-->Person.prototype
Person.prototype.__proto__ -->Object.prototype
Object.prototype.__proto__ --> null

如圖 藍(lán)色鏈為 原型鏈 紅色為構(gòu)造函數(shù)和原型的關(guān)系
3.借用構(gòu)造函數(shù)繼承 偽造對(duì)象 或經(jīng)典繼承
原理 用.call()和.apply()將父類構(gòu)造函數(shù)引入子類函數(shù)(在子類函數(shù)中做了父類函數(shù)的自執(zhí)行(復(fù)制))
優(yōu)點(diǎn)傳遞參數(shù)
1、只繼承了父類構(gòu)造函數(shù)的屬性,沒有繼承父類原型的屬性。
2、解決了原型鏈繼承缺點(diǎn)1、2、3。
3、可以繼承多個(gè)構(gòu)造函數(shù)屬性(call多個(gè))。
4、在子實(shí)例中可向父實(shí)例傳參。
缺點(diǎn)
1、只能繼承父類構(gòu)造函數(shù)的屬性。
2、無法實(shí)現(xiàn)構(gòu)造函數(shù)的復(fù)用。(每次用每次都要重新調(diào)用)
3、每個(gè)新實(shí)例都有父類構(gòu)造函數(shù)的副本,臃腫。(構(gòu)造函數(shù)缺點(diǎn) 所有屬性都綁定在對(duì)應(yīng)的對(duì)象上)
function Con(name){
Person.call(this,"jer",10)// 調(diào)用了 父構(gòu)造函數(shù) 可以傳參 提高自由度
// Person2.call(this,"") 多個(gè)構(gòu)造函數(shù) 多繼承
this.name = name;
}
var con1 = new Con('rr');
console.log(con1 instanceoof Person)
4.組合繼承(組合原型鏈繼承和借用構(gòu)造函數(shù)繼承)(常用)
原理 結(jié)合了兩種模式的優(yōu)點(diǎn),傳參和復(fù)用
優(yōu)點(diǎn)
1、可以繼承父類原型上的屬性,可以傳參,可復(fù)用。
2、每個(gè)新實(shí)例引入的構(gòu)造函數(shù)屬性是私有的。
缺點(diǎn) 調(diào)用了兩次父類構(gòu)造函數(shù)(耗內(nèi)存),
// 子類的構(gòu)造函數(shù)會(huì)代替原型上的那個(gè)父類構(gòu)造函數(shù)(沒有理解)。
function C(name){
Person.call(this,name);//構(gòu)造函數(shù)繼承屬性
}
C.prototype = new Person();//原型繼承方法 new的時(shí)候調(diào)用第二次
var per1=new C("aa");
per1.name; // 構(gòu)造函數(shù)屬性
per1.age; // 原型屬性
5.原型式繼承
原理 先創(chuàng)建了一個(gè)臨時(shí)性的構(gòu)造函數(shù),然后將傳入的對(duì)象作為個(gè)構(gòu)造函數(shù)的原型,最后返回這個(gè)構(gòu)造函數(shù)的實(shí)例 ,這個(gè)函數(shù)就變成了個(gè)可以隨意增添屬性的實(shí)例或?qū)ο?。object.create()就是這個(gè)原理 。
優(yōu)點(diǎn)類似于復(fù)制一個(gè)對(duì)象,用函數(shù)來包裝。
缺點(diǎn)
1、所有實(shí)例都會(huì)繼承原型上的屬性(共享問題)。
2、無法實(shí)現(xiàn)復(fù)用。(新實(shí)例屬性都是后面添加的) (不能傳參)
function D(obj){
function F(){}
F.prototype = obj;
return new F();
}
var per4 = new Person()
var per5 = D(per4);
console.log(per5.name)
per5 instanceof Person //
//
new 默認(rèn)原型對(duì)象 create 指定原型對(duì)象
Object.create()是Object的內(nèi)置方法,可以創(chuàng)建一個(gè)新對(duì)象,使用現(xiàn)有的對(duì)象來提供新創(chuàng)建的對(duì)象__proto__
Object.create ( proto, [ propertiesObject ] )
方法內(nèi)部定義一個(gè)新的空對(duì)象obj
將obj.__proto__的對(duì)象指向傳入的參數(shù)proto
將傳入的對(duì)象屬性復(fù)制到obj并且返回obj
6.寄生式繼承
原理 就是給原型式繼承外面套了個(gè)殼子。
優(yōu)點(diǎn) 傳參
缺點(diǎn) 沒用到原型,無法復(fù)用
function D(obj){
function F(){}
F.prototype = obj;
return new F();
}
var per4 = new Person();
function E(obj,name){
var sub = D(obj);// 繼承原型
sub.name = name; //在原來的基礎(chǔ)上加上私有的東西
return sub;
}
var per6 = E(per4,'ee');
// 給原型式繼承 加個(gè)處理函數(shù)傳參
7.寄生組合式繼承(最理想)
原理
寄生:在函數(shù)內(nèi)返回對(duì)象然后調(diào)用
組合:1、函數(shù)的原型等于另一個(gè)實(shí)例。
? 2、在函數(shù)中用apply或者call引入另一個(gè)構(gòu)造函數(shù),可傳參
優(yōu)點(diǎn) 修復(fù)了組合繼承的問題
缺點(diǎn) 過于繁瑣,故不如組合繼承
// 寄生
function G(obj){
function F(){};
F.prototype = obj;
return new F();
}
// G 是 F實(shí)例的另一種表示
var g1 = G(Person.prototype);
//組合
function Sub(){
Person.call(this);
}
//重點(diǎn)
Sub.prototype = g1; //繼承 實(shí)例
g1.constructor = Sub; // 修復(fù) 實(shí)例
var sub1 = new Sub();
//sub1 就繼承了繼承 函數(shù)屬性,父類實(shí)例,g1 的函數(shù)屬性
sub1.age;
8.ES6繼承 (class)(extends)
原理Class之間通過使用extends關(guān)鍵字,這比通過修改原型鏈實(shí)現(xiàn)繼承,要方便清晰很多
新的class寫法只是讓對(duì)象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法而已。
相當(dāng)于構(gòu)造函數(shù)的另一種寫法
class Point{
constructor(x,y){
this.x = x;
this.y = y;
}
toString(){
console.log(this.x);
}
}
class Colorpoint extends Point {
//這個(gè)就是默認(rèn)方法 使用new 生成實(shí)例時(shí) 會(huì)調(diào)用這個(gè)方法,
//如果未定義 會(huì)自動(dòng)添加
constructor(x,y,color){
//子類必須在constructor方法中調(diào)用super方法,否則新建實(shí)例時(shí)會(huì)報(bào)錯(cuò)
//這是因?yàn)樽宇悰]有自己的this對(duì)象,而是繼承父類的this對(duì)象,然后對(duì)其進(jìn)行加工,如果不調(diào)用super方法,子類就得不到this對(duì)象。
super(x,y); //調(diào)用父類構(gòu)造函數(shù)(Point.prototype.constructor.call(this,x,y))
this.color = color
// 隱式返回 this
// 如果顯示返回對(duì)象 就是該對(duì)象
}
toString(){
//通過 super調(diào)用父類的方法
return this.color + ' ' + super.toString();
}
}
class A extends B{}
A.__proto__ === B; //繼承屬性
A.prototype.__proto__ == B.prototype;//繼承方法
typeOf(Colorpoint)
//類的數(shù)據(jù)類型就是函數(shù),類本身就指向構(gòu)造函數(shù)。
//類的所有方法都定義在類的prototype屬性上面。
Colorpoint.prototype.constructor === Colorpoint // true
Object.assign(Colorpoint.prototype, {
toString(){},
toValue(){}
});
與 es5 不同之處
1. toString方法是Colorpoint類內(nèi)部定義的方法,它是不可枚舉的。這一點(diǎn)與 ES5 的行為不一致。
2. 必須使用new 調(diào)用
3.不存在變量提升,必須先聲明在使用
與es5 相同之處
1.prototype對(duì)象的constructor屬性,直接指向“類”的本身
2. 與 ES5 一樣,實(shí)例的屬性除非顯式定義在其本身(即定義在this對(duì)象上),否則都是定義在原型上(即定義在class上)。
3.類的所有實(shí)例共享一個(gè)原型對(duì)象。
Colorpoint.hasOwnProperty('x') // true
Colorpoint.hasOwnProperty('color') // true
Colorpoint.hasOwnProperty('toString') // false
“extends” 語法會(huì)設(shè)置兩個(gè)原型:
- 在構(gòu)造函數(shù)的
"prototype"之間設(shè)置原型(為了獲取實(shí)例方法) - 在構(gòu)造函數(shù)之間會(huì)設(shè)置原型(為了獲取靜態(tài)方法)
//繼承對(duì)象
class A extends Object 和 class A 區(qū)別
繼承自對(duì)象 繼承自函數(shù)
構(gòu)造函數(shù)中需要調(diào)用父類構(gòu)造函數(shù)
super
作為函數(shù)使用
調(diào)用父類構(gòu)造函數(shù)作為對(duì)象使用
靜態(tài)時(shí)指向父類本身可以調(diào)用父類本身的屬性和方法·指向父類的原型
//動(dòng)態(tài)時(shí)由于super指向父類的原型對(duì)象,所以定義在父類實(shí)例上的方法或?qū)傩?,是無法通過super調(diào)用的。
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();// 調(diào)用父類函數(shù)時(shí) 會(huì)綁定子類的this
console.log(super.p()); // 2
}
}
let b = new B();
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
b.m // undefined
// 用在靜態(tài)方法之中,這時(shí)super將指向父類,而不是父類的原型對(duì)象。
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
//在子類的靜態(tài)方法中通過super調(diào)用父類的方法時(shí),方法內(nèi)部的this指向當(dāng)前的子類,而不是子類的實(shí)例。
class A {
constructor() {
this.x = 1;
}
static print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
static m() {
super.print();
}
}
B.x = 3;
B.m() // 3
- 所以如果通過super對(duì)某個(gè)屬性賦值,這時(shí)super就是this,賦值的屬性會(huì)變成子類實(shí)例的屬性。
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined A.prototype.x
console.log(this.x); // 3
}
}
let b = new B();
- 由于對(duì)象總是繼承其他對(duì)象的,所以可以在任意一個(gè)對(duì)象中,使用super關(guān)鍵字。
- 直接打印 super會(huì)報(bào)錯(cuò) 由于瀏覽器不知道是函數(shù)還是對(duì)象, 所以必須顯式指定是作為函數(shù)、還是作為對(duì)象使用
-
箭頭函數(shù)沒有
super -
[[HomeObject]]super 的特殊特征
學(xué)習(xí)整理
JS高程學(xué)習(xí)-第六章(一)---認(rèn)識(shí)對(duì)象
JS高程學(xué)習(xí)-第六章(二)---創(chuàng)建對(duì)象
JS高程學(xué)習(xí)-第六章(三)---對(duì)象繼承