成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何繼承Date對象?由一道題讓你徹底弄懂JS繼承

開發(fā) 前端
由于繼承的介紹在網(wǎng)上已經(jīng)多不勝數(shù),因此本文沒有再重復描述,而是由一道Date繼承題引發(fā),展開(關(guān)鍵就是原型鏈)。

[[218667]]

前言

故事是從一次實際需求中開始的。。。

某天,某人向我尋求了一次幫助,要協(xié)助寫一個日期工具類,要求:

  • 此類繼承自 Date,擁有Date的所有屬性和對象

  • 此類可以自由拓展方法

形象點描述,就是要求可以這樣:

  1. // 假設(shè)最終的類是 MyDate,有一個getTest拓展方法

  2. let date = new MyDate();

  3. // 調(diào)用Date的方法,輸出GMT絕對毫秒數(shù)

  4. console.log(date.getTime());

  5. // 調(diào)用拓展的方法,隨便輸出什么,譬如helloworld!

  6. console.log(date.getTest());

于是,隨手用JS中經(jīng)典的組合寄生法寫了一個繼承,然后,剛準備***收工,一運行,卻出現(xiàn)了以下的情景:

但是的心情是這樣的: 😳囧

以前也沒有遇到過類似的問題,然后自己嘗試著用其它方法,多次嘗試,均無果(不算暴力混合法的情況),其實回過頭來看,是因為思路新奇,憑空想不到,并不是原理上有多難。。。

于是,借助強大的搜素引擎,搜集資料,***,再自己總結(jié)了一番,才有了本文。

正文開始前,各位看官可以先暫停往下讀,嘗試下,在不借助任何網(wǎng)絡(luò)資料的情況下,是否能實現(xiàn)上面的需求?(就以 10分鐘為限吧)

分析問題的關(guān)鍵

借助stackoverflow上的回答。

經(jīng)典的繼承法有何問題

先看看本文最開始時提到的經(jīng)典繼承法實現(xiàn),如下:

  1. /**

  2. * 經(jīng)典的js組合寄生繼承

  3. */

  4. function MyDate() {

  5.    Date.apply(this, arguments);

  6.    this.abc = 1;

  7. }

  8.  function inherits(subClass, superClass) {

  9.    function Inner() {}

  10.   Inner.prototype = superClass.prototype;

  11.    subClass.prototype = new Inner();

  12.    subClass.prototype.constructor = subClass;

  13. }

  14. inherits(MyDate, Date);

  15. MyDate.prototype.getTest = function() {

  16.    return this.getTime();

  17. };

  18. let date = new MyDate();

  19. console.log(date.getTest());

就是這段代碼⬆,這也是JavaScript高程(紅寶書)中推薦的一種,一直用,從未失手,結(jié)果現(xiàn)在馬失前蹄。。。

我們再回顧下它的報錯:

再打印它的原型看看:

怎么看都沒問題,因為按照原型鏈回溯規(guī)則, Date的所有原型方法都可以通過 MyDate對象的原型鏈往上回溯到。再仔細看看,發(fā)現(xiàn)它的關(guān)鍵并不是找不到方法,而是 thisisnotaDateobject.

嗯哼,也就是說,關(guān)鍵是:由于調(diào)用的對象不是Date的實例,所以不允許調(diào)用,就算是自己通過原型繼承的也不行。

為什么無法被繼承?

首先,看看 MDN上的解釋,上面有提到,JavaScript的日期對象只能通過 JavaScriptDate作為構(gòu)造函數(shù)來實例化。

然后再看看stackoverflow上的回答:

有提到, v8引擎底層代碼中有限制,如果調(diào)用對象的 [[Class]]不是 Date,則拋出錯誤。

總的來說,結(jié)合這兩點,可以得出一個結(jié)論:要調(diào)用Date上方法的實例對象必須通過Date構(gòu)造出來,否則不允許調(diào)用Date的方法。

該如何實現(xiàn)繼承?

雖然原因找到了,但是問題仍然要解決啊,真的就沒辦法了么?當然不是,事實上還是有不少實現(xiàn)的方法的。

暴力混合法

首先,說說說下暴力的混合法,它是下面這樣子的:

