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

資訊專欄INFORMATION COLUMN

繼承的實現(xiàn)方式及原型概述 | JavaScript 隨筆

chenjiang3 / 876人閱讀

摘要:每一個對象直接量都是的子類,即構(gòu)造函數(shù)中的構(gòu)造函數(shù)與普通函數(shù)并沒有什么兩樣,只不過在調(diào)用時,前面加上了關(guān)鍵字,就當(dāng)成是構(gòu)造函數(shù)了。由于沒有傳入變量,在調(diào)用的構(gòu)造函數(shù)時,會出錯這個問題可以通過一個空對象來解決改自。

對于 OO 語言,有一句話叫“Everything is object”,雖然 JavaScript 不是嚴格意義上的面向?qū)ο笳Z言,但如果想要理解 JS 中的繼承,這句話必須時刻銘記于心。

JS 的語法非常靈活,所以有人覺得它簡單,因為怎么寫都是對的;也有人覺得它難,因為很難解釋某些語法的設(shè)計,誰能告訴我為什么 typeof null 是 object 而 typeof undefined 是 undefined 嗎?并且這是在 null == undefined 的前提下。很多我們自認為“懂”了的知識點,細細琢磨起來,還是會發(fā)現(xiàn)有很多盲點,“無畏源于無知”吧……

1. 簡單對象

既然是講繼承,自然是從最簡單的對象說起:

var dog = {
  name: "tom"
}

這便是對象直接量了。每一個對象直接量都是 Object 的子類,即

dog instanceof Object; // true
2. 構(gòu)造函數(shù)

JS 中的構(gòu)造函數(shù)與普通函數(shù)并沒有什么兩樣,只不過在調(diào)用時,前面加上了 new 關(guān)鍵字,就當(dāng)成是構(gòu)造函數(shù)了。

    function Dog(name) {
      this.name = name;
    }


var dog = new Dog("tom");

dog instanceof Dog; // true

兩個問題,第一,不加 new 關(guān)鍵字有什么后果?

那么 Dog 函數(shù)中的 this 在上下文(Context)中被解釋為全局變量,具體在瀏覽器端的話是 window 對象,在 node 環(huán)境下是一個 global 對象。

第二,dog 的值是什么?很簡單,undefined 。Do>g 函數(shù)沒有返回任何值,執(zhí)行結(jié)束后,dog 的值自然是 undefined 。

關(guān)于 new 的過程,這里也順便介紹一下,這個對后面理解原型(prototype)有很大的幫助:

創(chuàng)建一個空的對象,僅包含 Object 的屬性和方法。
將 prototype 中的屬性和方法創(chuàng)建一份引用,賦給新對象。
將 this 上的屬性和方法新建一份,賦給新對象。
返回 this 對象,忽略 return 語句。
需要明確的是,prototype 上的屬性和方法是實例間共享的,this 上的屬性和方法是每個實例獨有的。

3. 引入 prototype

現(xiàn)在為 Dog 函數(shù)加上 prototype,看一個例子:

function Dog(name) {
  this.name = name;
  this.bark = function() {};
}

Dog.prototype.jump = function() {};
Dog.prototype.species = "Labrador";
Dog.prototype.teeth = ["1", "2", "3", "4"];

var dog1 = new Dog("tom"),
    dog2 = new Dog("jerry");

dog1.bark !== dog2.bark; // true
dog1.jump === dog2.jump; // true

dog1.teeth.push("5");
dog2.teeth; // ["1", "2", "3", "4", "5"]

看到有注釋的那三行應(yīng)該可以明白“引用”和“新建”的區(qū)別了。

那么我們經(jīng)常說到的“原型鏈”到底是什么呢?這個術(shù)語出現(xiàn)在繼承當(dāng)中,它用于表示對象實例中的屬性和方法來自于何處(哪個父類)。好吧,這是筆者的解釋。

- Object
  bark: Dog/this.bark()
  name: "tom"
- __proto__: Object
    jump: Dog.prototype.jump()
    species: "Labrador"
  + teeth: Array[4]
  + constructor: Dog()
  + __proto__: Object  

