成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

js繼承、構(gòu)造函數(shù)繼承、原型鏈繼承、組合繼承、組合繼承優(yōu)化、寄生組合繼承

孫淑建 / 2112人閱讀

摘要:創(chuàng)建子類實(shí)例,可以向父類構(gòu)造函數(shù)傳參數(shù)。修復(fù)如下其實(shí)方式組合繼承優(yōu)化核心通過這種方式,砍掉父類的實(shí)例屬性,這樣在調(diào)用父類的構(gòu)造函數(shù)的時(shí)候,就不會(huì)初始化兩次實(shí)例,避免組合繼承的缺點(diǎn)。優(yōu)點(diǎn)只調(diào)用一次父類構(gòu)造函數(shù)。

2018.06.03

第一部分:導(dǎo)入 1、構(gòu)造函數(shù)的屬性
funcion A(name) {
    this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
    this.arr = [1]; // 實(shí)例引用屬性 (該屬性,強(qiáng)調(diào)私用,不共享)
    this.say = function() { // 實(shí)例引用屬性 (該屬性,強(qiáng)調(diào)復(fù)用,需要共享)
        console.log("hello")
    }
}
注意:數(shù)組和方法都屬于‘實(shí)例引用屬性’,但是數(shù)組強(qiáng)調(diào)私有、不共享的。方法需要復(fù)用、共享。

注意:在構(gòu)造函數(shù)中,一般很少有數(shù)組形式的引用屬性,大部分情況都是:基本屬性 + 方法。
2、原型對象的作用
原型對象的用途是為每個(gè)實(shí)例對象存儲(chǔ)共享的方法和屬性,它僅僅是一個(gè)普通對象而已。并且所有的實(shí)例是共享同一個(gè)原型對象,因此有別于實(shí)例方法或?qū)傩裕蛯ο髢H有一份。而實(shí)例有很多份,且實(shí)例屬性和方法是獨(dú)立的。

在構(gòu)造函數(shù)中:為了屬性(實(shí)例基本屬性)的私有性、以及方法(實(shí)例引用屬性)的復(fù)用、共享。我們提倡:

將屬性封裝在構(gòu)造函數(shù)中

將方法定義在原型對象上

funcion A(name) {
    this.name = name; // (該屬性,強(qiáng)調(diào)私有,不共享)
}
A.prototype.say = function() { // 定義在原型對象上的方法 (強(qiáng)調(diào)復(fù)用,需要共享)
        console.log("hello")
}

// 不推薦的寫法:[原因](https://blog.csdn.net/kkkkkxiaofei/article/details/46474303)
A.prototype = {
    say: function() { 
        console.log("hello")
    }
}
第二部分:js 繼承---各種方式的優(yōu)缺點(diǎn) 方式1、原型鏈繼承

核心:將父類實(shí)例作為子類原型

優(yōu)點(diǎn):方法復(fù)用

由于方法定義在父類的原型上,復(fù)用了父類構(gòu)造函數(shù)的方法。比如say方法。

缺點(diǎn):

創(chuàng)建子類實(shí)例的時(shí)候,不能傳參數(shù)。

子類實(shí)例共享了父類構(gòu)造函數(shù)的引用屬性,比如arr屬性。

function Parent() {
    this.name = "父親"; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
    this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
}
Parent.prototype.say = function() { // -- 將需要復(fù)用、共享的方法定義在父類原型上 
    console.log("hello")
}
function Child(like) {
    this.like = like;
}
Child.prototype = new Parent() // 核心

let boy1 = new Child()
let boy2 = new Child()

// 優(yōu)點(diǎn):共享了父類構(gòu)造函數(shù)的say方法
console.log(boy1.say(), boy2.say(), boy1.say === boy2.say); // hello , hello , true

// 缺點(diǎn)1:不能傳參數(shù)
// 缺點(diǎn)2:
console.log(boy1.name, boy2.name, boy1.name===boy2.name); // 父親,父親,true

boy1.arr.push(2); // 修改了boy1的arr屬性,boy2的arr屬性,也會(huì)變化,因?yàn)閮蓚€(gè)實(shí)例的原型上(Child.prototype)有了父類構(gòu)造函數(shù)的實(shí)例屬性arr;所以只要修改了boy1.arr,boy2.arr的屬性也會(huì)變化。  ----  原型上的arr屬性是共享的。
console.log(boy2.arr); // [1,2]