說到底就是:內(nèi)部生成一個 Date對象,然后此類暴露的方法中,把原有 Date中所有的方法都代理一遍,而且嚴格來說,這根本算不上繼承(都沒有原型鏈回溯)。

ES5黑魔法

然后,再看看ES5中如何實現(xiàn)?

  1. // 需要考慮polyfill情況

  2. Object.setPrototypeOf = Object.setPrototypeOf ||

  3. function(obj, proto) {

  4.    obj.__proto__ = proto;

  5.   return obj;

  6. };

  7. /**

  8. * 用了點技巧的繼承,實際上返回的是Date對象

  9. */

  10. function MyDate() {

  11.    // bind屬于Function.prototype,接收的參數(shù)是:object, param1, params2...

  12.    var dateInst = new(Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))();

  13.   // 更改原型指向,否則無法調(diào)用MyDate原型上的方法

  14.    // ES6方案中,這里就是[[prototype]]這個隱式原型對象,在沒有標準以前就是__proto__

  15.    Object.setPrototypeOf(dateInst, MyDate.prototype);

  16.   dateInst.abc =

    1;
  17.   return dateInst;

  18. }

  19. // 原型重新指回Date,否則根本無法算是繼承

  20. Object.setPrototypeOf(MyDate.prototype, Date.prototype);

  21. MyDate.prototype.getTest = function getTest() {

  22.    return this.getTime();

  23. };

  24. let date = new MyDate();

  25. // 正常輸出,譬如1515638988725

  26. console.log(date.getTest());

一眼看上去不知所措?沒關(guān)系,先看下圖來理解:(原型鏈關(guān)系一目了然)

可以看到,用的是非常巧妙的一種做法:

正常繼承的情況如下:

  • newMyDate()返回實例對象 date是由 MyDate構(gòu)造的

  • 原型鏈回溯是: date(MyDate對象)->date.__proto__->MyDate.prototype->MyDate.prototype.__proto__->Date.prototype

這種做法的繼承的情況如下:

  • newMyDate()返回實例對象 date是由 Date構(gòu)造的

  • 原型鏈回溯是: date(Date對象)->date.__proto__->MyDate.prototype->MyDate.prototype.__proto__->Date.prototype

可以看出,關(guān)鍵點在于:

  • 構(gòu)造函數(shù)里返回了一個真正的 Date對象(由 Date構(gòu)造,所以有這些內(nèi)部類中的關(guān)鍵 [[Class]]標志),所以它有調(diào)用 Date原型上方法的權(quán)利

  • 構(gòu)造函數(shù)里的Date對象的 [[ptototype]](對外,瀏覽器中可通過 __proto__訪問)指向 MyDate.prototype,然后 MyDate.prototype再指向 Date.prototype

所以最終的實例對象仍然能進行正常的原型鏈回溯,回溯到原本Date的所有原型方法。

這樣通過一個巧妙的欺騙技巧,就實現(xiàn)了***的Date繼承。不過補充一點, MDN上有提到盡量不要修改對象的 [[Prototype]],因為這樣可能會干涉到瀏覽器本身的優(yōu)化。如果你關(guān)心性能,你就不應(yīng)該在一個對象中修改它的 [[Prototype]]

ES6大法

當然,除了上述的ES5實現(xiàn),ES6中也可以直接繼承(自帶支持繼承 Date),而且更為簡單:

  1. class MyDate extends Date {

  2.    constructor() {

  3.        super();

  4.        this.abc = 1;

  5.    }

  6.    getTest() {

  7.        return this.getTime();

  8.    }

  9. }

  10. let date = new MyDate();

  11. // 正常輸出,譬如1515638988725

  12. console.log(date.getTest());

對比下ES5中的實現(xiàn),這個真的是簡單的不行,直接使用ES6的Class語法就行了。而且,也可以正常輸出。

注意:這里的正常輸出環(huán)境是直接用ES6運行,不經(jīng)過babel打包,打包后實質(zhì)上是轉(zhuǎn)化成ES5的,所以效果完全不一樣。

ES6寫法,然后Babel打包

雖然說上述ES6大法是可以直接繼承Date的,但是,考慮到實質(zhì)上大部分的生產(chǎn)環(huán)境是: ES6+Babel

直接這樣用ES6 + Babel是會出問題的。

