JavaScript基礎概念之----面向對象----繼承(深刻)

實現繼承主要是依靠原型鏈來實現的。函數

基本思想是:利用原型 讓一個引用類型繼承另外一個引用類型的屬性和方法。this

原型鏈繼承spa

有兩個構造函數 A 和 B,若是,讓A的原型對象等於B的實例,結果會是怎麼樣呢?prototype

此時 A 的原型對象將包含一個指向 B 的原型的指針,相應地,B 的原型中也可包含着指向 C 的指針。假如 C 的原型又是 D 的實例,那麼上述關係依然成立,如此層層遞進,就構成了實例與原型的鏈條。這就是原型鏈的基本概念。指針

繼承實現的本質,就是重寫原型對象,代之以一個新類型的實例。code

function A(){
    //...
}

A.prototype.getA = function(){
    //...
}

function B(){
    //...
}

B.prototype = new A() //繼承了A

B.prototype.getB = function(){
    //...
}

var o = new B();
console.log(o.getA()) //在B構造函數建立的對象o中,執行A構造函數的方法

調用o.getA()會經歷三搜索步驟:對象

  • 搜索實例o
  • 搜索B.prototype
  • 搜索A.prototype

全部函數的默認原型都是Object的實例,所以默認原型都會包含一個內部指針,指向Object.prototype。blog

instanceof 操做符 確認實例 是否 原型鏈中出現過的構造函數的實例繼承

o instanceof Object //true
o instanceof A //true
o instanceof B //true

isPrototypeOf()方法ip

Object.prototype.isPrototypeOf(o) //true
A.prototype.isPrototypeOf(o) //true
B.prototype.isPrototypeOf(o) //true

給原型添加方法的代碼,必定要放在替換原型的語句以後。

function A(){}

A.prototype.getA = function(){}

function B(){}

B.prototype = new A() //繼承了A

//新增的方法
B.prototype.getB = function(){}

//重寫超類中的方法
B.prototype.getA = function(){}

var o = new B();
console.log(o.getA())
//重寫的方法會屏蔽超類中的方法

經過原型鏈實現繼承時,不能使用對象字面量建立原型方法。由於這樣會重寫原型鏈。

function A(){}

A.prototype.getA = function(){}

function B(){}

B.prototype = new A() //繼承了A

B.prototype = {
  getB:function(){},
  getOther:function(){}
}


var o = new B();
console.log(o.getA())  //報錯

缺點:

  • 原型的共享本性的緣由,經過原型實現繼承時,原型實際上會變成另外一個類型的實例。原先的實例屬性也就變成了如今的原型屬性了。
  • 在建立子類型的實例時,不能向超類型的構造函數中傳遞參數

構造函數繼承

基本思想是:在子類型構造函數的內部調用超類型構造函數。

function A(){
    this.arr = [1,2,3]
}

function B(){
    A.call(this) //繼承了A
}

var o1 = new B();
o1.arr.push(3);
console.log(o1.arr) //1,2,3,4

var o2 = new B()
console.log(o2.arr) //1,2,3

能夠在子類型構造函數中向超類型構造函數傳遞參數

function A(name){
    this.name = name;
}

function B(){
    A.call(this,'adhehe') //繼承了A,同時傳遞參數
    
    this.age = 23;
}

var o = new B();
console.log(o.name) //adhehe
console.log(o.age) //23

缺點:

  • 方法都在構造函數中定義,函數複用無從談起
  • 在超類型原型中定義的方法,對子類型是不可見的

組合繼承(原型鏈繼承 與 構造函數繼承 組合)

基本思想是:使用原型鏈實現對原型屬性和方法的繼承,使用構造函數實現對實例屬性的繼承。

組合繼承避免了原型鏈和構造函數的缺陷,融合了它們的優勢,是JavaScript中最經常使用的繼承模式。而且,instanceof和isPrototypeOf()也可以用於識別基於組合繼承建立的對象。

function A(name){
    this.name = name;
    this.arr = [1,2,3];
}

A.prototype.getName = function(){}

function B(name,age){
    A.call(this,name) //繼承屬性
    
    this.age = age;
}

//繼承方法
B.prototype = new A();
B.ptototype.constructor = B;
B.ptototype.getAge = function(){}

var o1 = new B('adhehe',23);
o1.arr.push(4)
o1.arr //1,2,3,4
o1.getName() //adhehe
o1.getAge() //23

var o2 = new B('Jhone',45);
o2.arr //1,2,3
o2.getName() //Jhone
o2.getAge() //45

缺點:不管在什麼狀況下,都會調用兩次超類型構造函數。一次是建立子類型原型的時候,一次是在子類型構造函數內部。

原型式繼承

藉助原型能夠基於已有的對象建立新對象,同時還沒必要所以建立自定義類型。

這種原型式繼承,要求必須有一個對象能夠做爲另外一個對象的基礎。

function Object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
var person = {
    name:'adhehe',
    colors:['red','blue','yellow']
}

var o1 = object(person);
o1.name  = 'Greg';
o1.colors.push('pink');

var o2 = object(person);
o2.name = 'Linda';
o2.colors.push('gray');

console.log(person.colors) //'red','blue','yellow','pink','gray'

ECMAScript5新增Object.create()方法規範化了原型式繼承

var person = {
    name:'adhehe',
    colors:['red','blue','yellow']
}

var o1 = Object.create(person)
o1.name  = 'Greg';
o1.colors.push('pink');

var o2 = Object.create(person);
o2.name = 'Linda';
o2.colors.push('gray');

console.log(person.colors) //'red','blue','yellow','pink','gray'

Object.create()方法的第二個參數 與 Object.defineProperties()方法的第二個參數格式相同:每一個屬性都是經過本身的描述符定義的。以這種方式指定的任何屬性都會覆蓋原型對象上的同名屬性。、

var person = {
    name:'adhehe',
    colors:['red','blue','yellow']
}

var o = Object.create(person,{
    name:{
        value:'Greg'
    }
})

console.log(o.name) //Greg

寄生式繼承

與寄生構造函數和工廠模式相似,即建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來加強對象,最後再返回對象。

function object(o){
    function F(){}
    F.prototype = o;
    return new F():
}

function createPreson(arg){
    var clone = object(arg);
    clone.sayHi = function(){}
    return clone;
}

var preson = {
    name:'adhehe',
    colors:['red','blue','yellow']
}

var o = createPerson(person);
o.sayHi(); 

任何可以返回新對象的函數都適用於此模式。

寄生組合式繼承

經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法。

基本思想是:沒必要爲了指定子類型的原型而調用超類型的構造函數,咱們須要的無非是超類型原型的一個副本而已。

function person(sub,sup){
    var ptototype = object(sup.prototype); //建立對象
    prototype.constructor = sub; //加強對象
    sub.prototype = prototype;   //指定對象
}
//第一步,建立超類型原型的一個副本
//第二步,爲建立的副本添加constructor屬性,彌補因重寫原型而失去的默認的constructor屬性
//最後一步,將新建立的對象賦值給子類型的原型
function Sup(name){
    this.name = name;
    this.colors = ['red','blue','yellow']
}

Sup.prototype.sayName = function(){}

function Sub(name,age){
    Sup.call(this,name)
    this.age = age;
}

person(sub,sup);

sub.prototype.sayAge = function(){}
相關文章
相關標籤/搜索