注意:修改boy1的name屬性,是不會(huì)影響到boy2.name。因?yàn)閚ame是基本屬性,不是引用屬性。
方式2、借用構(gòu)造函數(shù)

核心:借用父類的構(gòu)造函數(shù)來增強(qiáng)子類實(shí)例,等于是復(fù)制父類的實(shí)例屬性給子類。

優(yōu)點(diǎn):實(shí)例之間獨(dú)立。

創(chuàng)建子類實(shí)例,可以向父類構(gòu)造函數(shù)傳參數(shù)。

子類實(shí)例不共享父類構(gòu)造函數(shù)的引用屬性。如arr屬性

缺點(diǎn):

父類的方法不能復(fù)用

由于方法在父構(gòu)造函數(shù)中定義,導(dǎo)致方法不能復(fù)用(因?yàn)槊看蝿?chuàng)建子類實(shí)例都要?jiǎng)?chuàng)建一遍方法)。比如say方法。(方法應(yīng)該要復(fù)用、共享)

子類實(shí)例,繼承不了父類原型上的屬性。(因?yàn)闆]有用到原型)

function Parent(name) {
    this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
     this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
    this.say = function() { // 實(shí)例引用屬性 (該屬性,強(qiáng)調(diào)復(fù)用,需要共享)
        console.log("hello")
    }
}
function Child(name,like) {
    Parent.call(this,name);  // 核心
    this.like = like;
}
let boy1 = new Child("小紅","apple");
let boy2 = new Child("小明", "orange ");

// 優(yōu)點(diǎn)1:可傳參
console.log(boy1.name, boy2.name); // 小紅, 小明

// 優(yōu)點(diǎn)2:不共享父類構(gòu)造函數(shù)的引用屬性
boy1.arr.push(2);
console.log(boy1.arr,boy2.arr);// [1,2] [1]

// 缺點(diǎn)1:方法不能復(fù)用
console.log(boy1.say === boy2.say) // false (說明,boy1和boy2 
的say方法是獨(dú)立,不是共享的)

// 缺點(diǎn)2:不能繼承父類原型上的方法
Parent.prototype.walk = function () {   // 在父類的原型對象上定義一個(gè)walk方法。
    console.log("我會(huì)走路")
}
boy1.walk;  // undefined (說明實(shí)例,不能獲得父類原型上的方法)
方式3、組合繼承

核心:通過調(diào)用父類構(gòu)造函數(shù),繼承父類的屬性并保留傳參的優(yōu)點(diǎn);然后通過將父類實(shí)例作為子類原型,實(shí)現(xiàn)函數(shù)復(fù)用。

優(yōu)點(diǎn):

保留構(gòu)造函數(shù)的優(yōu)點(diǎn):創(chuàng)建子類實(shí)例,可以向父類構(gòu)造函數(shù)傳參數(shù)。

保留原型鏈的優(yōu)點(diǎn):父類的實(shí)例方法定義在父類的原型對象上,可以實(shí)現(xiàn)方法復(fù)用。

不共享父類的引用屬性。比如arr屬性

缺點(diǎn):

由于調(diào)用了2次父類的構(gòu)造方法,會(huì)存在一份多余的父類實(shí)例屬性,具體原因見文末。

注意:"組合繼承"這種方式,要記得修復(fù)Child.prototype.constructor指向

第一次Parent.call(this);從父類拷貝一份父類實(shí)例屬性,作為子類的實(shí)例屬性,

第二次Child.prototype = new Parent();創(chuàng)建父類實(shí)例作為子類原型,此時(shí)這個(gè)父類實(shí)例就又有了一份實(shí)例屬性,但這份會(huì)被第一次拷貝來的實(shí)例屬性屏蔽掉,所以多余。

為啥是兩次?如果還是,不清楚,可以看文末,我會(huì)詳細(xì)講解!

function Parent(name) {
    this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
    this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
}
Parent.prototype.say = function() { // --- 將需要復(fù)用、共享的方法定義在父類原型上 
    console.log("hello")
}
function Child(name,like) {
    Parent.call(this,name,like) // 核心   第二次
    this.like = like;
}
Child.prototype = new Parent() // 核心   第一次



let boy1 = new Child("小紅","apple")
let boy2 = new Child("小明","orange")