不信的話,可以自行嘗試下,Babel打包成ES5后代碼大致是這樣的:

然后當信心滿滿的開始用時,會發(fā)現(xiàn):

對,又出現(xiàn)了這個問題,也許這時候是這樣的⊙?⊙

因為轉(zhuǎn)譯后的ES5源碼中,仍然是通過 MyDate來構(gòu)造,而 MyDate的構(gòu)造中又無法修改屬于 Date內(nèi)部的 [[Class]]之類的私有標志,因此構(gòu)造出的對象仍然不允許調(diào)用 Date方法(調(diào)用時,被引擎底層代碼識別為 [[Class]]標志不符合,不允許調(diào)用,拋出錯誤)。

由此可見,ES6繼承的內(nèi)部實現(xiàn)和Babel打包編譯出來的實現(xiàn)是有區(qū)別的。(雖說Babel的polyfill一般會按照定義的規(guī)范去實現(xiàn)的,但也不要過度迷信)。

幾種繼承的細微區(qū)別

雖然上述提到的三種方法都可以達到繼承 Date的目的-混合法嚴格說不能算繼承,只不過是另類實現(xiàn)。

于是,將所有能打印的主要信息都打印出來,分析幾種繼承的區(qū)別,大致場景是這樣的:

可以參考:( 請進入調(diào)試模式)https://dailc.github.io/fe-interview/demo/extends_date.html

從上往下, 1,2,3,4四種繼承實現(xiàn)分別是:(排出了混合法)

  • ES6的Class大法

  • 經(jīng)典組合寄生繼承法

  • 本文中的取巧做法,Date構(gòu)造實例,然后更改 __proto__的那種

  • ES6的Class大法,Babel打包后的實現(xiàn)(無法正常調(diào)用的) 

  1. ~~~~以下是MyDate們的prototype~~~~~~~~~

  2. Date {constructor: ƒ, getTest: ƒ}

  3. Date {constructor: ƒ, getTest: ƒ}

  4. Date {getTest: ƒ, constructor: ƒ}

  5. Date {constructor: ƒ, getTest: ƒ}

  6.  

  7. ~~~~以下是new出的對象~~~~~~~~~

  8. Sat Jan 13 2018 21:58:55 GMT+0800 (CST)

  9. MyDate2 {abc: 1}

  10. Sat Jan 13 2018 21:58:55 GMT+0800 (CST)

  11. MyDate {abc: 1}

  12.  

  13. ~~~~以下是new出的對象的Object.prototype.toString.call~~~~~~~~~

  14. [object Date]

  15. [object Object]

  16. [object Date]

  17. [object Object]

  18.  

  19. ~~~~以下是MyDate們的__proto__~~~~~~~~~

  20. ƒ Date() { [native code] }

  21. ƒ () { [native code] }

  22. ƒ () { [native code] }

  23. ƒ Date() { [native code] }

  24.  

  25. ~~~~以下是new出的對象的__proto__~~~~~~~~~

  26. Date {constructor: ƒ, getTest: ƒ}

  27. Date {constructor: ƒ, getTest: ƒ}

  28. Date {getTest: ƒ, constructor: ƒ}

  29. Date {constructor: ƒ, getTest: ƒ}

  30.  

  31. ~~~~以下是對象的__proto__與MyDate們的prototype比較~~~~~~~~~

  32. true

  33. true

  34. true

  35. true

看出,主要差別有幾點:

  1. MyDate們的proto指向不一樣

  2. Object.prototype.toString.call的輸出不一樣

  3. 對象本質(zhì)不一樣,可以正常調(diào)用的 1,3都是 Date構(gòu)造出的,而其它的則是 MyDate構(gòu)造出的

我們上文中得出的一個結(jié)論是:由于調(diào)用的對象不是由Date構(gòu)造出的實例,所以不允許調(diào)用,就算是自己的原型鏈上有Date.prototype也不行

但是這里有兩個變量:分別是底層構(gòu)造實例的方法不一樣,以及對象的 Object.prototype.toString.call的輸出不一樣(另一個 MyDate.__proto__可以排除,因為原型鏈回溯肯定與它無關(guān))。

