JavaScript 對(duì)象和原型鏈

對(duì)象與原型鏈

基于類和基于原型

我們都知道 JavaScript 是一個(gè)面向?qū)ο蟮恼Z言,但是它卻沒有其他諸如 Java、C++ 這些面向?qū)ο蟮恼Z言中都存在的這個(gè)概念。取而代之的是原型的概念。這其實(shí)就是兩種不同的編程范式。

  1. 基于類的面向?qū)ο?/p>

    在這種范式中,類定義了對(duì)象的結(jié)構(gòu)和行為以及繼承關(guān)系,所有基于該類的對(duì)象都有相同的行為和結(jié)構(gòu),不同的只是他們的狀態(tài)。

    創(chuàng)建新的對(duì)象通過類的構(gòu)造器來創(chuàng)建。只有少數(shù)基于類的面向?qū)ο笳Z言允許類在運(yùn)行時(shí)進(jìn)行修改。

  2. 基于原型的面向?qū)ο?/p>

    在這種范式中,關(guān)注的是一系列對(duì)象的行為,將擁有相似行為的對(duì)象通過原型鏈串聯(lián)起來。

    創(chuàng)建新的對(duì)象通過拓展原有對(duì)象創(chuàng)建。很多的基于原型的語言提倡運(yùn)行時(shí)對(duì)原型進(jìn)行修改。

對(duì)比

圖片來自 MDN

總的來說基于原型相對(duì)來說更加靈活。這也許是 JavaScript 選擇基于原型構(gòu)建面向?qū)ο蟮脑蛑话伞?/p>

對(duì)象:無序?qū)傩缘募?/h2>

ECMA262 把對(duì)象定義為:無序?qū)傩缘募?,其屬性可以包含基本值、?duì)象或者函數(shù)。

var obj = {
    a: 5,
    b: function() {},
    c:{ d: 10 }
}

基本類型 a,函數(shù) b,對(duì)象 c 都是對(duì)象 obj 的屬性。

實(shí)際上 JavaScript 中函數(shù)也可以添加屬性。

var fun = function(){}
fun.a = 5
fun.b = function() {}
fun.c = { d: 10 }

因此函數(shù)也是屬性的集合,它也是對(duì)象。

構(gòu)造函數(shù)

‘面向?qū)ο缶幊獭牡谝徊?,就是要生成?duì)象。在基于類的語言中類都有創(chuàng)建對(duì)象的構(gòu)造函數(shù)。而在 JavaScript 中沒有類,那么生成對(duì)象的工作就由函數(shù)來完成。這種函數(shù)被稱為構(gòu)造函數(shù)。

所有對(duì)象都有一個(gè) constructor 的屬性指向它的構(gòu)建函數(shù)。

你可能會(huì)提出反對(duì)意見:

var obj = { a: 5, b: 10 }
var fun = function(){}

你會(huì)說 obj 和 fun 都是對(duì)象,但他們都沒有通過函數(shù)生成啊。

其實(shí)這是 JavaScript 提供的語法糖,本質(zhì)上他們會(huì)分別調(diào)用 Object 和 Function (注意大寫)這兩個(gè)函數(shù)來生成。

obj.constructor // ? Object() { [native code] }
fun.constructor // ? Function() { [native code] }

等同于

// var obj = { a: 5, b: 10 }
var obj = new Object()
obj.a = 5
obj.b = 10

// var fun = function(){} 
var fun = new Function()

除了 Object 和 Function 這兩個(gè)函數(shù)外,你也可以自定義構(gòu)造函數(shù)。函數(shù)要具備下面的特征:

  • 為區(qū)別于普通函數(shù),通常構(gòu)造函數(shù)名首字母大寫;
  • 構(gòu)造函數(shù)必須通過 new 命令調(diào)用;
  • 構(gòu)造函數(shù)內(nèi)部使用 this 關(guān)鍵字,this 指向當(dāng)前構(gòu)造函數(shù)生成的對(duì)象;
  • 構(gòu)造函數(shù)沒有 return,默認(rèn)返回 this。

一個(gè)例子

function Person(name) {
    this.name = name
}
var peter = new Person('Peter')

