JS筆記(12): 對象的繼承

繼承:子類繼承父類的屬性和方法:javascript

  • 1.原型繼承
  • 2.call繼承
  • 3.寄生組合繼承
  • 4.ES6中的class類實現繼承
  • 5.擴展式繼承 B.prototype = {...A.prototype}
  • 6.deepclone深克隆 B.prototype = deepclone(A.prototype)
  • 繼承的目的:就是爲了代碼可以更好複用,組合起來生成一個新的類別

### 1、原型繼承

讓子類的原型指向父類的實例java

  • 1.方式:B.prototype = new A(); A的實例自己具有父類A的私有屬性和公有方法,子類B的原型指向它,name子類B的實例就能夠找到這些屬性和方法了
  • 2.和傳統後臺語言繼承爲:子類繼承父類,是把父類複製一份副本給子類(這樣處理子類和父類就沒有直接關係了)。JS中的原型繼承是讓子類和父類創建原型連接的機制,子類是實例調取父類原型上的方法,都是基於原型鏈的查找機制完成的
原型繼承存在的問題:
  • 1.父類實例私有屬性和公有屬性,都變爲子類實例的公有屬性
  • 2.若是子類B的原型上以前有屬性方法,從新執行A的實例後,以前的方法都沒用了

function A() {
    this.x = 100;
};
A.prototype = {
    consyructor: A,
    getX: function () {
        console.log(this.x);
    }
};
function B() {
    this.y = 200;
};
// function AA() {
// function x() {

// } //先建立一個空對象
// x.prototype = B.prototype; //把父類的原型地址賦值給空對象(即空對象的原型指向父類的原型)
// return new x; // 返回實例
// }
B.prototype = new A; //核心
let f = new B();
複製代碼

2、call繼承(屬性繼承):

  • call繼承:把父類A做爲普通函數執行,讓A中的this變爲B的實例,至關於給B的實例增長一個屬性和方法
  • 弊端:把父類A當作普通函數執行,和原型無關了,僅僅是把A中的私有屬性變爲子類B實例的私有屬性,A原型中的公有屬性方法和B及B的實例無關
  • new A() 把A做爲類建立它的實例 this:實例
  • A() 把A做爲普通函數執行 this:window
function A(name) {
    // this:f 
    this.x = name; // f.x=100
};
A.prototype = {
    consyructor: A,
    getX: function () {
        console.log(this.x);//100
    }
};
function B(name) {
    // this: 實例f
    A.call(this,name); //核心 把A執行,讓A中的this變爲f
    this.y = name;
};
let f  = new B('abc');
console.log(f.x)
複製代碼

3、寄生組合式繼承

  • 子類的私有空間裏繼承父類的私有屬性,子類的公有空間裏繼承父類的公有屬性
  • 寄生組合式和原型繼承的惟一區別:
    • B.prototype = new A(); 建立的A四位實例雖然指向了A的原型,可是實例中不是空的,存放了A的私有屬性,這些屬性變爲B的公有屬性
    • B.prototype = Object.create(A.prototype); 好處在於咱們是建立一個沒有任何私有屬性的空對象,指向A的原型,這樣B的公有中不會存在A四位私有屬性
  • Object.create(): 內置Object類天生自帶的方法,
    • 1.目的爲建立一個空對象
    • 2.讓新建立的空對象的__proto__指向第一個傳遞進來的對象(把obj做爲新建立空對象的原型)
// 原理:
function create(obj){
    function AA(){}
    let o = new AA;
    o.__proto__ = obj;
    return o;
}
複製代碼

關於Object.create()數組

let obj = {
    name: 'Tom'
};
console.log(Object.create(obj)); //{}
複製代碼
function A() {
    // this:f 
    this.x = 100; // f.x=100
};
A.prototype = {
    consyructor: A,
    getX: function () {
        console.log(this.x);//100
    }
};
function B() {
    A.call(this); //核心 基於call繼承,把A的私有變爲B的私有 f.x=100
    this.y = 200;
};
// B.prototype = A.prototype; //通常不這樣處理,由於這種模式能夠輕易修改父類A原型上的東西(重寫),這樣會致使A的其餘實例受到影響
B.prototype = Object.create(A.prototype);
let f = new B();
console.log(f.x)
複製代碼