// 優(yōu)點(diǎn)1:可以傳參數(shù)
console.log(boy1.name,boy1.like); // 小紅,apple

// 優(yōu)點(diǎn)2:可復(fù)用父類原型上的方法
console.log(boy1.say === boy2.say) // true

// 優(yōu)點(diǎn)3:不共享父類的引用屬性,如arr屬性
boy1.arr.push(2)
console.log(boy1.arr,boy2.arr); // [1,2] [1] 可以看出沒有共享arr屬性。

注意:為啥要修復(fù)構(gòu)造函數(shù)的指向?
console.log(boy1.constructor); // Parent 你會(huì)發(fā)現(xiàn)實(shí)例的構(gòu)造函數(shù)居然是Parent。
而實(shí)際上,我們希望子類實(shí)例的構(gòu)造函數(shù)是Child,所以要記得修復(fù)構(gòu)造函數(shù)指向。修復(fù)如下
Child.prototype.constructor = Child;
其實(shí)Child.prototype = new Parent() 

console.log(Child.prototype.__proto__ === Parten.prototype); // true

方式4、組合繼承優(yōu)化1

核心:

通過這種方式,砍掉父類的實(shí)例屬性,這樣在調(diào)用父類的構(gòu)造函數(shù)的時(shí)候,就不會(huì)初始化兩次實(shí)例,避免組合繼承的缺點(diǎn)。

優(yōu)點(diǎn):

只調(diào)用一次父類構(gòu)造函數(shù)。

保留構(gòu)造函數(shù)的優(yōu)點(diǎn):創(chuàng)建子類實(shí)例,可以向父類構(gòu)造函數(shù)傳參數(shù)。

保留原型鏈的優(yōu)點(diǎn):父類的實(shí)例方法定義在父類的原型對象上,可以實(shí)現(xiàn)方法復(fù)用。

缺點(diǎn):

修正構(gòu)造函數(shù)的指向之后,父類實(shí)例的構(gòu)造函數(shù)指向,同時(shí)也發(fā)生變化(這是我們不希望的)

注意:"組合繼承優(yōu)化1"這種方式,要記得修復(fù)Child.prototype.constructor指向

原因是:不能判斷子類實(shí)例的直接構(gòu)造函數(shù),到底是子類構(gòu)造函數(shù)還是父類構(gòu)造函數(shù)。
function Parent(name) {
    this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
    this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
}
Parent.prototype.say = function() { // --- 將需要復(fù)用、共享的方法定義在父類原型上 
    console.log("hello")
}
function Child(name,like) {
    Parent.call(this,name,like) // 核心  
    this.like = like;
}
Child.prototype = Parent.prototype // 核心  子類原型和父類原型,實(shí)質(zhì)上是同一個(gè)



let boy1 = new Child("小紅","apple")
let boy2 = new Child("小明","orange")
let p1 = new Parent("小爸爸")

// 優(yōu)點(diǎn)1:可以傳參數(shù)
console.log(boy1.name,boy1.like); // 小紅,apple
// 優(yōu)點(diǎn)2:
console.log(boy1.say === boy2.say) // true

// 缺點(diǎn)1:當(dāng)修復(fù)子類構(gòu)造函數(shù)的指向后,父類實(shí)例的構(gòu)造函數(shù)指向也會(huì)跟著變了。
具體原因:因?yàn)槭峭ㄟ^原型來實(shí)現(xiàn)繼承的,Child.prototype的上面是沒有constructor屬性的,就會(huì)往上找,這樣就找到了Parent.prototype上面的constructor屬性;當(dāng)你修改了子類實(shí)例的construtor屬性,所有的constructor的指向都會(huì)發(fā)生變化。

沒修復(fù)之前:console.log(boy1.constructor); // Parent
修復(fù)代碼:Child.prototype.constructor = Child
修復(fù)之后:console.log(boy1.constructor); // Child
          console.log(p1.constructor);// Child 這里就是存在的問題(我們希望是Parent)
方式5、組合繼承優(yōu)化2 又稱 寄生組合繼承 --- 完美方式

核心:

優(yōu)點(diǎn):完美i

缺點(diǎn):---