其中 new 運(yùn)算符都做了以下工作:

  • 創(chuàng)建一個(gè)空對(duì)象,作為將要返回的對(duì)象實(shí)例;
  • 將空對(duì)象的原型 __proto__ 指向了構(gòu)造函數(shù)的 prototype 屬性;
  • 將空對(duì)象賦值給構(gòu)造函數(shù)內(nèi)部的 this 關(guān)鍵字;
  • 開始執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼;
  • 如果構(gòu)造器返回的是對(duì)象,則返回,否則返回第一步創(chuàng)建的對(duì)象。

這里出現(xiàn)了兩個(gè)容易混淆的概念:__proto__prototype。

  • __proto__ 是每個(gè)對(duì)象都有的一個(gè)屬性。指向創(chuàng)建該對(duì)象的函數(shù)的 prototype。用它來產(chǎn)生一個(gè)鏈,一個(gè)原型鏈,用于尋找方法名或?qū)傩?,等等。它是個(gè)隱藏屬性,早期低版本的瀏覽器甚至不支持這個(gè)屬性。
  • prototype 是每個(gè)函數(shù)都有的一個(gè)屬性。它本身是一個(gè)對(duì)象,它的 constructor 指向函數(shù)本身。這個(gè)屬性存在的目的就是在通過 new 來創(chuàng)建對(duì)象時(shí)構(gòu)造對(duì)象的 __proto__ 屬性。

只是通過上面的敘述可能理解起來比較困難,我們通過代碼和內(nèi)存布局來仔細(xì)分析。

對(duì)于 Person 函數(shù)

// Person.prototype 是一個(gè)對(duì)象,它有兩個(gè)屬性 constructor 指向 Person 函數(shù),__proto__ 指向 Object。(先不用理會(huì) Object 這個(gè)對(duì)象,后面會(huì)詳細(xì)介紹)

Person.prototype 
/*
 * {constructor: ?}
 *    constructor: ? Person()
 *    __proto__: Object
 */

Person 是函數(shù)因此它擁有 constructor、__proto__prototype 屬性。

Person.prototype 是只是個(gè)普通對(duì)象,因此 Person.prototype 擁有 constructor、__proto__ 屬性。

obj-6.png

對(duì)于 peter 來說,它是個(gè)對(duì)象因此具有 constructor__proto__ 屬性。

// peter 對(duì)象的構(gòu)造函數(shù)就是 Person()
peter.constructor // ? Person() {...}

// peter 對(duì)象的 __proto__ 屬性指向 Person.prototype
peter.__proto__ == Person.prototype // true

可以清楚的看到對(duì)象和它的構(gòu)造函數(shù)之間的聯(lián)系,總結(jié)一下:

  1. 每個(gè)對(duì)象都是由其構(gòu)造函數(shù)生成,并且對(duì)象有個(gè) constructor 屬性指向構(gòu)造函數(shù);
  2. 每個(gè)對(duì)象都有個(gè)原型屬性 __proto__,指向其構(gòu)造函數(shù)的 prototype 屬性;
  3. 每個(gè)函數(shù)都有一個(gè) prototype 屬性用于充當(dāng)構(gòu)造函數(shù)時(shí)構(gòu)建對(duì)象的 __proto__ 屬性。

原型鏈

到此你也許會(huì)疑惑為什么要這樣設(shè)計(jì),這是因?yàn)?JavaScript 也是面向?qū)ο蟮恼Z言,它通過這樣的設(shè)計(jì)來構(gòu)建原型鏈以實(shí)現(xiàn)繼承。

在上面代碼的基礎(chǔ)上我們?cè)俾暶饕粋€(gè) Student 的構(gòu)造函數(shù)

function Student(name, score) {
    Person.call(this, name)
    this.score = score
}

// 只執(zhí)行上面的語句還不夠,需要通過這行代碼將它們產(chǎn)生鏈接也就是繼承關(guān)系
Student.prototype = Object.create(Person.prototype)

// 這段代碼是為了讓 Student.prototype 的構(gòu)造函數(shù)指向 Student 函數(shù),不指定的話會(huì)指向 Person 函數(shù)。(許多地方都沒有這一步,也可以不寫。這里為了保持 constructor 指向一致)
Student.prototype.constructor = Student

// 創(chuàng)建一個(gè)對(duì)象
var jim = new Student('Jim', 90)

分析下內(nèi)存布局

Student.prototype.constructor == Student // true
Student.prototype.__proto__ == Person.prototype // true 

jim.__proto__ == Student.prototype // true
jim.constructor == Student // true 
image