萬一它的判斷是根據(jù) Object.prototype.toString.call來的呢?那這樣結(jié)論不就有誤差了?

于是,根據(jù)ES6中的, Symbol.toStringTag,使用黑魔法,動態(tài)的修改下它,排除下干擾:

  1. // 分別可以給date2,date3設(shè)置

  2. Object.defineProperty(date2, Symbol.toStringTag, {

  3.    get: function() {

  4.        return "Date";

  5.    }

  6. });

然后在打印下看看,變成這樣了:

  1. [object Date]

  2. [object Date]

  3. [object Date]

  4. [object Object]

可以看到,第二個的 MyDate2構(gòu)造出的實例,雖然打印出來是 [objectDate],但是調(diào)用Date方法仍然是有錯誤。

此時我們可以更加準確一點的確認:由于調(diào)用的對象不是由Date構(gòu)造出的實例,所以不允許調(diào)用

而且我們可以看到,就算通過黑魔法修改 Object.prototype.toString.call,內(nèi)部的 [[Class]]標識位也是無法修改的。(這塊知識點大概是Object.prototype.toString.call可以輸出內(nèi)部的[[Class]],但無法改變它,由于不是重點,這里不贅述)。

ES6繼承與ES5繼承的區(qū)別

從上午中的分析可以看到一點:ES6的Class寫法繼承是沒問題的。但是換成ES5寫法就不行了。

所以ES6的繼承大法和ES5肯定是有區(qū)別的,那么究竟是哪里不同呢?(主要是結(jié)合的本文繼承Date來說)

區(qū)別:(以 SubClassSuperClassinstance為例)

