国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

繼承與原型

My_Oh_My / 429人閱讀

摘要:既然構造函數有屬于自己的原型對象,那么我們應該能讓另一個構造函數來繼承他的原型對象咯我們在構造函數內部執行了函數并改變了函數內部的指向其實這個指向的是實例化之后的對象。

我們在討(mian)論(shi)JavaScript這門語言時,總是繞不過的一個話題就是“繼承與原型鏈”。那么“繼承與原型鏈”到底是什么呢?

我很喜歡的一個聊天模式是:我不能說XX是什么,我只能說XX像什么。也就是說我不直接跟你說定義,因為通常而言,“定義”所描述的概念很晦澀,比如關于“閉包”的定義——閉包是函數和聲明該函數的詞法環境的組合。

所以,我們先來看一下,JavaScript里到底“繼承與原型鏈”是如何表現的。

“繼承與原型鏈”像什么

不同于Java等的靜態語言,在JavaScript這門語言里,我們沒有“類”這個概念,所有的繼承都是基于原型的。我們先直接看個例子:

var obj = {
    a: 0,
    f: function() {
        return this.a + 1
    }
}

var obj_1 = {} // 我們期望cat也能有sound屬性跟speak方法

obj_1.__proto__ = obj

console.log(obj_1.a) // 0
console.log(obj_1.f()) // 1

如上,我們定義obj_1這個對象的時候,并沒有聲明a屬性跟f方法,但是我們依然可以找到它們。這是因為在JavaScript中,你在一個對象上尋找某個屬性(JavaScript對象都是鍵值對的形式,所以方法其實也可以算一個屬性),他首先會在該對象本地尋找,如果沒有,他會順著原型鏈一層層往上尋找。

在上面的栗子中,對象obj_1本地沒有定義任何屬性,所以當我們執行obj_1.a的時候,會順著原型鏈往上找。在obj_1.__proto__ = obj這句里,我們將obj賦值給了obj_1__proto__屬性。

但是等等,__proto__是什么?

__proto__屬性指向的就是obj_1的原型,obj的原型是什么呢?我們可以打印obj.__proto__來看看,結果打印出來一大堆東西,這些其實就是Object.prototype,也就是“終極原型”,這個對象不再繼承任何原型。按照之前說的,obj_1應該也能直接訪問到這上面的屬性。事實也的確如此,比如:

obj_1.hasOwnProperty("a") // false

我們并沒有在obj_1上定義hasOwnProperty方法,但是依然可以找到該方法。事實上,所有以對象字面量(Object Literal)形式創建出來的對象,都繼承了有Object.prototype上的所有屬性。

那么我們能不能創建一個不繼承自任何原型的對象呢?答案是可以的。

JavaScript為我們提供了一個方法叫Object.create,通過它,我們可以創建一個原型為特定對象的對象。如果我們傳入一個null,那么我們就能創建一個“原型為空”的對象。

var a = Object.create(null)

在這個例子里,a成了一個空的對象,不僅本地沒有任何屬性,連原型鏈都沒有,也就是說它甚至都沒有繼承Object.prototype。(思考:這樣的空對象到底有什么作用呢?)

這樣一來,我們也可以利用Object.create來實現繼承咯?對的。

var obj = {
    a: 0,
    f: function() {
        return this.a + 1
    }
}

var obj_2 = Object.create(obj)
console.log(obj_2.a) // 0
console.log(obj_2.f()) // 1

但是重新想象,繼承的本質是什么?繼承原型!那么不管用什么方法,只要在我的原型鏈上能找到你就行了。

現在有一個問題,obj上定義了一個屬性a,如果我在obj_2上再定義一個屬性a,那么打印出來的會是誰的a呢?

var obj = {
    a: 0,
    f: function() {
        return this.a + 1
    }
}

var obj_2 = Object.create(obj)
obj_2.a = 2
console.log(obj_2.a) // 2

答案是顯而易見的,因為我們在尋找一個屬性的時候,總是從當前對象本地開始的,如果在當前對象上找到了這個屬性,那么查詢就停止了。所以,如果原型鏈過長,在查找一個靠前的原型上的屬性的時候,就會比較耗時。我們應當盡量避免這種過長的原型鏈。

“繼承與原型鏈”是什么

讀到這里,相信我們已經能夠對繼承原型鏈做一個定義了。

原型鏈

原型鏈就是從一個對象的__proto__開始,一直到這條線的最末端,大部分情況下,這個最末端就是Object.prototype。例如上面的那個例子:

var obj = {
    a: 0,
    f: function() {
        return this.a + 1
    }
}

var obj_2 = Object.create(obj)

// obj_2.__proto__ === obj
// obj.__proto__ === Object.prototype
繼承

在這個例子里,obj --- Object.prototype就組成了一個原型鏈,順著原型鏈,我們可以找到這個對象最開始繼承自哪個對象,同時,原型鏈上的每一個節點都可以繼承上游對象的所有屬性。繼承描述的應該是一種關系,或者一種動作。

new運算符

在前面的篇幅里我們知道,在JavaScript里,對象可以用字面量的形式與Object.create的形式來創建。但是JavaScript里還有一種方式來創建一個對象,那就是使用new運算符。

var obj = new Object

console.log(obj) // {}

根據前面的內容,我們可知obj繼承了Object.prototype對象上的屬性。關于new操作符,可以看我的另一篇專欄當我們在JavaScript中new一個對象的時候,我們到底在做什么。那么Object是什么?

我們來執行一下typeof Object,打印出來的是"function"。對的,Object是一個函數,準確地說,它是一個構造函數。new運算符操作的,應該是一個函數。