可以明顯的看到一個(gè)鏈條,一個(gè)靠 __proto__ 屬性串聯(lián)起來的鏈條,這就是所謂的原型鏈。

image

正是有了原型鏈當(dāng)我們?cè)L問一個(gè)對(duì)象的屬性時(shí),會(huì)先在基本屬性中查找,如果沒有,再沿著 __proto__ 這條鏈向上找。這樣就實(shí)現(xiàn)了繼承。

我們還可以發(fā)現(xiàn)這個(gè)鏈條在 Person.prototype 這里斷了,而且圖中有些屬性沒有標(biāo)注出來。別急,我們后面來把它慢慢補(bǔ)全。

Function 和 Object

我們先看下面的代碼

var obj = { a: 5, b: 10 }
var fun = function(){}

等價(jià)于

// var obj = { a: 5, b: 10 }
var obj = new Object()
obj.a = 5
obj.b = 10
typeof Object // function

// var fun = function(){} 
var fun = new Function()
typeof Function // function

由此可見 Object 和 Function 是兩個(gè)比較總要的函數(shù)對(duì)象。我們來探究下它們的內(nèi)存布局。

因?yàn)樗鼈兌际菍?duì)象因此它們都有 constructor、__proto__ 屬性,又因?yàn)樗麄兪呛瘮?shù)對(duì)象,因此它們都有 prototype 屬性。

image

對(duì)于 Function 來說

// 雖然 Function.prototype 返回的類型是 function 但是它的 prototype 屬性并不存在,因此它是特殊的函數(shù)對(duì)象。
typeof Function.prototype // "function"
typeof Function.prototype.prototype // "undefined"

// Function.prototype 與 Function.__proto__ 指向同一個(gè)對(duì)象
Function.prototype == Function.__proto__

// Function.prototype 的 constructor 是 Function 函數(shù)
Function.prototype.constructor // ? Function() { [native code] }

// Function 的 constructor 是 Function 函數(shù)自己
Function.constructor // ? Function() { [native code] }

可以畫出下面的圖

obj-3.png

對(duì)于 Object 來說

// Object.prototype 是一個(gè)對(duì)象
typeof Object.prototype // "object"

// Object.__proto__ 與 Object.prototype 指向的不是同一個(gè)對(duì)象
Object.prototype == Object.__proto__ // false
// Object.__proto__ 指向 Function.prototype 的對(duì)象 
Object.__proto__ == Function.prototype // true

// Object.prototype 的 constructor 是 Object 函數(shù)
Object.prototype.constructor // ? Object() { [native code] }

// Object 的 constructor 是 Function 函數(shù)
Object.constructor // ? Function() { [native code] }

可以畫出下面的圖

image

我們發(fā)現(xiàn)還有兩個(gè)屬性沒確定分別是 Object.prototype.__proto__Function.prototype.__proto__。我們通過代碼在確認(rèn)一下。

// Function.prototype 的 __proto__ 屬性指向 Object.prototype
Function.prototype.__proto__ == Object.prototype // true

// Object.prototype 的 __proto__  屬性指向 null
Object.prototype.__proto__ // null

最終得到這個(gè)圖

image

我們?cè)俳Y(jié)合上面原型鏈那部分的內(nèi)容。

// Person 函數(shù)的構(gòu)造函數(shù)是 Function
Person.constructor // ? Function() { [native code] }

// Person 函數(shù)的原型是 Function.prototype
Person.__proto__ // ? () { [native code] }
Person.__proto__ == Function.prototype // true

// Student 函數(shù)的構(gòu)造函數(shù)是 Function
Student.constructor // ? Function() { [native code] }

// Student 函數(shù)的原型是 Function.prototype
Student.__proto__ // ? () { [native code] }
Student.__proto__ == Function.prototype // true

// Person.prototype 的原型是 Object.prototype
Person.prototype.__proto__ == Object.prototype
image

我們可以觀察到幾條明顯的原型鏈,見下圖:

  1. 綠色的是我們自定義的 Person Student 對(duì)象的原型鏈;
  2. 其他顏色的是 函數(shù)對(duì)象的原型鏈。