function Parent(name) {
    this.name = name; // 實(shí)例基本屬性 (該屬性,強(qiáng)調(diào)私有,不共享)
    this.arr = [1]; // (該屬性,強(qiáng)調(diào)私有)
}
Parent.prototype.say = function() { // --- 將需要復(fù)用、共享的方法定義在父類原型上 
    console.log("hello")
}
function Child(name,like) {
    Parent.call(this,name,like) // 核心  
    this.like = like;
}
Child.prototype = Object.create(Parent.prototype) // 核心  通過創(chuàng)建中間對象,子類原型和父類原型,就會(huì)隔離開。不是同一個(gè)啦,有效避免了方式4的缺點(diǎn)。


Child.prototype.constructor = Child

let boy1 = new Child("小紅","apple")
let boy2 = new Child("小明","orange")
let p1 = new Parent("小爸爸")


注意:這種方法也要修復(fù)構(gòu)造函數(shù)的
修復(fù)代碼:Child.prototype.constructor = Child
修復(fù)之后:console.log(boy1.constructor); // Child
          console.log(p1.constructor);// Parent  完美
第三部分:其他 + 相關(guān)問題解答 1、Object.create() 或 Object.create(object, [,propertiesObject])
Object.create() 的第二參數(shù),是可選的。
- Object.create() 的內(nèi)部原理:
// 其中,o 是新創(chuàng)建對象的原型(對象)
function object(o) {
    function F() {}
    F.prototype = o
    return new F()
}
注意:之前,Object.create()沒有出現(xiàn)之前,就是采用的這種方式。
參見《js高級程序設(shè)計(jì)》P170

Object.create() 做了哪幾件事情?

創(chuàng)建空對象{}

指定空對象{}的原型為Object.create()的參數(shù)。

new 與 Object.create() 的區(qū)別?

以下是我的個(gè)人見解,(如有不對,還請指正):

new 產(chǎn)生的實(shí)例,優(yōu)先獲取構(gòu)造函數(shù)上的屬性;構(gòu)造函數(shù)上沒有對應(yīng)的屬性,才會(huì)去原型上查找;如果構(gòu)造函數(shù)中以及原型中都沒有對應(yīng)的屬性,就會(huì)報(bào)錯(cuò)。

Object.create() 產(chǎn)生的對象,只會(huì)在原型上進(jìn)行查找屬性,原型上沒有對應(yīng)的屬性,就會(huì)報(bào)錯(cuò)。

let Base1 = function() {
  this.a = 1
}
let o1 = new Base1()
let o2 = Object.create(Base1.prototype)
console.log(o1.a); // 1
console.log(o2.a); // undefined



let Base2 = function() {}
Base2.prototype.a = "aa"
let o3 = new Base2()
let o4 = Object.create(Base2.prototype)
console.log(o3.a); // aa
console.log(o4.a); // aa



let Base3 = function() {
  this.a = 1
}
Base3.prototype.a = "aa"
let o5 = new Base3()
let o6 = Object.create(Base3.prototype)
console.log(o5.a); // 1
console.log(o6.a); // aa
2、new 的過程
funciton Func(name) {
    this.name = name
}
let p = new Func("小紅")

new 的過程,做了啥?做了四件事。

創(chuàng)建一個(gè)空對象obj:let obj = new Object()

設(shè)置原型鏈

obj.__proto__ = Func.prototype
就是:將新對象的__proto__ 指向構(gòu)造函數(shù)的prototype

將構(gòu)造函數(shù)Func的this指向obj,并執(zhí)行構(gòu)造函數(shù)Func

let result = Func.call(obj)
就是:使用call或apply,將構(gòu)造函數(shù)的this綁定到新對象,并執(zhí)行構(gòu)造函數(shù)

判斷構(gòu)造函數(shù)Func的返回值類型

如果是引用類型,就返回這個(gè)引用類型的對象。如果是值類型或沒有return,則返回空對象obj。
if (typeof(result) === "object"){  
  func=result;  
}  
else{  
   func=obj; // 默認(rèn)返回
}
注意:js中的構(gòu)造函數(shù),是不需要有返回值的,所以默認(rèn)返回的是新創(chuàng)建的空對象obj
3、為啥‘組合繼承’這種方式,會(huì)執(zhí)行兩次父類構(gòu)造函數(shù)??

第一次:Child.prototype = new Parent()

‘new 的過程’的第三步,其實(shí)就是執(zhí)行了父類構(gòu)造函數(shù)。

第二次:Parent.call(this,name,like)

