javascript繼承

javascript繼承

標籤(空格分隔):javascript javascript基礎java


原型鏈

var Super = function(){
    this.name="Super";
    this.arr = ['a','b','c'];
}
Super.prototype.getName = function(){
    alert(this.name);
}

var Sub = function(){
    this.age=12;
}
Sub.prototype = new Super();    //看在這裏的時候我常在想爲何不直接Sub.prototype=Super.prototype,要new一個出來,解釋【2】

var sub1 = new Sub();
var sub2 = new Sub();

sub1.arr.push('d');
console.log(sub1.name);
console.log(sub2.name);
sub1.name = 'kealin';   //這裏不是改的Super裏的name,看下面的解釋【1】
console.log(sub1.name,sub2.name);  //kealin Super
console.log(sub1.arr)   //[a,b,c,d]
console.log(sub2.arr);  //[a,b,c,d]

解釋__[1]__

sub1的結構
        Sub
            name:kealin
            age:12
            proto:Object
                name:Super
                arr:Array[3]
                proto:Super
                    constructor:Super
                    getName:function
    sub2的結構
        Sub
            age:12
            proto:Object
                name:Super
                ......
    //不是引用類型的,都直接在對象上聲明

解釋__[2]__

var Super = function(){
    this.name="Super";
    this.arr = ['a','b','c'];
}
Super.prototype.getName = function(){
    alert(this.name);
}

var Sub = function(){
    this.age=12;
}
Sub.prototype = Super.prototype;
//不要忘記了原型對象也是引用類型,後面改到的Sub.prototype也會反應到Super.prototype,
//他們倆的原型對象是一個對象,這裏就根本不是繼承了
Sub.prototype.getName = function(){ 
    console.log('haha');
}
Sub.prototype.getAge = function(){
    alert(1);
    return this.age;
}

var sub1 = new Sub();
var super1 = new Super();
super1.getName();   //haha   訪問父類的getName是訪問不到的,沒有繼承的概念在裏面了。
super1.getAge();    
sub1 instanceof Super //true

缺點

  1. 引用類型的問題,在父類裏聲明的引用類型將都是共享的
  2. 不能在不影響所在實例的狀況下(一改就都改了)向父類的構造函數裏傳遞參數,多個實例確定有屬性是不一樣的。

借用構造函數

var Super = function(name){
    this.name=name
    this.arr = ['a','b','c'];
    this.getName = function(){
        return this.name
    }
}

var Sub = function(name){
    //構造函數也是方法,因此能夠用call和apply來改變父類方法的執行環境,繼承(我以爲這裏不是繼承)父類裏的屬性
    //並且傳遞了參數
    Super.call(this,name);
    this.age = 12;
}

var sub1 = new Sub('kelly');
var sub2 = new Sub('haha');
console.log(sub1.name);                     //kelly
console.log(sub2.name);                     //haha
sub1.arr.push('d');
console.log(sub2.arr);                      //[a,b,c]
console.log(sub1.getName==sub2.getName);    //false

缺點

跟構造函數模式同樣,公用的引用類型不能共用,sub1的數組改了,sub2沒有改,並且getName方法也不同數組


組合繼承

function Super(name){
    this.name = name;
    this.arr = [1,2,3,4];
}
Super.prototype.getName = function(){
    return this.name;
}

function Sub = function(name,age){
    Super.call(this,name);        //第二次調用父類構造函數
    this.age = age;
}
Sub.prototype = new Super();     //第一次調用父類構造函數
Sub.prototype.getAge = function(){
    return this.age;
}

var sub1 = new Sub();
var Super1 = new Super();
console.log(sub1.constructor.name)  //''

優勢

避免了原型鏈裏引用類型公用的問題和借用構造函數形成的方法不能公用的問題,並且能夠經過構造函數來向父類中傳遞參數。
因此通常是經過構造函數來繼承屬性,經過原型鏈的繼承方法app

缺點

調有兩次構造函數,致使Sub在prototype建立了多餘的屬性[name,arr]
第一次調用Super的構造函數是設置Sub.prototype,這個時候Sub的原型上己經有name和arr兩個屬性了
第二次,實例Sub對象,調用Sub構造函數,在構造函數裏調用的Super的構造函數。這個時候實例對象也有了name,arr。
經過sub1.name時候實際上是覆蓋了原型裏的name,因此sub1.constructor.name的值爲''函數

一個傻傻的問題

給Sub.prototype賦值時,爲何不直接Sub.prototype==Super.prototype,這樣也不會有原型上產生兩個多餘的屬性了,而後我作了試驗
後面的寄生組合繼承實際上是Sub.prototype = Super.prototype模式,只是類似this

var Super = function(name){
    this.name=name
    this.arr = ['a','b','c'];
}
Super.prototype.getName = function(){
    return this.name;
}

var Sub = function(name){
    Super.call(this,name);
    this.age = 12;
}

Sub.prototype = Super.prototype;    //賦值後Sub與Super的原型都是同樣了
Sub.prototype.constructor = Sub;    //這個時候操做的也是Super的原型,這樣就不是繼承了,徹底是Super的原型都改了
Sub.prototype.getAge = function(){  //傻傻我糾結了這麼久
    return this.age;
}

Sub.prototype.getName = function(){
    console.log('haha');
}

var sub1 = new Sub('kelly');
var super1 = new Super('haha');

console.log(sub1.constructor === super1.constructor);   //true,
console.log(super1.constructor)                         //Sub
console.log(super1 instanceof Super);                   //true
console.log(super1 instanceof Sub);                     //true
console.log(sub1 instanceof Super);                     //true  
// a instanceof b a是不是b的實例  a原型鏈上其中一個__proto__===b.prototype
// Function instanceof Function  //true
// Number instanceof Function   //true
// Number instanceof Number     //false,Number實際上是個方法,都繼承自Function
//      Number.__proto__ = Function.prototype!=Number.prototype,
//      Function.prototype.__proto__=Object.prototype!=Number.prototype,
//      Object.prototype.__proto__=null!=Number.prototype.
//      因此爲false
console.log(sub1.__proto__===Super.prototype)           //true
console.log(super1.getAge);
super1.getName();

原型式繼承

function object(o){
    var F = function(){};   //一個空的構造函數名得裏面包含多餘的屬性
    F.prototype = o;        //須要提供一個原型對象
    return new F();
}


var person = {
    name:'kaleys',
    friends:['a','b']
}
var person1 = object(person);
person1.name = 'kelly';
person1.friends.push('d');

var person2 = object(person);
console.log(person2.name);
console.log(person2.friends);   //a,b,d

//Object.create是ECMAScript5提出來的,IE9+
var person3 = Object.create(person);    //Object.create與object方法同樣的效果
console.log(person3);

用途

讓一個對象與另外一個對象保先相似的狀況prototype

缺點

引用類型會共享值code

寄生式繼承

寄生組合式繼承

var Super = function(name){
    this.name = name;
    this.getName();
}
Super.prototype.getName = function(){
    return this.name;
}

var Sub = function(name,age){
    Super.call(this,name);  //只在這裏調用一Super的構造函數
    this.age = age;
}

//寄生開始,所謂寄生就是在原有的對象上進行加強,好比說增長一個方法,改變一個屬性行嗎
function myInstance(o){
    var F = function(){}
    F.prototype = o;
    return new F();
}
Sub.prototype = myInstance(Super.prototype);
Sub.prototype.constructor = Sub;


//寄生結束


Sub.prototype.getAge = function(){
    return this.age;
}

var sub1 = new Sub('kelly','12');

sub1的結構

相關文章
相關標籤/搜索