分析這張圖我們可以得出以下結(jié)論

  1. 所有函數(shù)的原型都是 Function.prototype,包括 Function 函數(shù)自己
  2. 所有函數(shù)的構(gòu)造函數(shù)都是 Function,包括 Function 函數(shù)自己
  3. 所有對(duì)象的原型終點(diǎn)都是 Object.prototype,包括函數(shù)對(duì)象和普通對(duì)象,而 Object.prototype.__proto__ 的原型指向了 null

這里面有個(gè)最初讓我比較疑惑的就是 Object 的原型為什么不是 Object.prototype 而是 Function.prototype

Object.__proto__ == Function.prototype // true

其實(shí)這是因?yàn)?Object 本身就是個(gè)函數(shù),它跟其他函數(shù)一樣都是由 Function 來構(gòu)造的。

這里還有張大佬的圖片,相信你可以很清楚的跟上面的圖片對(duì)應(yīng)上。

image

與原型鏈相關(guān)的方法

instanceof

instanceof 主要的作用就是判斷一個(gè)實(shí)例是否屬于某種類型,實(shí)現(xiàn)原理就是通過原型鏈進(jìn)行判斷。

function new_instance_of(leftVaule, rightVaule) { 
    let rightProto = rightVaule.prototype; // 取右表達(dá)式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表達(dá)式的__proto__值
    while (true) {
        if (leftVaule === null) {
            return false;   
        }
        if (leftVaule === rightProto) {
            return true;    
        } 
        leftVaule = leftVaule.__proto__ 
    }
}

可以看出來 instanceof 的實(shí)現(xiàn)思路就是判斷右值變量的 prototype 是否在左值變量的原型鏈上。

jim instanceof Person // true
jim instanceof Student // true

參考上方的圖我們也可以解釋一些看起來比較詭異的判斷

Object instanceof Object // true
image
Function instanceof Function // true
image
Function instanceof Object // true
image

下面這些你可以自行檢測(cè)。

function Foo() { } // 定義一個(gè)函數(shù)

Foo instanceof Object // true
Foo instanceof Function // true

hasOwnProperty

Object.hasOwnProperty() 返回一個(gè)布爾值,表示某個(gè)對(duì)象的實(shí)例是否含有指定的屬性,而且此屬性非原型鏈繼承。用來判斷屬性是來自實(shí)例屬性還是原型屬性。類似還有 in 操作符,in 操作符只要屬性存在,不管實(shí)在實(shí)例中還是原型中,就會(huì)返回 true。同時(shí)使用 in 和 hasOwnProperty 就可以判斷屬性是在原型中還是在實(shí)例中。

isPrototypeOf

返回一個(gè)布爾值,表示指定的對(duì)象是否在本對(duì)象的原型鏈中。

getPrototypeOf

返回該對(duì)象的原型。

創(chuàng)建對(duì)象和生成原型鏈

上面已經(jīng)提到了創(chuàng)建對(duì)象以及實(shí)現(xiàn)繼承的部分方法,其實(shí)還有其他很多的方法來創(chuàng)建和生成原型鏈以實(shí)現(xiàn)繼承。

使用語法結(jié)構(gòu)創(chuàng)建對(duì)象

var o = {a: 1}

o 這個(gè)對(duì)象繼承了 Object.prototype 上面的所有屬性

原型鏈: o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"]

數(shù)組都繼承于 Array.prototype

原型鏈: a ---> Array.prototype ---> Object.prototype ---> null

function f(){ return 2 }

函數(shù)都繼承于 Function.prototype

原型鏈: f ---> Function.prototype ---> Object.prototype ---> null

使用構(gòu)造器創(chuàng)建對(duì)象

function Person(name) {
    this.name = name
}
var peter = new Person('Peter')

構(gòu)造器創(chuàng)建的對(duì)象繼承了對(duì)應(yīng)構(gòu)造函數(shù)的 prototype 屬性

原型鏈: peter ---> peter.prototype ---> Object.prototype ---> null

其實(shí)上面使用語法結(jié)構(gòu)創(chuàng)建對(duì)象本質(zhì)上也是調(diào)用相應(yīng)的構(gòu)造器。

使用 Object.create 創(chuàng)建的對(duì)象

ECMAScript 5 中引入了一個(gè)的新方法:Object.create()。可以調(diào)用這個(gè)方法來創(chuàng)建一個(gè)新對(duì)象。新對(duì)象的原型就是調(diào)用 create 方法時(shí)傳入的第一個(gè)參數(shù)。

