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

資訊專欄INFORMATION COLUMN

寫給Java開發者看的JavaScript對象機制

Charles / 2192人閱讀

摘要:如果你已經對機制已有了解,但是由于兩者對象機制的巨大本質差異,對它和構造函數,實例對象的關系仍有疑惑,本文或許可以解答你的問題。所有的原型對象都會自動獲得一個屬性,這個屬性的值是指向原型所在的構造函數的指針。

幫助面向對象開發者理解關于JavaScript對象機制

本文是以一個熟悉OO語言的開發者視角,來解釋JavaScript中的對象。

對于不了解JavaScript 語言,尤其是習慣了OO語言的開發者來說,由于語法上些許的相似會讓人產生心理預期,JavaScript中的原型繼承機制和class語法糖是讓人迷惑的。

如果你已經對prototype機制已有了解,但是由于兩者對象機制的巨大(本質)差異,對它和構造函數,實例對象的關系仍有疑惑,本文或許可以解答你的問題。

我們看下面的代碼,可以看出和OO語言相比,語法上也有很大分別:

// 定義一個類
class Foo {
  constructor() {
    this.a = "a";
  }
}

//實例化對象
const foo = new Foo();

//定義原型的屬性
Foo.prototype.b = "b";

//實例可以訪問屬性
foo.b // "b"

//修改原型的屬性
Foo.prototype.b= "B";


//實例屬性值沒有被修改
foo.b // "b"

類已經定義了怎么還能修改呢?prototype又是什么?

不存在面向對象

對于熟悉了面向對象的開發者而言JS中種種非預期操作的存在,都是因為JavaScript中根本沒有面向對象的概念,只有對象,沒有類。

即使ES6新添了class語法,不意味著JS引入了面向對象,只是原型繼承的語法糖。

原型是什么

什么是原型?如果說類是面向對象語言中對象的模版,原型就是 JS中創造對象的模版。

在面向類的語言中,實例化類,就像用模具制作東西一樣。實例化一個類就意味著“把類的形態復制到物理對象中”,對于每一個新實例來說都會重復這個過程。

但是在JavaScript中,并沒有類似的復制機制。你不能創建一個類的多個實例,只能創建多個對象,它們[[Prototype]]關聯的是同一個對象。

//構造函數
function Foo(){
}
//在函數的原型上添加屬性
Foo.prototype.prototypeAttribute0 = {status: "initial"};

const foo0 = new Foo();
const foo1 = new Foo();
foo0.prototypeAttribute0 === foo1.prototypeAttribute0 //true
對象、構造函數和原型的關系

當我們創建一個新對象的時候,發生了什么,對象、構造函數和原型到底什么。

先簡單地概括:

原型用于定義共享的屬性和方法。

構造函數用于定義實例屬性和方法,僅負責創造對象,與對象不存在直接的引用關系。

我們先不用class語法糖,這樣便于讀者理解和暴露出他們之間真正的關系。

// 先創建一個構造函數 定義原型的屬性和方法
function Foo() {
    this.attribute0 = "attribute0";
}

當創建了一個函數,就會為該函數創建一個prototype屬性,它指向函數原型。

所有的原型對象都會自動獲得一個constructor屬性,這個屬性的值是指向原型所在的構造函數的指針。

現在定義原型的屬性和方法

Foo.prototype.prototypeMethod0 = function() {
    console.log("this is prototypeMethod0");
}

Foo.prototype.prototypeAttribute0 = "prototypeAttribute0";

好了,現在,新建一個對象,

const foo = new Foo();

foo.attribute0 // "attribute0"
foo.prototypeAttribute0 //"prototypeAttribute0"
foo.prototypeMethod0() // this is prototypeMethod0

它擁有自己的實例屬性attribute0,并且可以訪問在原型上定義的屬性和方法,他們之間的引用關系如圖所示。

當調用構造函數創建實例后,該實例的內部會包含一個指針(內部對象),指向構造函數的原型對象。

當讀取實例對象的屬性時,會在實例中先搜尋,沒有找到,就會去原型鏈中搜索,且總是會選擇原型鏈中最底層的屬性進行訪問。

對象的原型可以通過__proto__在chrome等瀏覽器上訪問。

__proto__是對象的原型指針,prototype是構造函數所對應的原型指針。

語法糖做了什么

ES6推出了class語法,為定義構造函數和原型增加了便利性和可讀性。

class Foo {
    constructor(){
        this.attribute0 = "attribute0";
    }

    prototypeMethod0(){
        console.log("this is prototypeMethod0")
    }
}

/* 相當于下面的聲明*/
function Foo() {
    this.attribute0 = "attribute0";
}

Foo.prototype.prototypeMethod0 = function() {
    console.log("this is prototypeMethod0")
}