我們可以對任意函數執行new操作。但是一個函數如果被用作了構造函數來實例化對象,那我們傾向于把它的首字母大寫。

var Foo = function(x) {
    this.x = x
}

var boo = new Foo(1)
console.log(boo, boo.x) // Foo?{x: 1} 1

構造函數能讓我們初始化一個對象,在構造函數里,我們可以做一些初始化的操作。通常我們在編寫一些JavaScript插件的時候會在全局對象上掛載一個構造函數,通過實例化這個構造函數,我們可以繼承它的原型對象上的所有屬性。

既然構造函數有屬于自己的原型對象,那么我們應該能讓另一個構造函數來繼承他的原型對象咯?

var Human = function(name) {
    this.name = name
}
var Male = function(name) {
    Human.call(this, name)
    this.gender = "male"
}

var jack = new Male("jack")
console.log(jack) // Male?{name: "jack", gender: "male"}

我們在構造函數內部執行了Human函數并改變了Human函數內部的this指向(其實這個this指向的是實例化之后的對象)。同時,我們在Male的原型上定義一個自己的屬性gender,這樣,實例化出來的對象同時有了兩個屬性。

但是這個繼承完整么?繼承是需要繼承原型的,但是jack的原型鏈上并沒有Human,我們需要額外兩步。

var Human = function(name) {
    this.name = name
}
var Male = function(name) {
    Human.call(this, name)
    this.gender = "male"
}

Male.prototype = Object.create(Human.prototype)
Male.prototype.constructor = Male

var jack = new Male("jack")
console.log(jack) // Male?{name: "jack", gender: "male"}

這樣一來,我們就能在jack的原型鏈上找到Human了。

ES6的類

其實前面一節看起來會比較晦澀,因為在ES6之前,JavaScript沒有類的概念(當然之后也沒有),但是我們卻有“構造函數”,那上面一節的栗子就應該說是構造函數Male繼承了構造函數Human

我記得當時場面有點尷尬,大家都搓著手低著頭都不知道說點兒什么

好在ES6里我們有了Class的關鍵字,這是個語法糖,本質上,JavaScript的繼承還是基于原型的。但是,至少形式上,我們可以按照“類”的方式來寫代碼了。

class Human {
    constructor(name) {
        this.name = name
    }
}
class Male extends Human {
    constructor(name) {
        super(name)
        this.gender = "male"
    }
}

var jack = new Male("jack")
console.log(jack) // Male?{name: "jack", gender: "male"}

在控制臺上順著__proto__一層層往下翻,我們會能找到class Maleclass Human,這說明我們的繼承成功了。同時,我們也可以理解成“類Male繼承了類Human”,雖然在JavaScript其實并沒有類這個東西。

結語

其實通篇的核心還是那句話:JavaScript的繼承是基于原型的。很多內容我沒有展開講解很多,表達了主干即可。

引用

繼承與原型

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/107341.html

相關文章

  • JavaScript系列--淺析原型繼承

    摘要:綜上所述有原型鏈繼承,構造函數繼承經典繼承,組合繼承,寄生繼承,寄生組合繼承五種方法,寄生組合式繼承,集寄生式繼承和組合繼承的優點于一身是實現基于類型繼承的最有效方法。 一、前言 繼承是面向對象(OOP)語言中的一個最為人津津樂道的概念。許多面對對象(OOP)語言都支持兩種繼承方式::接口繼承 和 實現繼承 。 接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。由于js中方法沒有簽名...

    draveness 評論0 收藏0
  • 創建對象(一):創建繼承

    摘要:創建實例的方式有三種對象字面量表示法操作符跟構造函數中的函數。下面主要講的是最為復雜的操作符跟構造函數的創建對象實例的方法。 創建對象 一.創建對象的方法 理解原型對象: 無論什么時候,只要創建了新函數,就會根據一組特定的規則為該函數創建一個 prototype屬性,這個屬性指向函數的原型對象。在默認情況下,所有原型對象都會自動獲得一個constructor屬性,這個屬性包含一個指向p...

    microelec 評論0 收藏0
  • JavaScript 的繼承方式及優缺點

    摘要:繼承簡介在的中的面向對象編程,繼承是給構造函數之間建立關系非常重要的方式,根據原型鏈的特點,其實繼承就是更改原本默認的原型鏈,形成新的原型鏈的過程。 showImg(https://segmentfault.com/img/remote/1460000018998684); 閱讀原文 前言 JavaScript 原本不是純粹的 OOP 語言,因為在 ES5 規范中沒有類的概念,在 ...

    nanchen2251 評論0 收藏0
  • JS學習筆記(第6章)(面向對象之繼承——JS繼承的六大方式)

    摘要:除此之外,在超類型的原型中定義的方法,對子類型而言也是不可兼得,結果所有類型都只能用構造函數模式。創建對象增強對象指定對象繼承屬性這個例子的高效率體現在它只調用了一次構造函數。 1、原型鏈 原型鏈的基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。構造函數、原型和實例的關系:每個構造函數都有一個原型對象;原型對象都包含著一個指向構造函數的指針;實例都包含一個指向原型對象的...

    lscho 評論0 收藏0
  • 前端進擊的巨人(七):走進面向對象,原型原型鏈,繼承方式

    摘要:除了以上介紹的幾種對象創建方式,此外還有寄生構造函數模式穩妥構造函數模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向對象 是以 對象 為中心的編程思想,它的思維方式是構造。 面向對象 編程的三大特點:封裝、繼承、多態: 封裝:屬性方法的抽象 繼承:一個類繼承(復制)另一個類的屬性/方法 多態:方...

    wums 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<