var a = {a: 1}; 
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (繼承而來)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因?yàn)閐沒有繼承Object.prototype

上面的創(chuàng)建方法都節(jié)選自 MDN 繼承與原型鏈。其實(shí)還有其他很多的方法來實(shí)現(xiàn)創(chuàng)建對(duì)象和繼承,但是萬變不離其宗。他們的本質(zhì)就是保證原型鏈的正確構(gòu)建就可以了。

class

通過上面的學(xué)習(xí)我們知道了很多創(chuàng)建對(duì)象和實(shí)現(xiàn)繼承的方式,但是這些方式都比較繁瑣,需要使用者自己保證原型鏈的正確構(gòu)建。尤其是對(duì)于熟悉基于類面向?qū)ο笳Z言的同學(xué)來說感覺這種方式比較怪異。

其實(shí)在 ES6 出現(xiàn)之前,就有很多框架來模擬類,使得 JS 能基于類實(shí)現(xiàn)繼承。但是由于社區(qū)和碎片化原因趨于小眾。

直到 ES6 的新特性 class,這讓類的概念成為了語言一個(gè)基礎(chǔ)特性。

當(dāng)然它實(shí)質(zhì)上是 JavaScript 現(xiàn)有的基于原型的繼承的語法糖。類語法不會(huì)為 JavaScript 引入新的面向?qū)ο蟮睦^承模型。

定義 class

類實(shí)際上是個(gè)“特殊的函數(shù)”,與聲明一個(gè)函數(shù)類似,不過這里使用的是 class 關(guān)鍵字。

class Rectangle {
    // 屬性 
    color = 'Red'

    // constructor 構(gòu)造函數(shù)
    constructor(height, width) {
        this.height = height
        this.width = width
    }

    // Getter Setter
    get prop() {
        return 'getter'
    }
    set prop(value) {
        console.log('setter: ' + value)
    }

    // Method
    calcArea() {
        return this.height * this.width
    }

    static 
}

class Square extends Rectangle {
    // constructor 構(gòu)造函數(shù)
    constructor(edge) {
        super(edge, edge)
    }
}

let square = new Square(10)

構(gòu)造函數(shù)

