首頁 web前端 js教程 Javascript之旅 对象的原型链之由来_javascript技巧

Javascript之旅 对象的原型链之由来_javascript技巧

May 16, 2016 pm 06:20 PM
原型鏈 物件

以问题开始:

function Base(){}var base = new Base()
上面两行代码会创建几个对象(object)?

要回答这个问题,先明确一下Javascript里object的概念。


Objects

在Javascript里,几乎一切都是object(Arrays、Functions、Numbers、Objects……),而没有C#里的class的概念。object的本质是一个name-value pairs的集合,其中name是string类型的,可以把它叫做“property”,value包括各种objects(string,number,boolean,array,function…),指的是property的值。


typeof

既然object包含Arrays、Functions、Numbers、Objects……,那怎么区分这些呢?答案是typeof。 typeof返回一个字符串,如typeof(Null) = “object”,typeof(false) = “Boolean”, typeof(1) = “number”。既然总是返回字符串,那么对于typeof (typeof x),不管x是什么,总是返回”string”。

Javascript之旅 对象的原型链之由来_javascript技巧

Constructor

JS里没有class,也就没有class里的构造函数,那么object是怎么被创建的呢?用构造器:constructor。constructor其实就是Function,因此本身也是object。开头的function Base(){}就是一个构造器,var b = new Base()就是用这个构造器(通过关键字new)创建了一个叫b的object。至此我们可以得出结论,开头的两行代码至少创建了2个object:一个是Base,类型为function的object,一个是base,类型为object的object。

 

Function()和Object()

这是两个重要的预定义好的构造器。一切function(比如开头的Base())都是由Function()构造出来的;而Object的prototype将会被所有object继承,下面会讲到。

    Javascript之旅 对象的原型链之由来_javascript技巧

 

Function的创建过程

当执行function Base(){this.a = 1}时,相当于var Base = new Function(“this.a = 1”),也就是说,这行代码本身,将使用预定义好的Function() constructor,来构造一个function型object(即Base)出来。在这个创建过程中,js将做哪些事呢?

  1, 首先当然会创建一个object起来,Base指向这个object。typeof 这个object = “function”
     Javascript之旅 对象的原型链之由来_javascript技巧
  2, 给Base附上__proto__属性,让它等于Function这个构造器的prototype(也是预定义好的)。这是很重要的一步,也是规律性的一步。(规律:)在执行任意类似varx = new X()时,都会把X的prototype赋给x的__proto__,也就是说,x.__proto__和X.prototype此时会指向同一个对象。

     Javascript之旅 对象的原型链之由来_javascript技巧
  3, 为Base创建call属性,该属性是个function。因此我们可以这样写:Base.Call()

     Javascript之旅 对象的原型链之由来_javascript技巧
  4, 为Base创建Construct属性,该属性也是个function。在执行var base = new Base()时,即会调用这个Construct属性。
  5, 为Base创建Scope,Length等属性,略。
  6, 为Base创建prototype属性:先用new Object()创建一个对象,为这个对象创建一个属性叫constructor,该属性值设置为Base。再把Base的prototype设置为这个新创建的对象。伪代码如下:

<div>
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="COLOR: #0000ff">var</span><span style="COLOR: #000000"> x </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Object();<br>x.constructor </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Base;<br>Base.prototype </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> x;</span>
</div>
登入後複製

 

先把关注点放到2和6。

 

__proto__和prototype

从2可以看出来,任意一个用构造器构造出来的object(包括Objects和Functions),都会有__proto__属性,指向该构造器的prototype属性。注意__proto__是个私有属性,在IE上是看不到的,我用的是chrome,可以看到。

从6可以看出,任意一个用new Function()构造出来的functions,都会有prototype属性,该属性是用new Object()构建出来的,初始公开属性只有一个constructor。

    Javascript之旅 对象的原型链之由来_javascript技巧

 

原型链

再来分析下第6步的伪代码,也就是为function创建prototype的这一步:

<div>
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="COLOR: #0000ff">var</span><span style="COLOR: #000000"> x </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Object(); </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> 参见2中的规律,会有x.__proto__= Object.prototype。</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">x.constructor </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Base;<br>Base.prototype </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> x;</span>
</div>
登入後複製

此时我们用Base()构造一个对象出来:

<div>
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="COLOR: #0000ff">var</span><span style="COLOR: #000000"> base</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Base(); </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> 参见2中的规律,会有base.__proto__ = Base.prototype,也就是 = x。</span>
</div><div>
<span style="COLOR: #008000"> </span><span style="COLOR: #008000; FONT-FAMILY: 'Courier New'"> // 因此有base.__proto__.__proto__ = x.__proto__</span>
</div><div><span style="COLOR: #008000; FONT-FAMILY: 'Courier New'"> // 而x.__proto__ = Object.prototype(见上一个代码片段)  </span></div><div><span style="COLOR: #008000; FONT-FAMILY: 'Courier New'"> // 所以,base.__proto__.__proto__ = Object.prototype.</span></div>
登入後複製

__proto__.__proto__,这就是传说中JS对象的原型链!由于用Function()创建构造器时的关键的第6步,保证了所有object的原型链的顶端,最终都指向了Object.prototype。

    Javascript之旅 对象的原型链之由来_javascript技巧

 

Property Lookup

而我们如果要读某个object的某个属性,JS会怎么做呢?

比如有个object叫xxx,我们执行alert(xxx.a),也就是读取xxx的a属性,那么JS首先会到xxx本身去找a属性,如果没找到,则到xxx.__proto__里去找a属性,由此沿着原型链往上,找到即返回(没找到,则返回undefined)。可以来看个例子:

    Javascript之旅 对象的原型链之由来_javascript技巧

上图得知:base本身是没有constructor属性的,但是base.constructor确实能返回Base这个函数,原因就在于base.__proto__有这个属性。(base.__proto__是啥?就是Base.prototype,上面构建Function的第6步的伪代码里,为Base.prototype.constructor赋值为Base本身。)

 

Object作为“基类”

另外,由于任意object的原型链的顶端都是Object.prototype。所以,Object.prototype里定义的属性,就会通过原型链,被所有的object继承下来。这样,预定义好的Object,就成了所有对象的“基类”。这就是原型链的继承。

    Javascript之旅 对象的原型链之由来_javascript技巧

看上图,Object.prototype已经预定义好了一些属性,我们再追加一条属性叫propertyA,那么这个属性和预定义属性一样,都可以从base上读到。

 

原型继承

已经得知,

对于 var xxx =new Object(); 有xxx.__proto__= Object.prototype;

对于 var xxx =new Base(); 有xxx.__proto__.__proto__= Object.prototype;

看上去很像什么呢?从c#角度看,很像Base是Object的子类,也就是说,由Base()构造出来的object,比由Object()构造出来的object,在原型链上更低一个层级。这是通过把Base.prototype指向由Object()创建的对象来做到的。那么自然而然,如果我想定义一个继承自Base的构造器,只需把改构造器的prototype指向一个Base()构造出来的对象。

<div>
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="COLOR: #0000ff">function</span><span style="COLOR: #000000"> Derived(){}<br></span><span style="COLOR: #0000ff">var</span><span style="COLOR: #000000"> base </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Base();<br>Derived.prototype </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> base;<br></span><span style="COLOR: #0000ff">var</span><span style="COLOR: #000000"> d </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> newDerived(); </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">很容易推算出:d.__proto__.__proto__.__proto__ = Object.prototype.</span>
</div>
登入後複製

推算过程:d.__proto__指向Derived.prototype,也就是base;则__proto__.__proto__指向base.__proto__,也就是Base.prototype,也就是某个new object()创建出来的东东,假设是o;则__proto__.__proto__.__proto__指向o.__proto__,也就是Object.prototype。

 

回答开头的问题,以及几个新的问题

那两行代码至少创建了三个对象:Base、base、Base.prototype。顺便说说,base是没有prototype属性的,只有function类型的object,在被构建时才会被创建prototype属性。

    Javascript之旅 对象的原型链之由来_javascript技巧

 

d.constructor会返回什么呢?

构造器Base()和Derived()里都是空的,如果有代码,将会怎么被执行呢?

……

待续。见下篇

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

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

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1318
25
PHP教程
1269
29
C# 教程
1248
24
使用PHP的json_encode()函數將陣列或物件轉換為JSON字串 使用PHP的json_encode()函數將陣列或物件轉換為JSON字串 Nov 03, 2023 pm 03:30 PM

