搜尋
首頁web前端js教程對原型鏈上不能使用物件的理解以及JS原型鏈的深刻探討

為什麼不能在原型鏈上使用物件?以及JS原型鏈的深層原理是什麼?

在剛接觸JS原型鏈的時候都會接觸到一個熟悉的名詞:prototype;如果你曾經深入過prototype,你會接觸到另一個名詞:__proto__(注意:兩邊各有兩條底線,不是一條)。以下將會圍繞著prototype__proto__這兩個名詞解釋

  一、為什麼不能在原型鏈上使用物件:

  先舉一個很簡單的例子,我有一個類別叫Humans(人類),然後我有一個物件叫Tom(一個人)和另一個物件叫Merry(另一個人),很明顯Tom和Merry都是由Humans這一類別實例化之後得到的,然後可以把這個例子寫成如下程式碼:

function Humans() {
    this.foot = 2;
}
Humans.prototype.ability = true;var Tom = new Humans();var Merry = new Humans();

console.log(Tom.foot);//结果:2console.log(Tom.ability);//结果:trueconsole.log(Merry.foot);//结果:2console.log(Merry.ability);//结果:true

以上是一個非常簡單的物件導向的例子,相信都能看懂,如果嘗試修改Tom的屬性ability,則

function Humans() {
    this.foot = 2;
}
Humans.prototype.ability = true;var Tom = new Humans();var Merry = new Humans();

Tom.ability = false;
console.log(Tom.foot);//结果:2console.log(Tom.ability);//结果:falseconsole.log(Merry.foot);//结果:2console.log(Merry.ability);//结果:true

以上可以看出Tom的ability屬性的值改變了,但不影響Merry的ability屬性的值,這正是我們想要的結果,也是物件導向的好處,由同一個類別實例化得到的各個物件之間是互不干擾的;OK,接下來給ability換成object物件又如何?程式碼如下:

function Humans() {    this.foot = 2;
}
Humans.prototype.ability = {
    run : '100米/10秒',
    jump : '3米'};var Tom = new Humans();var Merry = new Humans();

Tom.ability = {
    run : '50米/10秒',
    jump : '2米'};console.log(Tom.ability.run); //结果:'50米/10秒'console.log(Tom.ability.jump); //结果:'2米'console.log(Merry.ability.run); //结果:'100米/10秒'console.log(Merry.ability.jump); //结果:'3米'

以上程式碼就是在原型鏈上使用了對象,但從以上程式碼可以看出Tom的ability屬性的改變依然絲毫不會影響Merry的ability的屬性,於是乎你會覺得這樣的做法並無不妥,為什麼說不能在原型鏈上使用對象?接下來的程式碼就會顯得很不一樣,並且可以完全表達出原型鏈上使用物件的危險性:

function Humans() {    this.foot = 2;
}
Humans.prototype.ability = {
    run : '100米/10秒',
    jump : '3米'};var Tom = new Humans();var Merry = new Humans();

Tom.ability.run = '50米/10秒';
Tom.ability.jump = '2米';console.log(Tom.ability.run); //结果:'50米/10秒'console.log(Tom.ability.jump); //结果:'2米'console.log(Merry.ability.run); //结果:'50米/10秒'console.log(Merry.ability.jump); //结果:'2米'

沒錯,從以上程式碼的輸出結果可以看出Tom的ability屬性的改變影響到Merry的ability屬性了,於是就可以明白在原型鏈上使用物件是非常危險的,很容易會打破實例化物件之間的相互獨立性,這就是為什麼不能在原型鏈上使用物件的原因?是的,但我想說的可不只如此,而是其中的原理,看完後面JS原型鏈的深層原理之後,相信你會完全明白。

  在以下第二部分解釋JS原型鏈的深層原理之前,先來明確一個概念:原型鏈上的屬性或方法都是被實例化物件共用的,正因如此,上面的Tom.ability.run='50米/10秒',改動了原型連上的ability才導致另一個對象Merry受影響,既然如此,你可能會問Tom.ability = {……}不也是改動了原型鏈上的ability嗎,為什麼Merry沒有受影響?答案是Tom.ability = {……}並沒有改動原型鏈上的ability屬性,而是為Tom添加了一個自有屬性ability,以後在訪問Tom.ability的時候不再需要訪問原型鏈上的ability,而是訪問其自有屬性ability,這是就近原則;OK,如果你仍有疑問,可以用紙筆記下你的疑問,繼續往下看你會更明白。

二、JS原型鏈的深層原理

  首先要引入一個名詞__proto____proto__是什麼?在我的理解裡,__proto__才是真正的原型鏈,prototype只是一個殼。如果你使用的是chrome瀏覽器,那麼你可以嘗試使用console.log(Tom.__proto__.ability.run),你發現這樣的寫法完全可行,而且事實上當只有原型鏈上存在ability屬性的時候,Tom.ability其實是指向Tom.__proto__.ability的;當然,如果你跑到IE瀏覽器裡嘗試必然會報錯,事實上IE瀏覽器禁止了對__proto__ 的訪問,而chrome則是允許的,當然實際開發中,我並不建議直接就使用__proto__這一屬性,但它往往在我們調試程式碼時發揮著重要作用。有人可能會問到底Tom.__proto__和Humans.prototype是什麼關係,為了理清兩者的關係,下面先列出三條法則:

  1 、物件是擁有__proto__屬性的,但沒有prototype;例如:有Tom.__proto__,但沒有Tom.prototype

  2、類別沒有__proto__屬性,但有prototype;例如:沒有Humans.__proto__#prototype (這裡必須糾正一下,同時非常感謝'川川哥哥'提出這一處錯處,確實是我在寫到這一點的時候沒有考慮清楚,事實上Humans也是Function的一個實例對象,因此Humans. __proto__===Function.

