日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

写给Java开发者看的JavaScript对象机制

發布時間:2023/11/29 javascript 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 写给Java开发者看的JavaScript对象机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

幫助面向對象開發者理解關于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 // truefoo0.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.prototypeMethod0,prototypeMethod0在foo中不存在而在上層(即foo.__proto__中存在),并且這不是一個特殊屬性(如只讀)。

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

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

<!--更多屬性屏蔽的場景也不做贅述-->

小結

再溫習一遍這些定義:

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

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

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

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

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


參考

《JavaScript高級程序設計》

《你不知道的JavaScript》

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

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

總結

以上是生活随笔為你收集整理的写给Java开发者看的JavaScript对象机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。