call的作用是改變函數(shù)執(zhí)行時(shí)的上下文。比如:A.call(B)。其實(shí),最終執(zhí)行的還是A函數(shù),只不過是用B來調(diào)用而已。所以,你就懂了Parent.call(this,name,like) ,也就是執(zhí)行了父類構(gòu)造函數(shù)。
第四部分:參考鏈接

new操作符具體干了什么呢?

new的過程

你不知道的javascript之Object.create 和new區(qū)別

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/108027.html

相關(guān)文章

  • js組合模式和寄生組合模式的區(qū)別研究

    摘要:組合模式繼承結(jié)合了構(gòu)造函數(shù)繼承時(shí)可以為每個(gè)屬性重新初始化,構(gòu)造一個(gè)副本的優(yōu)點(diǎn),以及原型鏈繼承時(shí)一次定義處處共享的優(yōu)點(diǎn)。但令我百思不得其解的是,從上面給出的例子來看,組合繼承并沒有調(diào)用兩次超類型構(gòu)造函數(shù)。 最近在閱讀《js權(quán)威指南》的繼承這一章,對于組合模式和寄生組合模式的區(qū)別有點(diǎn)混淆,在多次重讀以及嘗試之后,得到一些心得。 組合模式繼承 結(jié)合了構(gòu)造函數(shù)繼承時(shí)可以為每個(gè)屬性重新初始化,構(gòu)...

    tolerious 評論0 收藏0
  • js繼承的理解

    摘要:創(chuàng)建自定義的構(gòu)造函數(shù)之后,其原型對象只會(huì)取得屬性,其他方法都是從繼承來的。優(yōu)缺點(diǎn)寄生式繼承在主要考慮對象而不是創(chuàng)建自定義類型和構(gòu)造函數(shù)時(shí),是十分有用的。 原文鏈接:https://kongchenglc.coding.me... 1.原型鏈 ??js的繼承機(jī)制不同于傳統(tǒng)的面向?qū)ο笳Z言,采用原型鏈實(shí)現(xiàn)繼承,基本思想是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。理解原型鏈必須先理...

    BlackFlagBin 評論0 收藏0
  • JS高程筆記 - 繼承

    摘要:下面來看一個(gè)例子繼承屬性繼承方法在這個(gè)例子中構(gòu)造函數(shù)定義了兩個(gè)屬性和。組合繼承最大的問題就是無論什么情況下都會(huì)調(diào)用兩次超類型構(gòu)造函數(shù)一次是在創(chuàng)建子類型原型的時(shí)候另一次是在子類型構(gòu)造函數(shù)內(nèi)部。 組合繼承 組合繼承(combination inheritance),有時(shí)候也叫做偽經(jīng)典繼承,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊,從而發(fā)揮二者之長的一種繼承模式。其背后的思路是使用原型鏈...

    fsmStudy 評論0 收藏0
  • 《javascript高級程序設(shè)計(jì)》 繼承實(shí)現(xiàn)方式

    摘要:寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似,即創(chuàng)建一個(gè)僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部已某種方式來增強(qiáng)對象,最后再像真的是它做了所有工作一樣返回對象。 這篇本來應(yīng)該是作為寫JS 面向?qū)ο蟮那白?,只是作為《javascript高級程序設(shè)計(jì)》繼承一章的筆記 原型鏈 code 實(shí)現(xiàn) function SuperType() { this.colors = [red,blu...

    cppprimer 評論0 收藏0
  • JavaScript面向?qū)ο?--原型繼承

    摘要:因?yàn)檫@造成了繼承鏈的紊亂,因?yàn)榈膶?shí)例是由構(gòu)造函數(shù)創(chuàng)建的,現(xiàn)在其屬性卻指向了為了避免這一現(xiàn)象,就必須在替換對象之后,為新的對象加上屬性,使其指向原來的構(gòu)造函數(shù)。這個(gè)函數(shù)接收兩個(gè)參數(shù)子類型構(gòu)造函數(shù)和超類型構(gòu)造函數(shù)。 最近一直在研究js面向?qū)ο螅玩溊^承是一個(gè)難點(diǎn),下面是我對繼承的理解以下文章借鑒自CSDN季詩筱的博客 原型鏈繼承的基本概念: ES中描述了原型鏈的概念,并將原型鏈作為實(shí)現(xiàn)...

    vspiders 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<