prototype###是絕對成立的,稍有特殊的是這時Function.prototype是指向一個Empty(空)函數,值得推敲)。 ###

  3、由同一个类实例化(new)得到的对象的__proto__是引用该类的prototype的(也就是我们说的引用传递);例如Tom和Merry的__proto__都引用自Humans的prototype

  OK,上面说过Tom.ability={……}其实并没有改变原型链上的ability属性,或者说并没有改变Tom.__proto__.ability,而是为Tom添加了一个自有的ability属性,为了说明这一点,我们再次回到以上的第三个代码块,其代码如下:

function Humans() {    this.foot = 2;
}
Humans.prototype.ability = {
    run : '100米/10秒',
    jump : '3米'};var Tom = new Humans();var Merry = new Humans();

Tom.ability = {
    run : '50米/10秒',
    jump : '2米'};console.log(Tom.ability.run); //结果:'50米/10秒'console.log(Tom.ability.jump); //结果:'2米'console.log(Merry.ability.run); //结果:'100米/10秒'console.log(Merry.ability.jump); //结果:'3米'

当为Tom.ability赋予新的值后,再次访问Tom.ability时就不再指向Tom.__proto__.ability了,因为这时其实是为Tom添加了自有属性ability,可以就近取值了,你可以尝试用Chrome浏览器分别console.log(Tom.ability.run)和console.log(Tom.__proto__.ability.run),你会发现确实存在两个不同的值,再看完下面的图后,相信你会完全明白:
對原型鏈上不能使用物件的理解以及JS原型鏈的深刻探討于是可以有这样一个结论:当访问一个对象的属性或方法的时候,如果对象本身有这样一个属性或方法就会取其自身的属性或方法,否则会尝试到原型链(__proto__)上寻找同名的属性或方法。明白了这一点后,要解释以上第四个代码块的原理也非常容易了,其代码如下:

function Humans() {    this.foot = 2;
}
Humans.prototype.ability = {
    run : '100米/10秒',
    jump : '3米'};var Tom = new Humans();var Merry = new Humans();

Tom.ability.run = '50米/10秒';
Tom.ability.jump = '2米';console.log(Tom.ability.run); //结果:'50米/10秒'console.log(Tom.ability.jump); //结果:'2米'console.log(Merry.ability.run); //结果:'50米/10秒'console.log(Merry.ability.jump); //结果:'2米'

当Tom.ability.run=’50米/10秒’的时候,JS引擎会认为Tom.ability是存在的,因为有Tom.ability才会有Tom.ability.run,所以引擎开始寻找ability属性,首先是会从Tom的自有属性里寻找,在自有属性里并没有找到,于是到原型链里找,结果找到了,于是Tom.ability就指向了Tom.__proto__.ability了,修改Tom.ability.run的时候实际上就是修改了原型链上的ability了,因而影响到了所有由Humans实例化得到的对象,如下图:
對原型鏈上不能使用物件的理解以及JS原型鏈的深刻探討

希望上面所讲的内容足够清楚明白,下面通过类的继承对原型链作更进一步的深入:
先来看一个类的继承的例子,代码如下:

function Person() {
    this.hand = 2;    this.foot = 2;
}
Person.prototype.say = function () {
    console.log('hello');
}function Man() {
    Person.apply(this, arguments);//对象冒充
    this.head = 1;
}
Man.prototype = new Person();//原型链Man.prototype.run = function () {
    console.log('I am running');
};
Man.prototype.say = function () {
    console.log('good byte');
}var man1 = new Man();

以上代码是使用对象冒充和原型链相结合的混合方法实现类的继承,也是目前JS主流的实现类的继承的方法,如果对这种继承方法缺乏了解,可以看看这里。

  接下来看看以上实现继承后的原型链,可以运用prototype__proto__来解释其中的原理:

  1、从man1 = new Man(),可以知道man1的__proto__是指向Man.prototype的,于是有:

  公式一:man1.__proto__ === Man.prototype 为true

  2、从上面的代码原型链继承里面看到这一句代码 Man.prototype = new Person(),作一个转换,变成:Man.prototype = a,a = new Perosn();一个等式变成了两个等式,于是由a = new Perosn()可以推导出a.__proto__ = Person.prototype,结合Man.prototype = a,于是可以得到:

  公式二:Man.prototype.__proto__ === Person.prototype 为true

  由公式一和公式二我们就得出了以下结论:

  公式三:man1.__proto__.__proto__ === Person.prototype 为true

  公式三就是上述代码的原型链,有兴趣的话,可以尝试去推导多重继承的原型链,继承得越多,你会得到一个越长的原型链,而这就是原型链的深层原理;从公式三可以得出一个结论:当你访问一个对象的属性或方法时,会首先在自有属性寻找(man1),如果没有则到原型链找,如果在链上的第一环(第一个__proto__)没找到,则到下一环找(下一个__proto__),直到找到为止,如果到了原型链的尽头仍没找到则返回undefined(这里必须补充一点:同时非常感谢深蓝色梦想提出的疑问:尽头不是到了Object吗?是的,原型链的尽头就是Object,如果想问为什么,不妨做一个小小的实验:如果指定Object.prototype.saySorry = ‘I am sorry’,那么你会惊喜地发现console.log(man1.saySorry)是会弹出结果‘I am sorry’的)。

  以上就是原型链的深层原理,说难其实也算容易,如果细心研究,会发现原型链上有很多惊喜。

相关文章:

js中的作用域链和原型链以及原型继承

js的原型及原型链详解

相关视频:

JavaScript基本語法及基本語句影片教學

以上是對原型鏈上不能使用物件的理解以及JS原型鏈的深刻探討的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器