詳解JavaScript中的屬性和特性
JavaScript中屬性和特性是完全不同的兩個概念,這裡我將根據自己所學,來深入理解JavaScript中的屬性和特性。
主要內容如下:
理解JavaScript中物件的本質、物件與類別的關係、物件與引用類型的關係
物件屬性如何進行分類
屬性中特性的理解
第一部分:理解Java物件的本質、物件與類別的關係、物件與參考類型的關係
物件的本質:ECMA-262把物件定義為:無序屬性的集合,其屬性可以包含基本值、物件或函數。即物件是一組沒有特定順序的值,物件的每個屬性或方法都有一個名字,而這個名字都映射到一個值。故物件的本質是一個散列表:其中是一組名值對,值可以是資料或函數。
物件和類別的關係:在JavaScript中,物件和類別沒有任何關係。這是因為ECMAScript中根本沒有類別的概念,它的物件與其他基於類別的語言中的物件是不同的。
物件和引用類型的關係:物件和引用類型並不是等價的,因為每個物件都是基於一個引用類型建立的。
第二部分:對象屬性如何進行分類
由構造函數或對象字面量方法創建的對像中具有屬性和方法(只要提到屬性和方法,它們一定是屬於對象的;只要提到對象,它一定是具有屬性和方法的(自訂除外)),其中屬性又可分為資料屬性和存取器屬性,他們的區別如下:
資料屬性一般用於儲存資料數值,存取器屬性不包含資料值
訪問器屬性多用於get/set操作
第三部分:屬性中特性的理解
ECMAScript為了描述物件屬性(property)的各種特徵,定義了特性(attribute)這個概念。也就是說特性不同於屬性,特性是為了描述屬性的。下面,我將分別講解:
資料屬性及其特性
存取器屬性及其特性
如何利用Object.defineProperties()方法定義多個特性
如何利用Object.getOwnPropertyDescripter()方法讀取屬性的描述符以讀取屬性的特性
1.資料屬性及其特性
剛剛我們說過,資料屬性是用於儲存資料數值的,因此資料屬性具有一個資料值的位置,在這個位置可以讀取和寫入值。資料屬性有4個描述其行為的特性,由於ECMAScript規定:在JavaScript中不能直接存取屬性的特性(注意:不是不能存取),所以我們把它放在兩組方括號中。如下:
[[Configurable]]:預設值為true,a、表示能否透過delete刪除屬性從而重新定義屬性 b、能否修改屬性的特性 c、能夠把屬性由資料屬性修改為存取器屬性
[[Enumerable]]:預設值為true,表示能否透過for-in循環回傳該屬性(所以:如果為false,那麼for-in循環沒法枚舉它所在的屬性)
[[Writable]]:預設值為true,表示能否修改屬性的值,這是與[[Configurable]]不同之處。
[[Value]]:預設值為undefined,這個值即為屬性的屬性值,我們可以在這個位置上讀取屬性值,也可以在這個位置上寫入屬性值。
注意:上述的預設是指透過建構函式或物件字面量所建立的物件所自行擁有的屬性,而不是下面要介紹的Object.defineProperty()方法
這些特性都具有預設值,但是如果這些預設數值不是我們想要的,該怎麼辦呢?當然就是修改啦!我們可以透過Object.defineProperty()方法來修改屬性預設的特性。英文difineProperty為定義屬性的意思。這個方法接收三個參數:屬性所在的物件、屬性的名字、一個描述符物件。其中第三個參數描述符物件是物件字面量的方法所建立的,裡面的屬性和屬性值實際上保存的是要修改的特性和特性值。
下面透過幾個例子來深入理解。
a
var person={}; Object.defineProperty(person,"name",{ writable:false, value:"zhuzhenwei" }); console.log(person.name);//zhuzhenwei person.name="heting"; console.log(person.name);//zhuzhenwei
這裡我用對象字面量的方法創建了一個對象,但是沒有同時創建方法和屬性。而是利用了Object.defineProperty()方法來創建了屬性和修改了預設值。這裡將writable設定為false,於是後面我試著修改person.name時,是無效的。
b
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei" }); console.log(person.name);//zhuzhenwei person.name="heting"; console.log(person.name);//zhuzhenwei
注意看這個例子,這個例子中我刪去了writable:false,為什麼還是不能修改呢?這是因為之前我在介紹特性時,前三個預設為ture,是在建立物件並建立屬性的情況下得到的。對於透過呼叫Object.defineProperty()方法所建立的屬性,其前三個特性的預設值均為false,這裡需要注意。
c
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:false }); console.log(person.name);//zhuzhenwei delete person.name; console.log(person.name);//zhuzhenwei
這裡我們將新建的屬性name的特性設定為了configurable:false;因此下面刪除屬性的操作是無效的。根據b,可知configurable,預設就是false,即使去掉也不可修改。
d
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//zhuzhenwei delete person.name; console.log(person.name);//undefined
在这里我将默认的configurable的值由默认的false修改为了true,于是变成了可配置的,那么最后就成功删除了。
e
var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:false }); console.log(person.name);//zhuzhenwei Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
如果之前已经设置成为了false,那么后面再改成true也是徒劳的,即:一旦把属性设置成为不可配置的,就不能再把它变回可配置了。
f
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…) var person={}; Object.defineProperty(person,"name",{ value:"zhuzhenwei", }); console.log(person.name);//zhuzhenwei Object.defineProperty(person,"name",{ value:"zhuzhenwei", configurable:true }); console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
这里可以说明,即使前一步我们不管默认的configurable:false,后面得到的仍是不可配置。于是,可以得出结论,为了可配置,必须在第一次调用Object.defineProperty()函数时就将默认的值修改为true。
2.访问器属性及其特性
之前提到,访问器属性不包含数据值,他们包含一对getter函数和setter函数(这两个函数不是必须的)。在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter函数并传入新值,这个函数负责决定如何处理数据。同样,由于不能通过JavaScript来直接访问得到访问器属性的特性,所以下面列出的特性将由[[]]括起来以作区分。
[[Configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由访问器属性修改为数据属性
[[Enumerable]]:默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)
[[Get]]:在读取属性时调用的函数。默认值为undefined 关键:特性可以是一个函数
[[Set]]: 在写入属性时调用的函数。默认值为undefined 关键:特性可以是一个函数 由于get和set函数也属于属性的特性,那么他们就有可能(说有可能是因为这两个函数也不是必须的)出现在Object.defineproperty的第三个参数描述符对象的属性中。
注意:1.相对于数据属性,我们发现访问器属性中没有writable特性和value特性。这是因为访问器属性不包含数据值,那么我们怎么当然就不可修改属性的值(用不到writable特性),更不用考虑value了。
2.访问器属性不能直接定义,必须是用Object.defineProperty()来定义。(通过这个规定我们就能准确地判断出访问器属性和数据属性了)
通过下面这个例子来深入理解:
var book={ _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function(){<br> return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } }); book.year=2005; console.log(book.edition);//2
几个需要深入理解的地方:
1.访问器属性不能直接定义,必须使用Object.defineProperty()来定义,且该属性具有set和ger特性,于是可以判断,_year和edition是数据属性,而year是访问器属性。
2.我们看到_year这个数据属性前面是以_(下划线)开头的,这个一种常用的记号,用于表示只能通过对象方法访问的属性。从上面的例子中可以看到get相当于描述符对象的一个方法,而_year正是在这个对象方法访问的属性。而edition既可以通过对象方法访问,也可以由对象直接访问。
3.book.year表示正在读取访问器属性,这时会调用get函数,并返回了2004这个有效的值。
4.book.year=2005表示写入访问器属性,这时会调用set函数并传入新值,即将2005传给newValue,这个函数决定如何处理数据。
5.这时使用访问器属性的常见方法-即设置一个属性的值会导致其他属性发生变化。
3.如何利用Object.defineProperties()方法定义多个特性
显然,一个对象不可能只具有一个属性,因此,定义多个属性的可能性很大,于是JavaScript提供了Object.defineProperties()方法解决这个问题。这个方法接收两个参数,第一个是要定义属性所在的对象,第二个是一个对象字面量方法创建的对象,对象的属性名即为要定义的特姓名,对象的属性值又是一个对象,这个对象里的属性名和属性值分别是特性名和特性值(这里不是很好理解,看例子即可)。
var book={}; Object.defineProperties(book,{ _year:{ writable:true, value:2004 }, edition:{ writable:true, value:1 }, year:{ get:function(){ return this._year; }, set:function(){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } } });
4.如何利用Object.getOwnPropertyDescripter()方法读取属性的描述符以读取属性的特性
我们可以使用Object.getOwnPropertyDescripter()方法来取得给定属性的描述符。getOwnPropertyDescripter即为取得自身属性描述符的意思。这个方法接收两个参数:属性所在的对象要要读取其描述符的属性名称。返回一个对象。
对于访问器属性而言,这个对象的属性有configurable、enumerable、get和set;
对于数据属性而言,这个对象的属性有configurable、enumerable、writable和value。
var book={}; Object.defineProperties(book,{ _year:{ value:2004 }, edition:{ value:1 }, year:{ get:function(){ return this._year; }, set:function(){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } } }); var descriptor=Object.getOwnPropertyDescriptor(book,"_year"); console.log(descriptor.value);//2004 console.log(descriptor.configurable);//false 因为通过Object.defineProperties()方法创建的属性的特性configurable enumerable都是false console.log(typeof descriptor.get);//undefined 注意:这是数据属性,是不具有get特性的 var descriptor=Object.getOwnPropertyDescriptor(book,"year"); console.log(descriptor.value);//undefined console.log(descriptor.enumerable);//false console.log(typeof descriptor.get);//function get虽然是属性的一个特性,但是它也是函数
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时想要关注更多的相关内容请关注PHP中文网(www.php.cn)!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

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

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

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

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

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

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

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

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。