class中的constractor相當于構造函數,而class中的方法相當于原型上的方法。、

值得注意的特性 屬性屏蔽 —— 避免實例對象無意修改原型

看這段代碼,思考輸出的結果。

class Foo {
    prototypeMethod0(){
        console.log("this is prototypeMethod0")
    }
}

const foo0 = new Foo();
const foo1 = new Foo();

foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // true

foo0.prototypeMethod0 = () => console.log("foo0 method");
foo0.prototypeMethod0(); //??
foo1.prototypeMethod0(); //??
foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // ??

輸出的結果是

foo0.prototypeMethod0(); // foo0 method
foo1.prototypeMethod0(); // this is prototypeMethod0
foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // false

我們知道對象(即便是原型對象),都是運行時的。

創建之初,foo本身沒有prototypeMethod0這個屬性,訪問foo0.prototypeMethod0將會讀取foo0.__proto__.prototypeMethod0

直接修改foo0.prototypeMethod0沒有改變__proto__上的方法原因是存在屬性屏蔽

現在的情況是:想要修改foo0.prototypeMethod0prototypeMethod0foo中不存在而在上層(即foo.__proto__中存在),并且這不是一個特殊屬性(如只讀)。

那么會在foo中添加一個新的屬性。

這便是為什么直接修改卻沒有影響__proto__的原因。

小結

再溫習一遍這些定義:

原型用于定義共享的屬性和方法。

構造函數用于定義實例屬性和方法,僅負責創造對象,與對象不存在直接的引用關系。

__proto__是對象的原型指針,prototype是構造函數的原型指針。

在解釋原型作用的文章或書籍中,我們會聽到繼承這樣的術語,其實更準確地,委托對于JavaScript中的對象模型來說,是一個更合適的術語。

委托行為意味著某些對象在找不到屬性或者方法引用時會把這個請求委托給另一個對象。對象之間的關系不是復制而是委托。

參考

《JavaScript高級程序設計》

《你不知道的JavaScript》

本文僅供解惑,要在腦袋里形成系統的概念,還是要看書呀。

有疑問歡迎大家一起討論。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108193.html

相關文章

  • Java開發

    摘要:大多數待遇豐厚的開發職位都要求開發者精通多線程技術并且有豐富的程序開發調試優化經驗,所以線程相關的問題在面試中經常會被提到。將對象編碼為字節流稱之為序列化,反之將字節流重建成對象稱之為反序列化。 JVM 內存溢出實例 - 實戰 JVM(二) 介紹 JVM 內存溢出產生情況分析 Java - 注解詳解 詳細介紹 Java 注解的使用,有利于學習編譯時注解 Java 程序員快速上手 Kot...

    LuDongWei 評論0 收藏0
  • 寫給小白看的JS異步

    摘要:主線程在啟動程序時被創建,用于執行函數。用戶自主創建的若干進程相對于主線程而言就是子線程。子線程和主線程都是獨立的運行單元,各自的執行互不影響,因此能夠并發執行。這就是的異步機制了。 某天突然寫了個方法要從后臺調用數據,顯示在前臺頁面,但是輸出結果總是空undefined,得不到數據。多方找資料才發現,原來是入了JS異步的坑。 我們常常聽到單線程、多線程、同步、異步這些概念,那么這些東...

    gghyoo 評論0 收藏0
  • 26自學轉行前端(寫給和1年前一樣迷茫的我的你)

    摘要:轉行前端有哪些疑慮在人生的抉擇處,尋求一些別人的經驗和總結,無可厚非,但是決定了就一定要堅定的走下去,謹慎是為了更好的堅持,而不是放棄的理由。寫在前面這里前后端指的是開發的前后端。 轉行前端有哪些疑慮? 在人生的抉擇處,尋求一些別人的經驗和總結,無可厚非,但是決定了就一定要堅定的走下去,謹慎是為了更好的堅持,而不是放棄的理由。寫在前面:這里前后端指的是web開發的前后端。1、前端崗位需...

    番茄西紅柿 評論0 收藏2637
  • 26自學轉行前端(寫給和1年前一樣迷茫的我的你)

    摘要:轉行前端有哪些疑慮在人生的抉擇處,尋求一些別人的經驗和總結,無可厚非,但是決定了就一定要堅定的走下去,謹慎是為了更好的堅持,而不是放棄的理由。寫在前面這里前后端指的是開發的前后端。 轉行前端有哪些疑慮? 在人生的抉擇處,尋求一些別人的經驗和總結,無可厚非,但是決定了就一定要堅定的走下去,謹慎是為了更好的堅持,而不是放棄的理由。寫在前面:這里前后端指的是web開發的前后端。1、前端崗位需...

    番茄西紅柿 評論0 收藏2577

發表評論

0條評論

Charles

|高級講師

TA的文章

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