上面的是 dog1 的原型鏈,不知道夠不夠直觀地描述“鏈”這個概念。

其中,bark 和 name 是定義在 this 中的,所以最頂層可以看到它倆。
然后,每一個對象都會有一個 proto 屬性(IE 11+),它表示定義在原型上的屬性和方法,所以 jump、species 和 teeth 自然就在這兒了。

最后就一直向上找 proto 中的屬性和方法。

4. 繼承的幾種實現(xiàn) 4.1 通過 call 或者 apply

繼承在編程中有兩種說法,一個叫 inherit,另一個是 extend 。前者是嚴格意義上的繼承,即存在父子關(guān)系,而后者僅僅是一個類擴展了另一個類的屬性和方法。那么 call 和 apply 就屬于后者的范疇。怎么說?

function Animal(gender) {
  this.gender = gender;
}

function Dog(name, gender) {
  Animal.call(this, gender);
  this.name = name;
}

var dog = new Dog("tom", "male");

dog instanceof Animal; // false

雖然在 dog 對象中有 gender 屬性,但 dog 卻不是 Animal 類型。甚至,這種方式只能“繼承”父類在 this 上定義的屬性和方法,并不能繼承 Animal.prototype 中的屬性和方法。

4.2 通過 prototype 實現(xiàn)繼承

要實現(xiàn)繼承,必須包含“原型”的概念。下面是很常用的繼承方式。

function Dog(name) {
  Animal.call(this);
}

Dog.prototype = new Animal(); // 先假設(shè) Animal 函數(shù)沒有參數(shù)
Dog.prototype.constructor = Dog;

var dog = new Dog("tom");

dog instanceof Animal; // true

繼承的結(jié)果有兩個:

獲得父類的屬性和方法;
正確通過 instanceof 的測試。
prototype 也是對象,它是創(chuàng)建實例時的裝配機,這個在前面有提過。new Animal() 的值包含 Animal 實例所有的屬性和方法,既然它賦給了 Dog 的 prototype,那么 Dog 的實例自然就獲得了父類的所有屬性和方法。

并且,通過這個例子可以知道,改變 Dog 的 prototype 屬性可以改變 instanceof 的測試結(jié)果,也就是改變了父類。

然后,為什么要在 Dog 的構(gòu)造函數(shù)中調(diào)用 Animal.call(this)?

因為 Animal 中可能在 this 上定義了方法和函數(shù),如果沒有這句話,那么所有的這一切都會給到 Dog 的 prototype 上,根據(jù)前面的知識我們知道,prototype 中的屬性和方法在實例間是共享的。

我們希望將這些屬性和方法依然保留在實例自身的空間,而不是共享,因此需要重寫一份。

至于為什么要修改 constructor,只能說是為了正確的顯示原型鏈吧,它并不會影響 instanceof 的判斷。或者有其他更深的道理我并不知道……

4.3 利用空對象實現(xiàn)繼承

上面的繼承方式已經(jīng)近乎完美了,除了兩點:

Animal 有構(gòu)造參數(shù),并且使用了這些參數(shù)怎么辦?
在 Dog.prototype 中多了一份定義在 Animal 實例中冗余的屬性和方法。

function Animal(name) {
  name.doSomething();
}

function Dog(name) {
  Animal.call(this, name);
}

Dog.prototype = new Animal(); // 由于沒有傳入name變量,在調(diào)用Animal的構(gòu)造函數(shù)時,會出錯
Dog.prototype.constructor = Dog;

這個問題可以通過一個空對象來解決(改自 Douglas Crockford)。

function DummyAnimal() {}
DummyAnimal.prototype = Animal.prototype;

Dog.prototype = new DummyAnimal();
Dog.prototype.constructor = Dog;

他的原始方法是下面的 object:

function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

Dog.prototype = object(Animal.prototype);
Dog.prototype.constructor = Dog;
4.4 利用 proto 實現(xiàn)繼承

現(xiàn)在就只剩下一個問題了,如何把冗余屬性和方法去掉?