4、ES6中的類和繼承

  • 語法:ES6中建立類是有本身標準語法的(這種語法建立出來的類只能new執行,不能當作普通函數執行)
  • class繼承的核心原理:
    • 1.子類.call(this) => 繼承父類的私有屬性
    • 2.子類.prototype = Object.create(父類.prototype) => 繼承父類的公有方法
    • 3.子類.__proto__ 指向父類(本來,類.__proto__ = Function.prototype

//ES6 class繼承
class A {
    constructor() {
        console.log(arguments); //Tom 子類的實例把參數傳遞給子類的構造函數,
        this.name = arguments;
        this.x = 100;
    }
    getX() {
        console.log(this.x);
    }
}
class B extends A{//相似與實現了原型繼承
    constructor(){
        super(...arguments)
        //super相似於call繼承,在這裏至關於把父類的constructor執行,而且讓方法中的this是B的實例,super當中傳遞的實參都是給A的
        this.y = 200;
        console.log(...arguments)
    }
    getY (){
        console.log(this.y);
    }
}
let f = new B('Tom','Jerry');
console.dir(f);
複製代碼

關於class語法:函數

class Fn { //Fn是類名,沒有小括號
    constructor(n, m) {
        // super();
        //=> 等價於傳統ES5中類的構造體
        this.x = n;
        this.y = m;
    }
    //=> 給Fn的原型上設置方法(只能設置方法不能設置屬性)
    getX() {
        console.log(this.x);
    }
    //設置靜態方法:把Fn當作一個普通對象設置的私有方法(和實例沒有關係),一樣也只能設置方法不能寫屬性
    static AA(){
        console.log(3)
    }
}
let f = new Fn(10,20);
console.log(f.x); //10
f.getX(); //10
Fn.AA(); //3
複製代碼

5、擴展式繼承:(淺克隆)

  • B.prototype = {...A.prototype}
  • 把父類的prototype複製一份給子類(把地址賦值給子類)
function A (){
    this.name = 'Tom';
};
A.prototype.getX = function(){
    console.log(this.name);
}
function B(){
    A.call(this);
    this.age = 18
}
B.prototype = {...A.prototype};
let f = new B();
console.log(f); //B {name: "Tom", age: 18} age: 18 name: "Tom" __proto__:getX: ƒ () __proto__: Object
複製代碼

6、deepclone:深克隆

function A() {
    this.name = 'Tom';
};
A.prototype.getX = function () {
    console.log(this.name);
}
function B() {
    A.call(this);
    this.age = 18
}
B.prototype = deepClone(A.prototype); //將父類的原型上的屬性和方法克隆一份 複製地址給子類
let f = new B();
console.log(f);

function deepClone(obj) {
//先聲明一個數組,去存克隆出來的內容
//判斷obj是否爲數組,是數組就o就爲[],不然爲{}
let o = obj.push ? [] : {};
//循環傳進來的對象
for (let attr in obj) {
//檢測對象中各項是否爲該對象的私有屬性,true則執行下面的代碼
//判斷對象中的某個值是否爲引用類型
//若是是,就繼續調用deepClone把引用值傳到函數中
if (obj.hasOwnProperty(attr)) {
    if (typeof obj[attr] === 'object') {
        o[attr] = deepClone(obj[attr])
    } else {
        //若是是簡單類型就直接賦值
        o[attr] = obj[attr];
    }
}
}
return o;
}
複製代碼

====ui

我的認爲,this

  • 原型繼承,call繼承,寄生組合繼承,class繼承是真正的繼承,由於這四種方式都是讓子類和父類產生關係,父類的屬性或方法發生變化,那麼子類繼承的屬性或方法也會發生相應的變化
  • 而擴展式繼承和深克隆繼承不該算做是真正的繼承,由於這兩種方式的目的都是把父類複製一份 給子類,此時子類原型的地址和父類原型的地址是不同的。若是父類的屬性或方法發生改變,那麼子類上的屬性或方法不會改變,沒有作到真正的繼承
相關文章
相關標籤/搜索