ES5中繼承的實質(zhì)是:(那種經(jīng)典組合寄生繼承法)

  • 先由子類( SubClass)構(gòu)造出實例對象this

  • 然后在子類的構(gòu)造函數(shù)中,將父類( SuperClass)的屬性添加到 this上, SuperClass.apply(this,arguments)

  • 子類原型( SubClass.prototype)指向父類原型( SuperClass.prototype

  • 所以 instance是子類( SubClass)構(gòu)造出的(所以沒有父類的 [[Class]]關(guān)鍵標志)

  • 所以, instance有 SubClass和 SuperClass的所有實例屬性,以及可以通過原型鏈回溯,獲取 SubClass和 SuperClass原型上的方法

ES6中繼承的實質(zhì)是:

  • 先由父類( SuperClass)構(gòu)造出實例對象this,這也是為什么必須先調(diào)用父類的 super()方法(子類沒有自己的this對象,需先由父類構(gòu)造)

  • 然后在子類的構(gòu)造函數(shù)中,修改this(進行加工),譬如讓它指向子類原型( SubClass.prototype),這一步很關(guān)鍵,否則無法找到子類原型(注,子類構(gòu)造中加工這一步的實際做法是推測出的,從最終效果來推測)

  • 然后同樣,子類原型( SubClass.prototype)指向父類原型( SuperClass.prototype

  • 所以 instance是父類( SuperClass)構(gòu)造出的(所以有著父類的 [[Class]]關(guān)鍵標志)

  • 所以, instance有 SubClass和 SuperClass的所有實例屬性,以及可以通過原型鏈回溯,獲取 SubClass和 SuperClass原型上的方法

以上⬆就列舉了些重要信息,其它的如靜態(tài)方法的繼承沒有贅述。(靜態(tài)方法繼承實質(zhì)上只需要更改下 SubClass.__proto__SuperClass即可)

可以看著這張圖快速理解:

有沒有發(fā)現(xiàn)呢:ES6中的步驟和本文中取巧繼承Date的方法一模一樣,不同的是ES6是語言底層的做法,有它的底層優(yōu)化之處,而本文中的直接修改_proto_容易影響性能

ES6中在super中構(gòu)建this的好處?

因為ES6中允許我們繼承內(nèi)置的類,如Date,Array,Error等。如果this先被創(chuàng)建出來,在傳給Array等系統(tǒng)內(nèi)置類的構(gòu)造函數(shù),這些內(nèi)置類的構(gòu)造函數(shù)是不認這個this的。所以需要現(xiàn)在super中構(gòu)建出來,這樣才能有著super中關(guān)鍵的 [[Class]]標志,才能被允許調(diào)用。(否則就算繼承了,也無法調(diào)用這些內(nèi)置類的方法)

構(gòu)造函數(shù)與實例對象

看到這里,不知道是否對上午中頻繁提到的構(gòu)造函數(shù)實例對象有所混淆與困惑呢?這里稍微描述下。

要弄懂這一點,需要先知道 new一個對象到底發(fā)生了什么?先形象點說:

new MyClass()中,都做了些什么工作
  1. function MyClass() {

  2.    this.abc = 1;

  3. }

  4.  

  5. MyClass.prototype.print = function() {

  6.    console.log('this.abc:' + this.abc);

  7. };

  8.  

  9. let instance = new MyClass();

譬如,上述就是一個標準的實例對象生成,都發(fā)生了什么呢?

步驟簡述如下:(參考MDN,還有部分關(guān)于底層的描述略去-如[[Class]]標識位等)

  1. 構(gòu)造函數(shù)內(nèi)部,創(chuàng)建一個新的對象,它繼承自 MyClass.prototype, letinstance=Object.create(MyClass.prototype);

  2. 使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) MyClass,并將 this綁定到新創(chuàng)建的對象, MyClass.call(instance);,執(zhí)行后擁有所有實例屬性

  3. 如果構(gòu)造函數(shù)返回了一個“對象”,那么這個對象會取代整個 new出來的結(jié)果。如果構(gòu)造函數(shù)沒有返回對象,那么new出來的結(jié)果為步驟1創(chuàng)建的對象。 (一般情況下構(gòu)造函數(shù)不返回任何值,不過用戶如果想覆蓋這個返回值,可以自己選擇返回一個普通對象來覆蓋。當然,返回數(shù)組也會覆蓋,因為數(shù)組也是對象。)

結(jié)合上述的描述,大概可以還原成以下代碼(簡單還原,不考慮各種其它邏輯)

  1. let instance = Object.create(MyClass.prototype);

  2. let innerConstructReturn = MyClass.call(instance);

  3. let innerConstructReturnIsObj = typeof innerConstructReturn === 'object' || typeof innerConstructReturn === 'function';

  4.  

  5. return innerConstructReturnIsObj ? innerConstructReturn : instance;

注意⚠️:普通的函數(shù)構(gòu)建,可以簡單的認為就是上述步驟。實際上對于一些內(nèi)置類(如Date等),并沒有這么簡單,還有一些自己的隱藏邏輯,譬如 [[Class]]標識位等一些重要私有屬性。譬如可以在MDN上看到,以常規(guī)函數(shù)調(diào)用Date(即不加 new 操作符)將會返回一個字符串,而不是一個日期對象,如果這樣模擬的話會無效。

覺得看起來比較繁瑣?可以看下圖梳理:

那現(xiàn)在再回頭看看。

什么是構(gòu)造函數(shù)?

如上述中的 MyClass就是一個構(gòu)造函數(shù),在內(nèi)部它構(gòu)造出了 instance對象。

什么是實例對象?

instance就是一個實例對象,它是通過 new出來的?

實例與構(gòu)造的關(guān)系

有時候淺顯點,可以認為構(gòu)造函數(shù)是xxx就是xxx的實例。即:

  1. let instance = new MyClass();

此時我們就可以認為 instanceMyClass的實例,因為它的構(gòu)造函數(shù)就是它。

實例就一定是由對應(yīng)的構(gòu)造函數(shù)構(gòu)造出的么?

不一定,我們那ES5黑魔法來做示例。

  1. function MyDate() {

  2.    // bind屬于Function.prototype,接收的參數(shù)是:object, param1, params2...

  3.    var dateInst = new(Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))();

  4.   // 更改原型指向,否則無法調(diào)用MyDate原型上的方法

  5.    // ES6方案中,這里就是[[prototype]]這個隱式原型對象,在沒有標準以前就是__proto__

  6.    Object.setPrototypeOf(dateInst, MyDate.prototype);

  7.   dateInst.abc =

  8. 1;

  9.   return dateInst;

  10. }

我們可以看到 instance的最終指向的原型是 MyDate.prototype,而 MyDate.prototype的構(gòu)造函數(shù)是 MyDate,因此可以認為 instanceMyDate的實例。

