天天看點

常用的六種繼承

原型繼承

原型繼承是JS中最常用的一種繼承方式,

子類B想要繼承父類A中的所有的屬性和方法(私有+共有),隻需要讓B.prototype=new A;即可

特點:把父類中私有的+共有的都繼承到了子類的原型上(子類共有的)

例如:

function A() {
    this.x = ;
}
A.prototype.getX = function () {
    console.log(this.x);
};
function B() {
    this.x = ;
}
B.prototype = new A;
           

示意圖:

常用的六種繼承

核心:原型繼承并不是把父類中的屬性和方法克隆一份一模一樣的給B,而是讓B和A之間增加了原型鍊的連接配接,以後B的執行個體n想要A中的getX方法,需要一級一級的向上查找來使用

如果繼續執行這句代碼:n.x = 1000;

修改的是n的私有屬性,與其他無影響

如果繼續執行:

n.__proto__.x

= 2000;

修改A的執行個體上的屬性,對B的執行個體、n都有影響,但是對A及執行個體沒有影響

如果繼續執行:

n.__proto__.__proto__.getX

= 2000;

修改A的原型上的方法,對所有都有影響,該過程叫方法的重寫

#div1.__proto__

->HTMLDivElement.prototype->HTMLElement.prototype->Element.prototype->Node.prototype->EventTarget.prototype->Object.prototype

模拟上述過程;

function myObject() {}
myObject.prototype = {
    constructor: myObject,
    hasOwnProperty: function () {}
};
function myEventTarget() {}
myEventTarget.prototype = new myObject;
myEventTarget.prototype.addEventListener = function () {};
function myNode() {}
myNode.prototype = new myEventTarget;
myNode.prototype.createElement= function () {};

var n = new myNode;
console.dir(n);
           

n.constructor = A;

故需增加:

B.prototype.constructor = B;

call繼承

call繼承的最終結果是把父類私有的屬性和方法克隆一份一模一樣的作為子類私有的屬性,與父類沒有關系。

function A() {
    this.x = ;
}
A.prototype.getX = function () {
    console.log(this.x);
};
function B() {
    //this是n
    A.call(this); //把A執行,讓A中的this變成n
}
var n = new B;
console.log(n.x); //100
           

冒充對象繼承

把父類私有的加公有的克隆一份一模一樣的給子類私有的,與父類無關

function A() {
    this.x = ;
}
A.prototype.getX = function () {
    console.log(this.x);
};
function B() {
    var temp = new A;
    for (var key in temp) {
        //console.log(key); //x getX
        this[key] = temp[key];
    }
    temp = null;
}
var n = new B;
console.log(n.x); //100
           

混合模式繼承

原型繼承+call繼承

私有的變成私有的,私有的加公有的變成公有的

私有的重複了

function A() {
    this.x = ;
}
A.prototype.getX = function () {
    console.log(this.x);
};
function B() {
    A.call(this); //相當于n.x=100
}
B.prototype = new A; //相當于在原型上既有x=100,也有getX
B.prototype.constructor = B;

var n = new B;
console.log(n.x); //100
           

寄生組合式繼承(create考慮相容)

私有的隻拿私有的(call)

公有的隻拿公有的(create)

function A() {
    this.x = ;
}
A.prototype.getX = function () {
    console.log(this.x);
};
function B() {
    A.call(this);
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

var n = new B;
console.log(n.x); //100
           

中間類繼承(

__proto__

考慮相容)

function avgFn() {
    arguments.__proto__ = Array.prototype;
}
avgFn(,,,);
           

讓arguments使用數組中的方法

1.每一個函數資料類型(普通函數類)都有一個天生自帶的屬性:prototype(原型),并且這個屬性是一個對象資料類型的值

2.并且在prototype上浏覽器天生給他加了一個屬性constructor(構造函數),屬性值是目前函數(類)本身

3.每一個函數資料類型也天生自帶一個屬性proto,屬性值是目前執行個體所屬類的原型(prototype)

建立一個對象的兩種方式:

var obj = {}; 字面量方式

var obj = new Object(); 構造函數方式/執行個體建立方式

兩種方式沒有差別

var num = 1; 基本資料類型的數字1

var num = new Number(1); 對象資料類型

兩種方式有差別

for-in循環在周遊的時候,預設可以把自己私有的和在它所有類原型上拓展的屬性和方法都可以周遊到,但是,一般情況下,隻需要周遊私有的即可,可以使用以下的判斷進行處理:

可枚舉的:obj.propertyIsEnumerable()

Object.prototype.aaa = function () {
};
var obj = {name: "abc", age: };
for (var key in obj) {
    if (obj.propertyIsEnumerable(key)) {
        console.log(key); //name age
    }
}

           

或者:

Object.prototype.aaa = function () {
};
var obj = {name: "abc", age: };
for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
        console.log(key); //name age
    }
}
           

如果沒有可枚舉:

Object.prototype.aaa = function () {
};
var obj = {name: "abc", age: };
for (var key in obj) {
    console.log(key); //name age aaa
}
           

Object.create(proObj)

建立一個新對象,但是還要把proObj作為這個對象的原型

IE6-8中不相容

var obj = {
    getX: function () {

    }
};
var obj2 = Object.create(obj);
//obj2是對象資料類型
//__proto__:
//   getX:function...
//   __proto__:Object.prototype
obj2.getX();
obj.getY = function () {
    console.log();
};
obj.getY(); //能夠輸出2
           

模拟obj.create():

建立一個類,并且讓這個類的原型指向傳進來的對象,傳回類的執行個體,得到新的對象就指向傳進來的值

function object(o) {
    function Fn() {

    }
    Fn.prototype = o;
    return new Fn;
}
           

繼續閱讀