constructor 方法是一個(gè)特殊的方法,這種方法用于創(chuàng)建和初始化一個(gè)由 class 創(chuàng)建的對(duì)象。它具有以下特性。

  1. 一個(gè)類只能擁有一個(gè)名為 constructor 的特殊方法。

    如果類包含多個(gè) constructor 的方法,則將拋出 一個(gè) SyntaxError 。

  2. 一個(gè)構(gòu)造函數(shù)可以使用 super 關(guān)鍵字來調(diào)用一個(gè)父類的構(gòu)造函數(shù)。

    比如我們定義的 Square 子類中就調(diào)用了父類的構(gòu)造方法。

    但是需要注意的一點(diǎn)是子類的構(gòu)造方法中 this 必須在調(diào)用父類構(gòu)造方法之后才能使用。這是因?yàn)槿绻诟割惙椒ㄖ罢{(diào)用這時(shí)候 this 還沒有被初始化。

  3. constructor 方法不用寫 return 它默認(rèn)返回實(shí)例對(duì)象(即this),當(dāng)然你也可以指定返回另外一個(gè)對(duì)象。

屬性

實(shí)例屬性

我們都知道 JavaScript 對(duì)象有兩類屬性:數(shù)據(jù)屬性和訪問器(getter/setter)屬性。

定義數(shù)據(jù)屬性有兩種方式:

  1. 在構(gòu)造函數(shù)里創(chuàng)建。
  2. 也可以定義在類的最頂層,與構(gòu)造函數(shù)同級(jí)的地方。
class Rectangle {
    // 實(shí)例屬性 1 
    color = 'Red'

    // constructor 構(gòu)造函數(shù)
    constructor(height, width) {
        // 實(shí)例屬性 2
        this.height = height
        this.width = width
    }
}

訪問器(getter/setter)屬性的定義跟之前類似。

    // Getter Setter
    get prop() {
        return 'getter'
    }
    set prop(value) {
        console.log('setter: ' + value)
    }

靜態(tài)屬性

靜態(tài)屬性指的是 Class 本身的屬性,,即Class.propName,而不是定義在實(shí)例對(duì)象上的屬性。

// 現(xiàn)在的寫法
class Foo {
  // ...
}
Foo.prop = 1;

// 提案的寫法
class Foo {
  static prop = 1;
}

目前只有個(gè)上面的方法來定義,下面的寫法還只是一個(gè)提案。

方法

類相當(dāng)于實(shí)例的原型,所有在類中定義的方法,都會(huì)被實(shí)例繼承,這些稱為實(shí)例方法。另外如果在一個(gè)方法前,加上 static 關(guān)鍵字,就表示該方法不會(huì)被實(shí)例繼承,而是直接通過類來調(diào)用,這就稱為“靜態(tài)方法”。

靜態(tài)方法

實(shí)例方法沒什么好說的,這里主要介紹下靜態(tài)方法。

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello

上面代碼中,靜態(tài)方法 bar 調(diào)用了 this.baz,注意,如果靜態(tài)方法包含this關(guān)鍵字,這個(gè)this指的是類,而不是實(shí)例。因此這里的 this 指的是 Foo 類,而不是 Foo 的實(shí)例,等同于調(diào)用Foo.baz。另外,從這個(gè)例子還可以看出,靜態(tài)方法可以與非靜態(tài)方法重名。

注意點(diǎn)

  1. 嚴(yán)格模式

    類聲明和類表達(dá)式的主體都執(zhí)行在嚴(yán)格模式下。比如,構(gòu)造函數(shù),靜態(tài)方法,實(shí)例方法,getter 和 setter 都在嚴(yán)格模式下執(zhí)行。

    只要你的代碼寫在類或模塊之中,就只有嚴(yán)格模式可用。考慮到未來所有的代碼,其實(shí)都是運(yùn)行在模塊之中,所以 ES6 實(shí)際上把整個(gè)語言升級(jí)到了嚴(yán)格模式。

  2. 不存在提升

    函數(shù)聲明和類聲明之間的一個(gè)重要區(qū)別是函數(shù)聲明會(huì)提升,類聲明不會(huì)。你首先需要聲明你的類,然后訪問它,否則代碼會(huì)拋出一個(gè) ReferenceError。

  3. this 的指向

    類的方法內(nèi)部如果含有 this,它默認(rèn)指向類的實(shí)例。但是,必須非常小心,一旦單獨(dú)使用該方法,很可能報(bào)錯(cuò)。

    class Logger {
        printName(name = 'there') {
            this.print(`Hello ${name}`);
        } 
    
        print(text) {
            console.log(text);
        }
    }
    
    const logger = new Logger();
    const { printName } = logger;
    printName(); // TypeError: Cannot read property 'print' of undefined
    

    printName 方法被提取出來單獨(dú)使用,this 會(huì)指向該方法運(yùn)行時(shí)所在的環(huán)境(由于 class 內(nèi)部是嚴(yán)格模式,所以 this 實(shí)際指向的是 undefined),從而導(dǎo)致找不到 print 方法而報(bào)錯(cuò)。

    可以通過在構(gòu)造方法中綁定 this 或者使用箭頭函數(shù)來解決

    class Logger {
        constructor() {
            this.printName = this.printName.bind(this);
        }
    }
    
    class Obj {
        constructor() {
            this.getThis = () => this;
        }
    }
    const myObj = new Obj();
    myObj.getThis() === myObj // true
    

總結(jié)

通過上面我們可以看出來 JavaScript 中對(duì)象獨(dú)有的特色就是:對(duì)象具有高度的動(dòng)態(tài)性。它是一個(gè)徹底的動(dòng)態(tài)語言。這讓我想到了另一個(gè)動(dòng)態(tài)語言 Objective-C。而且 OC 中的 NSObject class 和 meta class 之間的關(guān)系也類似于 JS 中 Function 和 Object 之間的關(guān)系。由此可見語言之間有些東西都是相通的,多學(xué)習(xí)一門語言也可以觸類旁通。??

參考

  1. JavaScript 中的構(gòu)造函數(shù)
  2. 全方位理解JavaScript面向?qū)ο?/a>
  3. 大話JavaScript對(duì)象
  4. 深入理解javascript原型和閉包(完結(jié))
  5. 淺談 instanceof 和 typeof 的實(shí)現(xiàn)原理
  6. MDN 繼承與原型鏈
  7. 極客時(shí)間:重學(xué)前端
  8. 阮一峰:ECMAScript 6 入門
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容