但是,實際上, instance卻是由 Date構(gòu)造的,我們可以繼續(xù)用 ES6中的 new.target來驗證。

注意⚠️:關(guān)于 new.targetMDN中的定義是:new.target返回一個指向構(gòu)造方法或函數(shù)的引用

嗯哼,也就是說,返回的是構(gòu)造函數(shù)。

我們可以在相應(yīng)的構(gòu)造中測試打印:

  1. class MyDate extends Date {

  2.    constructor() {

  3.        super();

  4.        this.abc = 1;

  5.        console.log('~~~new.target.name:MyDate~~~~');

  6.        console.log(new.target.name);

  7.    }

  8. }

  9. // new操作時的打印結(jié)果是:

  10. // ~~~new.target.name:MyDate~~~~

  11. // MyDate

然后,可以在上面的示例中看到,就算是ES6的Class繼承, MyDate構(gòu)造中打印 new.target也顯示 MyDate,但實際上它是由 Date來構(gòu)造(有著 Date關(guān)鍵的 [[Class]]標志,因為如果不是Date構(gòu)造(如沒有標志)是無法調(diào)用Date的方法的)。

這也算是一次小小的勘誤吧。

所以,實際上new.target是無法判斷實例對象到底是由哪一個構(gòu)造構(gòu)造的(這里指的是判斷底層真正的 [[Class]]標志來源的構(gòu)造)

再回到結(jié)論:實例對象不一定就是由它的原型上的構(gòu)造函數(shù)構(gòu)造的,有可能構(gòu)造函數(shù)內(nèi)部有著寄生等邏輯,偷偷的用另一個函數(shù)來構(gòu)造了下,當然,簡單情況下,我們直接說實例對象由對應(yīng)構(gòu)造函數(shù)構(gòu)造也沒錯(不過,在涉及到這種Date之類的分析時,我們還是得明白)。

[[Class]]與Internal slot

這一部分為補充內(nèi)容。

前文中一直提到一個概念:Date內(nèi)部的 [[Class]]標識

其實,嚴格來說,不能這樣泛而稱之(前文中只是用這個概念是為了降低復雜度,便于理解),它可以分為以下兩部分:

在ES5中,每種內(nèi)置對象都定義了 [[Class]] 內(nèi)部屬性的值,[[Class]] 內(nèi)部屬性的值用于內(nèi)部區(qū)分對象的種類

  • Object.prototype.toString訪問的就是這個[[Class]]

  • 規(guī)范中除了通過 Object.prototype.toString,沒有提供任何手段使程序訪問此值。

  • 而且Object.prototype.toString輸出無法被修改

而在ES5中,之前的 [[Class]] 不再使用,取而代之的是一系列的 internalslot

  • Internal slot 對應(yīng)于與對象相關(guān)聯(lián)并由各種ECMAScript規(guī)范算法使用的內(nèi)部狀態(tài),它們沒有對象屬性,也不能被繼承

  • 根據(jù)具體的 Internal slot 規(guī)范,這種狀態(tài)可以由任何ECMAScript語言類型或特定ECMAScript規(guī)范類型值的值組成

  • 通過 Object.prototype.toString,仍然可以輸出Internal slot值

  • 簡單點理解(簡化理解),Object.prototype.toString的流程是:如果是基本數(shù)據(jù)類型(除去Object以外的幾大類型),則返回原本的slot,如果是Object類型(包括內(nèi)置對象以及自己寫的對象),則調(diào)用 Symbol.toStringTag。 Symbol.toStringTag方法的默認實現(xiàn)就是返回對象的Internal slot,這個方法可以被重寫

這兩點是有所差異的,需要區(qū)分(不過簡單點可以統(tǒng)一理解為內(nèi)置對象內(nèi)部都有一個特殊標識,用來區(qū)分對應(yīng)類型-不符合類型就不給調(diào)用)。

JS內(nèi)置對象是這些:

  1. "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"

ES6新增的一些,這里未提到:(如Promise對象可以輸出 [objectPromise]),而前文中提到的:

  1. Object.defineProperty(date, Symbol.toStringTag, {

  2.    get: function() {

  3.        return "Date";

  4.    }

  5. });

