摘要:中有兩種屬性數(shù)據(jù)屬性和訪問器屬性數(shù)據(jù)屬性數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置。表示能否通過刪除屬性從而重新定義屬性可配置的能否修改屬性的特性能否把屬性修改為訪問器屬性。以上代碼在對(duì)外上定義了兩個(gè)數(shù)據(jù)屬性和和一個(gè)訪問器屬性。
屬性類型:
ECMA-262第5版在定義只有內(nèi)部才用的特性(attribute)時(shí),描述了屬性(property)的各種特征。ECMA-262定義這些特性是為了實(shí)現(xiàn)JavaScript引擎用的,因此在JavaScript中不能直接訪問它們。為了表示特性是內(nèi)部值,該規(guī)范把它們放在兩對(duì)兒方括號(hào)中,例如[[Enumerable]]。
ECMAScript中有兩種屬性:數(shù)據(jù)屬性和訪問器屬性
數(shù)據(jù)屬性:數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置。在這個(gè)位置可以讀取和寫入值。數(shù)據(jù)屬性有4個(gè)描述其行為的特性。
[[Configurable]]
表示能否通過delete刪除屬性從而重新定義屬性(可配置的)
能否修改屬性的特性
能否把屬性修改為訪問器屬性。
默認(rèn)值為true。
[[Enumerable]]
表示能否通過for-in循環(huán)遍歷屬性(可枚舉的)
默認(rèn)值為true
[[Writable]]
表示能否修改屬性的值。(可寫的)
默認(rèn)值為true
[[Value]]
包含這個(gè)屬性的數(shù)據(jù)值。
讀取屬性值的時(shí)候,從這個(gè)位置讀;
寫入屬性值的時(shí)候,把新值保存在這個(gè)位置。
默認(rèn)值為undefined
對(duì)于直接在對(duì)象上定義的屬性,它們的[[Configurable]]、[[Enumerable]]、[[Writable]]特性都被設(shè)置為true,而[[Value]]特性被設(shè)置為指定的值。例如:
var person = { name:"Nicholas" };
這里創(chuàng)建了一個(gè)名為name的屬性,為它指定的值是"Nicholas"。也就是說,[[Value]]特性將被設(shè)置為"Nicholas",而對(duì)這個(gè)值的任何修改都將反映在這個(gè)位置。
要修改屬性默認(rèn)的特性,必須使用ECMAScript5的Object.defineProperty()方法。這個(gè)方法接受三個(gè)參數(shù):屬性所在的對(duì)象、屬性的名字和一個(gè)描述符對(duì)象。其中,描述符(descriptor)對(duì)象的屬性必須是:confirgurable、enumerable、writable和value。設(shè)置其中的一或多個(gè)值,可以修改對(duì)應(yīng)的特性值。例如:
var person = {}; Object.defineProperty(person,"name",{ writable:false, value:"Nicholas" }); console.log(person.name); // "Nicholas" person.name = "Greg"; console.log(person.name); // "Nicholas"
創(chuàng)建了一個(gè)名為name的屬性,它的值"Nicholas"是只讀的。這個(gè)屬性的值是不可修改的,如果嘗試為它指定新值,則在非嚴(yán)格模式下,賦值操作將被忽略;在嚴(yán)格模式下,賦值操作會(huì)導(dǎo)致拋出錯(cuò)誤。
類似的規(guī)則也適用與不可配制的屬性。例如:
var person = {}; Object.defineProperty(person,"name",{ configurable:false, value:"Nicholas" }); console.log(person.name); // "Nicholas" delete person.name; console.log(person.name); // "Nicholas"
把configurable設(shè)置為false,表示不能從對(duì)象中刪除屬性。如果對(duì)這個(gè)屬性調(diào)用delete,則在非嚴(yán)格模式下什么也不會(huì)發(fā)生,而在嚴(yán)格模式下會(huì)導(dǎo)致錯(cuò)誤。
而且,一旦把屬性定義為不可配置的(false),就不能再把它變回可配置了。此時(shí),再調(diào)用Object.defineProperty()方法修改除writable之外的特性,都會(huì)導(dǎo)致錯(cuò)誤:
var person = {}; Object.defineProperty(person,"name",{ configurable:false, value:"Nicholas" }); // 拋出錯(cuò)誤 "Uncaught TypeError: Cannot redefine property: name" Object.defineProperty(person,"name",{ configurable:true, value:"Nicholas" });也就是說,可以多次調(diào)用Object.defineProperty()方法修改同一個(gè)屬性,但如果把configurable特性設(shè)置為false之后,就會(huì)有限制了。 調(diào)用Object.defineProperty()方法時(shí),如果不顯式指定,configurable、enumerable和writable特性的默認(rèn)值都是false。
多數(shù)情況下,可能都沒有必要利用Object.defineProperty()提供的這些高級(jí)功能。不過,理解這些概念對(duì)理解JavaScript對(duì)象卻非常有用。
兩種定義方法的描述符默認(rèn)情況Test: 訪問器屬性IE8是第一個(gè)實(shí)現(xiàn)Object.defineProperty()方法的瀏覽器版本。然而,這個(gè)版本的實(shí)現(xiàn)存在諸多限制:只能在DOM對(duì)象上使用這個(gè)方法,而且只能創(chuàng)建訪問器屬性。由于實(shí)現(xiàn)不徹底,建議不要在IE8中使用Object.defineProperty()方法。
訪問器屬性不包含數(shù)據(jù)值;它們包含一對(duì)兒getter和setter函數(shù)(不過,這兩個(gè)函數(shù)都不是必需的)。
在讀取訪問器屬時(shí),會(huì)調(diào)用getter函數(shù),這個(gè)函數(shù)負(fù)責(zé)返回有效的值;
在寫入訪問器屬性時(shí),會(huì)調(diào)用setter函數(shù)并傳入新值,這個(gè)函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù)。
訪問器屬性有4個(gè)特性:
[[Configurable]]
表示能否通過delete刪除屬性從而重新定義屬性
能否修改屬性的特性
或者能否把屬性修改為數(shù)據(jù)屬性
對(duì)于直接在對(duì)象上定義的屬性,這個(gè)特性的默認(rèn)值為true
[[Enumerable]]
表示能否通過for-in循環(huán)遍歷屬性
對(duì)于直接在對(duì)象上定義的屬性,這個(gè)特性的默認(rèn)值為true。
[[Get]]
在讀取屬性時(shí)調(diào)用的函數(shù)
默認(rèn)值為undefined
[[Set]]
寫入屬性時(shí)調(diào)用的函數(shù)
默認(rèn)值為undefined
訪問器屬性不能直接定義,必須使用Object.defineProperty()來定義。
var book = { _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function () { return this._year; }, set:function (newValue) { if ((newValue > 2004)) { this._year = newValue; this.edition += newValue - 2004; } } }) book.year = 2005; alert(book.edition); // 2
創(chuàng)建一個(gè)book對(duì)象,并給它定義兩個(gè)默認(rèn)的屬性:_year、edition。_year前面的下劃線是一種常用的記號(hào),用于表示只能通過對(duì)象方法訪問的屬性。而訪問器屬性year則包含一個(gè)getter函數(shù)和一個(gè)setter函數(shù)。
getter函數(shù)返回_year的值
setter函數(shù)通過計(jì)算來確定正確的版本
因此,把year屬性修改為2005會(huì)導(dǎo)致_year變成2005,而edition變?yōu)?strong>2。這是使用訪問器屬性的常見方式,即設(shè)置一個(gè)屬性的值會(huì)導(dǎo)致其他屬性發(fā)生變化。
不一定非要同時(shí)指定getter和setter。只指定getter意味著屬性是不能寫,嘗試寫入屬性會(huì)被忽略。在嚴(yán)格模式下,嘗試寫入只指定了getter函數(shù)的的屬性會(huì)拋出錯(cuò)誤。類似地,沒有指定setter函數(shù)的屬性也不能讀,否則在非嚴(yán)格模式下會(huì)返回undefined,在嚴(yán)格模式下會(huì)拋出錯(cuò)誤。
// 下劃線表示只能通過對(duì)象方法訪問的屬性 var obj = { _x:"obj._x", _y:"obj._y", _z:"obj._z" }; // Object.defineProperty(obj,"x",{ // x屬性,只讀不能寫 get:function () { return this._x; } }); console.log(obj.x); // "obj._x" 可以讀取,調(diào)用obj.x實(shí)際上調(diào)用了obj._x的getter函數(shù) obj.x = "Rewrite x"; // 嘗試修改x屬性 console.log(obj.x); // "obj._x" 寫入失敗 // Object.defineProperty(obj,"y",{ // y屬性,只寫不能讀 set:function (newValue) { this._y = newValue; } }); console.log(obj.y); // "undefined" 讀取失敗 obj.y = "Rewrite obj.y"; // 嘗試修改屬性 console.log(obj._y); // "Rewrite obj.y" 可以寫入 // Object.defineProperty(obj,"z",{ // z屬性可讀可寫 get:function () { return this._z; }, set:function (newValue) { this._z = newValue; } }); console.log(obj.z); // "obj._z" obj.z = "Rewrite obj.z"; // 修改z屬性 console.log(obj._z); // "Rewrite obj._z"
支持ES5這個(gè)方法的瀏覽器有IE9+(IE8部分實(shí)現(xiàn))、Firefox4+、Safari5+、Opera12+和Chrome。在這個(gè)方法之前,創(chuàng)建訪問器屬性一般使用兩個(gè)標(biāo)準(zhǔn)方法:_defineGetter_()和_defineSetter_()。這兩個(gè)方法最初是由Firefox引入的,后來Safari3、Chrome1He Opera9.5也給出了相同的實(shí)現(xiàn)。使用這兩個(gè)遺留的方法,可以像下面這樣重寫前面的例子。
var book = { _year:2004, edition:1 }; // 定義訪問器的舊有方法 book.__defineGetter__("year",function(){ return this._year; }); book.__defineSetter__("year",function(newValue){ if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } }); book.year = 2005; console.log(book.edition); // 2
在不支持Object.defineProperty()方法的瀏覽器中不能修改[[Configurable]]和[[Enumerable]]。
定義多個(gè)屬性Object.definePropertys()方法。var book = {}; Object.defineProperties(book,{ _year:{ value:2004 }, edition:{ value:1 }, year:{ get:function () { return this._year; }, set:function (newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } }, });
以上代碼在book對(duì)外上定義了兩個(gè)數(shù)據(jù)屬性(_year和edition)和一個(gè)訪問器屬性(year)。最終的對(duì)象與上一節(jié)中定義的對(duì)象相同。唯一區(qū)別是這里的屬性都是在同一時(shí)間創(chuàng)建的。
讀取屬性的特性Object.getOwnPropertyDescriptor()使用ES5的Object.getOwnPropertyDescriptor()方法,可以取得給定屬性的描述符,這個(gè)方法接收兩個(gè)參數(shù):屬性所在的對(duì)象和要讀取其描述符的屬性名稱。返回值是一個(gè)對(duì)象,
如果是訪問器屬性,這個(gè)對(duì)象的屬性有configurable、enumerable、get和set;
如果是數(shù)據(jù)屬性,這個(gè)對(duì)象的屬性有configurable、enumerable、writable、value。
var book = {}; Object.defineProperties(book,{ _year:{ value:2004 }, edition:{ value:1 }, year:{ get:function () { return this._year; }, set:function (newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } } }); var descriptor = Object.getOwnPropertyDescriptor(book,"_year"); alert(descriptor.value); // 2004 alert(descriptor.configurable); // false alert(typeof descriptor.get); // "undefined" var descriptor = Object.getOwnPropertyDescriptor(book,"year"); alert(descriptor.value); // "undefined" alert(descriptor.enumerable); // false alert(descriptor.get); // "function"
對(duì)于數(shù)據(jù)屬性_year,value等于最初的值,configurable是false,而get等與undefined。
對(duì)于訪問器屬性year,value等于undefined,enumerable是false,而get是一個(gè)指向getter函數(shù)的指針。
Objecct.create方法的第二個(gè)參數(shù)是一個(gè)關(guān)聯(lián)數(shù)組,其鍵為屬性名,其值為屬性描述符(屬性對(duì)象)。
下面是個(gè)具體例子。其中屬性值是通過value屬性指定的。大部分屬性默認(rèn)值是false,這個(gè)例子中會(huì)將它們顯式地指定為true。
var obj = {x:2,y:3}; // 與下面代碼等價(jià) var obj = Object.create(Object.prototype,{ x:{ value:2, writable:true, enumerable:true, configurable:true }, y:{ value:3, writable:true, enumerable:true, configurable:true } });
可以通過Object.create來顯式地指定屬性的屬性。
只要將get屬性與set屬性指定為相應(yīng)的函數(shù),就能夠定義一個(gè)只能夠通過訪問器getter和setter來訪問值的屬性。訪問器與value屬性是互相排斥的,也就是說,如果指定了value屬性的值,訪問器(同時(shí)包括get和set)就會(huì)失效;反之,如果指定了訪問器(get或set中的某一個(gè)),value屬性就會(huì)失效。
(get、set)與value互斥從內(nèi)部來看,將屬性作為右值訪問時(shí)使用的是getter函數(shù),而將屬性作為左值進(jìn)行賦值時(shí)使用的是setter函數(shù)。
只要能寫出正確的getter訪問器函數(shù),就能依以此為基礎(chǔ)設(shè)計(jì)出一個(gè)不可變對(duì)象。
var obj = Object.create(Object.prototype,{ x:{ get:function () { alert("get called"); }, set:function () { alert("set called"); } } }); alert(obj.x); // "get called" 當(dāng)要讀取屬性時(shí),將會(huì)調(diào)用getter函數(shù) // undefined 由于getter函數(shù)沒有返回值,默認(rèn)返回undefined obj.x = 1; // "set called" 當(dāng)要寫入屬性值時(shí),將調(diào)用setter函數(shù) alert(obj.x); //"get called" 驗(yàn)證了setter函數(shù)并沒有修改屬性,依舊是原屬性 // undefined 調(diào)用getter函數(shù),沒有指定返回值,默認(rèn)返回undefined
訪問器函數(shù)也可以通過對(duì)象字面量來表述。下面代碼中的對(duì)象與上圖是一樣的。
var obj = { get x() { alert("get called"); }, set x(v) { alert("set called"); } };
getter函數(shù)與setter函數(shù)中的this引用指向的是當(dāng)前對(duì)象,不過下面這樣的代碼是無法運(yùn)行的。這是因?yàn)槠渲械拿總€(gè)訪問器函數(shù)都會(huì)不斷調(diào)用訪問器函數(shù)。(??。?/p>
// 無法運(yùn)行,有無限loop的致命錯(cuò)誤 var obj = Object.create(Object.prototype,{ x:{ get:function () {return this.x}, set:function (v) {this.x = v} } }); alert(obj.x); // 讀取obj.x,隱式調(diào)用了x屬性的getter函數(shù),函數(shù)的this引用指向obj,則this.x等價(jià)于調(diào)用obj.x。出現(xiàn)循環(huán)錯(cuò)誤
下面的代碼雖然能夠運(yùn)行,但還是有些問題
// 使用了隱藏的屬性_x的訪問器的例子(姑且算是能夠運(yùn)行),x作為訪問器屬性 var obj = Object.create(Object.prototype,{ x:{ get:function () {return this._x}, set:function (v) {return this._x = v}, x:{writable:true} } }); obj._x = "obj._x"; alert(obj.x); // obj.x obj.x = "Rewrite obj._x"; alert(obj.x); // "Rewrite obj._x"
這段代碼的問題指出在于屬性_x是可以從外部改寫的(不符合訪問器屬性規(guī)范)
不借助與規(guī)范的更恰當(dāng)?shù)姆椒ㄊ峭ㄟ^閉包來隱藏這個(gè)變量。
function createObject() { var _x = 0; // 變量名也可以用x,不過容易產(chǎn)生混亂,所以這里仍使用_x // 返回了一個(gè)定義了訪問器的對(duì)象 return { get x() {return _x}, set x(v) {_x = v} }; }; var obj = createObject(); // 生成對(duì)象 alert(obj.x); // 讀取(在內(nèi)部調(diào)用getter) // 0 obj.x = 1; // 改寫(在內(nèi)部調(diào)用setter) alert(obj.x); // 1
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/83419.html
摘要:高程讀書筆記第六章理解對(duì)象創(chuàng)建自定義對(duì)象的方式有創(chuàng)建一個(gè)實(shí)例,然后為它添加屬性和方法。創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對(duì)象默認(rèn)只會(huì)取得屬性至于其他方法都是從繼承而來的。 JS高程讀書筆記--第六章 理解對(duì)象 創(chuàng)建自定義對(duì)象的方式有創(chuàng)建一個(gè)Object實(shí)例,然后為它添加屬性和方法。還可用創(chuàng)建對(duì)象字面量的方式 屬性類型 ECMAScript在定義只有內(nèi)部采用的特性時(shí),描述了屬性的各種特征...
摘要:與執(zhí)行環(huán)境相關(guān)的變量對(duì)象中有執(zhí)行環(huán)境定義的所有變量和函數(shù)作用域鏈代碼在一個(gè)環(huán)境中執(zhí)行,便會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈。 執(zhí)行環(huán)境 執(zhí)行環(huán)境是什么? javascript的解釋器每次開始執(zhí)行一個(gè)函數(shù)時(shí),都會(huì)為每個(gè)函數(shù)創(chuàng)建一個(gè)執(zhí)行環(huán)境(execution context)。 執(zhí)行環(huán)境定義了變量或者函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了他們各自的行為。 與執(zhí)行環(huán)境相關(guān)的變量對(duì)象(...
摘要:高程讀書筆記第五章類型創(chuàng)建實(shí)例的方式有兩種。第一種是使用操作符后跟構(gòu)造函數(shù),另一種方式是使用對(duì)象字面量表示法。 JS高程讀書筆記--第五章 Object類型 創(chuàng)建Object實(shí)例的方式有兩種。第一種是使用new操作符后跟Object構(gòu)造函數(shù),另一種方式是使用對(duì)象字面量表示法。 在通過對(duì)象字面量定義對(duì)象時(shí),實(shí)際上不會(huì)調(diào)用Object構(gòu)造函數(shù) 訪問對(duì)象屬性時(shí)可以使用點(diǎn)表示法和方括號(hào)表示法。...
摘要:高程第六章繼承理解與實(shí)踐昨日細(xì)細(xì)的讀了一遍高程現(xiàn)在寫篇文章來鞏固下認(rèn)知吧讀首先是從中讀到了什么我自己也在讀書的時(shí)候用筆記下了各個(gè)部分的點(diǎn)現(xiàn)在等于閱讀筆記回憶下書本理解基礎(chǔ)第五版中規(guī)定了兩種屬性數(shù)據(jù)屬性訪問器屬性數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位 JavaScript高程第六章:繼承-理解與實(shí)踐昨日細(xì)細(xì)的讀了一遍JavaScript高程,現(xiàn)在寫篇文章來鞏固下認(rèn)知吧. 讀 首先是從中讀到了什么,我...
摘要:創(chuàng)建一個(gè)新對(duì)象將構(gòu)造函數(shù)的作用域賦給新對(duì)象因此就指向了這個(gè)新對(duì)象執(zhí)行構(gòu)造函數(shù)中的代碼為這個(gè)新對(duì)象添加屬性返回新對(duì)象。 本章內(nèi)容 理解對(duì)象屬性 理解并創(chuàng)建對(duì)象 理解繼承 ECMA-262把對(duì)象定義為:無序?qū)傩缘募?,其屬性可以包含基本值、?duì)象或者函數(shù) 理解對(duì)象 創(chuàng)建對(duì)象 創(chuàng)建自定義對(duì)象的最簡(jiǎn)單方式就是創(chuàng)建一個(gè)Object的實(shí)例,再為它添加屬性和方法。 var person = new...
閱讀 2155·2023-04-26 00:23
閱讀 830·2021-09-08 09:45
閱讀 2451·2019-08-28 18:20
閱讀 2559·2019-08-26 13:51
閱讀 1612·2019-08-26 10:32
閱讀 1406·2019-08-26 10:24
閱讀 2043·2019-08-26 10:23
閱讀 2210·2019-08-23 18:10