JSON(JavaScriptObjectNotation)是一種輕量級的資料交換格式,已成為Web應用程式之間資料交換的常用格式。 PHP的json_encode()函數可以將陣列或物件轉換為JSON字串。本文將介紹如何使用PHP的json_encode()函數,包括語法、參數、傳回值以及具體的範例。語法json_encode()函數的語法如下:st

使用Python的__contains__()函數定義物件的包含操作 使用Python的__contains__()函數定義物件的包含操作 Aug 22, 2023 pm 04:23 PM

使用Python的__contains__()函數定義物件的包含操作Python是一種簡潔而強大的程式語言,提供了許多強大的功能來處理各種類型的資料。其中之一是透過定義__contains__()函數來實現物件的包含操作。本文將介紹如何使用__contains__()函數來定義物件的包含操作,並且給予一些範例程式碼。 __contains__()函數是Pytho

如何將 MySQL 查詢結果陣列轉換為物件? 如何將 MySQL 查詢結果陣列轉換為物件? Apr 29, 2024 pm 01:09 PM

將MySQL查詢結果陣列轉換為物件的方法如下:建立一個空物件陣列。循環結果數組並為每一行建立一個新的物件。使用foreach迴圈將每一行的鍵值對賦給新物件的對應屬性。將新物件加入到物件數組中。關閉資料庫連線。

原始碼探針:Python 中物件是如何被呼叫的? 原始碼探針:Python 中物件是如何被呼叫的? May 11, 2023 am 11:46 AM

楔子我們知道物件被創建,主要有兩種方式,一種是透過Python/CAPI,另一種是透過呼叫類型物件。對於內建類型的實例物件而言,這兩種方式都是支援的,例如列表,我們即可以透過[]創建,也可以透過list(),前者是Python/CAPI,後者是呼叫類型物件。但對於自訂類別的實例物件而言,我們只能透過呼叫類型物件的方式來創建。而一個物件如果可以被調用,那麼這個物件就是callable,否則就不是callable。而決定一個物件是不是callable,就取決於其對應的型別物件中是否定義了某個方法。如

使用Python的__le__()函數定義兩個物件的小於等於比較 使用Python的__le__()函數定義兩個物件的小於等於比較 Aug 21, 2023 pm 09:29 PM

標題:使用Python的__le__()函數定義兩個物件的小於等於比較在Python中,我們可以透過使用特殊方法來定義物件之間的比較操作。其中之一就是__le__()函數,它用來定義小於等於比較。 __le__()函數是Python中的一個魔法方法,並且是一種用於實現「小於等於」操作的特殊函數。當我們使用小於等於運算子(&lt;=)比較兩個物件時,Python

PHP 函數如何傳回物件? PHP 函數如何傳回物件? Apr 10, 2024 pm 03:18 PM

PHP函數可以透過使用return語句後接物件實例來傳回對象,從而將資料封裝到自訂結構中。語法:functionget_object():object{}。這允許創建具有自訂屬性和方法的對象,並以對象的形式處理資料。

C++ 函式回傳物件時有什麼需要注意的? C++ 函式回傳物件時有什麼需要注意的? Apr 19, 2024 pm 12:15 PM

在C++中,函數傳回物件需要注意三點:物件的生命週期由呼叫者負責管理,以防止記憶體洩漏。避免懸垂指針,透過動態分配記憶體或返回物件本身來確保物件在函數返回後仍然有效。編譯器可能會最佳化傳回物件的副本生成,以提高效能,但如果物件是值語義傳遞的,則無需副本生成。

分析Java中堆疊和堆疊的不同以及它們的應用情景 分析Java中堆疊和堆疊的不同以及它們的應用情景 Feb 24, 2024 pm 11:12 PM

Java堆和棧的區別及應用場景解析,需要具體程式碼範例在Java程式中,堆和棧是兩個常用的資料結構,它們在記憶體中承擔不同的角色和功能。了解堆疊和堆疊的差異對於編寫高效的Java程式至關重要。首先,我們來看看Java堆。堆是一個用來儲存物件的區域,所有在程式中被建立的物件都被儲存在堆中。堆是在程式運行時動態分配和釋放記憶體的地方,它不受任何限制,並且可以根據需要自動

See all articles