其實,從第 3 小節(jié)介紹原型的時候就提到了 proto 屬性,instanceof 運算符是通過它來判斷是否屬于某個類型的。

所以我們可以這么繼承:

function Dog() {
  Animal.call(this);
}

Dog.prototype = {
  __proto__: Animal.prototype,
  constructor: Dog
};

如果不考慮兼容性的話,這應(yīng)該是從 OO 的角度來看最貼切的繼承方式了。

4.5 拷貝繼承

這個方式也只能稱之為 extend 而不是 inherit,所以也沒必要展開說。

像 Backbone.Model.extend、jQuery.extend 或者 _.extend 都是拷貝繼承,可以稍微看一下它們是怎么實現(xiàn)的。(或者等我自己再好好研究之后過來把這部分補上吧)

5. 個人小結(jié)

當(dāng)我們在討論繼承的實現(xiàn)方式時,給我的感覺就像孔乙己在炫耀“茴香豆”的“茴”有幾種寫法一樣。繼承是 JS 中占比很大的一塊內(nèi)容,所以很多庫都有自己的實現(xiàn)方式,它們并沒有使用我認為的“最貼切”的方法,為什么?JS 就是 JS,它生來就設(shè)計得非常靈活,所以我們?yōu)槭裁床焕眠@個特性,而非得將 OO 的做法強加于它呢?

通過繼承,我們更多的是希望獲得父類的屬性和方法,至于是否要保證嚴格的父類/子類關(guān)系,很多時候并不在乎,而拷貝繼承最能體現(xiàn)這一點。對于基于原型的繼承,會在代碼中看到各種用 function 定義的類型,而拷貝繼承更通用,它只是將一個對象的屬性和方法拷貝(擴展)到另一個對象而已,并不關(guān)心原型鏈是什么。

當(dāng)然,在我鼓吹拷貝繼承多么多么好時,基于原型的繼承自然有它不可取代的理由。所以具體問題得具體分析,當(dāng)具體的使用場景沒定下來時,就不存在最好的方法。

個人見解,能幫助大家更加理解繼承一點就最好,如果有什么不對的,請多多指教!

文章來自:http://1ke.co/course/393

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/91511.html

相關(guān)文章

  • [JavaScript 隨筆] 關(guān)于 this 你必須知道這幾點

    摘要:關(guān)于中的坑大家都踩過。那這里的和是嚴格相等的。這里介紹的是通過創(chuàng)建對象時的。提示一下,數(shù)組對象的函數(shù)本身就是有這個功能的,也就是說可以達到要求。事件有兩種記法,一個是也是類似,那么在中出現(xiàn)的表示觸發(fā)該事件的元素,也就是。 TL;DR: this 指向調(diào)用該方法的對象,只有函數(shù)執(zhí)行時,this 才有定義。 關(guān)于 JavaScript 中 this 的坑大家都踩過。像本文開頭的這句話,道理...

    鄒強 評論0 收藏0
  • JS對象隨筆

    摘要:原型對象對象的原型對象實質(zhì)上是對象的構(gòu)造函數(shù)的原型對象。構(gòu)造函數(shù)所有的對象都是通過構(gòu)造函數(shù)實例化出來的。即一個對象,如果沿著原型鏈找下去,最終都會找到構(gòu)造函數(shù)原型對象相互之間糾纏不休,你中有我,我中有你。 JS中的對象 JS中對象(若無特殊說明,本文中的對象都為對象實例,即使是空對象實例)可謂是一個核心的概念,縱觀整個JS的數(shù)據(jù)結(jié)構(gòu)如String、Number、Array、Boolea...

    Lin_YT 評論0 收藏0
  • 前端文檔收集

    摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個最重要的技術(shù)點常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實現(xiàn)的大排序算法一怪對象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術(shù)點 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...

    jsbintask 評論0 收藏0
  • 前端文檔收集

    摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個最重要的技術(shù)點常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實現(xiàn)的大排序算法一怪對象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術(shù)點 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...

    muddyway 評論0 收藏0

發(fā)表評論

0條評論

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