它的作用是重寫Symbol.toStringTag,截取date(雖然是內(nèi)置對象,但是仍然屬于Object)的 Object.prototype.toString的輸出,讓這個對象輸出自己修改后的 [objectDate]

但是,僅僅是做到輸出的時候變成了Date,實際上內(nèi)部的 internalslot值并沒有被改變,因此仍然不被認為是Date。

如何快速判斷是否繼承?

其實,在判斷繼承時,沒有那么多的技巧,就只有關(guān)鍵的一點: [[prototype]]__ptoto__)的指向關(guān)系

譬如:

  1. console.log(instance instanceof SubClass);

  2. console.log(instance instanceof SuperClass);

實質(zhì)上就是:

  • SubClass.prototype是否出現(xiàn)在 instance的原型鏈上

  • SuperClass.prototype是否出現(xiàn)在 instance的原型鏈上

然后,對照本文中列舉的一些圖,一目了然就可以看清關(guān)系。有時候,完全沒有必要弄的太復雜。

寫在***的話

由于繼承的介紹在網(wǎng)上已經(jīng)多不勝數(shù),因此本文沒有再重復描述,而是由一道Date繼承題引發(fā),展開(關(guān)鍵就是原型鏈)

不知道看到這里,各位看官是否都已經(jīng)弄懂了JS中的繼承呢?

另外,遇到問題時,多想一想,有時候你會發(fā)現(xiàn),其實你知道的并不是那么多,然后再想一想,又會發(fā)現(xiàn)其實并沒有這么復雜。。。

原文鏈接:http://www.dailichun.com/2018/01/15/howtoextenddate.html

責任編輯:龐桂玉 來源: 前端大全
相關(guān)推薦

2009-11-03 13:54:55

VB.NET繼承

2022-07-26 01:11:09

AMD芯片Intel

2011-03-07 13:29:52

NeusoftJava API

2022-01-19 11:39:15

數(shù)據(jù)治理大數(shù)據(jù)數(shù)據(jù)

2023-11-08 07:56:38

單鏈表雙鏈表

2024-03-18 13:32:11

2023-12-26 08:10:18

Postgresql數(shù)據(jù)庫Oracle

2022-02-18 00:13:53

JavaScript編程語言數(shù)組

2021-11-04 10:34:02

JavaScript繼承編程

2010-03-18 14:54:57

Java SynDem

2009-01-08 21:21:45

程序員筆記

2020-11-06 09:05:18

前端web開發(fā)

2022-04-14 20:43:24

JavaScript原型鏈

2023-10-30 11:53:37

繼承JS父類

2022-02-08 18:09:20

JS引擎解析器

2020-12-08 08:14:11

SQL注入數(shù)據(jù)庫

2023-10-11 08:18:22

RocketMQ邏輯訂閱

2009-07-14 17:36:28

Jython的繼承

2018-02-01 16:26:44

面試題static變量

2023-02-28 23:04:15

點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 狠狠色综合网站久久久久久久 | 拍拍无遮挡人做人爱视频免费观看 | 完全免费在线视频 | 亚洲人精品午夜 | 欧美精品久久久久久久久久 | 三级免费毛片 | 天天操天天干天天爽 | 天堂在线91| a级在线免费 | 超碰97人人人人人蜜桃 | 天天干天天玩天天操 | 国产精品欧美一区二区 | 成人在线视频一区 | 日韩中文字幕一区二区 | 99爱视频 | 毛片av免费看 | 青春草国产| 午夜精品福利视频 | 91大片| 亚洲国产一区二区在线 | 国产精品夜间视频香蕉 | 啪啪精品 | 国产免费一区 | 亚洲电影一区二区三区 | avmans最新导航地址 | 精品九九在线 | 国产91综合一区在线观看 | 欧美日韩国产一区二区 | 成人免费av在线 | 日本在线视频不卡 | 国产精品欧美一区二区三区不卡 | 午夜久久久久久久久久一区二区 | 日本免费网 | 日韩在线一区二区 | 欧美一区二区三区,视频 | 国产日韩久久久久69影院 | 欧美日韩在线一区二区三区 | 国产乱码一二三区精品 | 中文字幕 在线观看 | 精品国产一区二区三区久久久蜜月 | 伊人伊成久久人综合网站 |