可變對象和不可變對象


一、可變對象

js有7種基本數據類型:number、string、boolean、null、undefine、object、symbol(ES6新增),除了object為引用類型,其他均為基本類型

基本類型將值存在棧中

引用類型將地址存在棧中,值存在棧中地址指向的堆中

因此,我們如果copy基本類型,拷貝的是它的值,如果再單獨賦值也不會影響被拷貝的值。如果我們copy一個引用類型,其實拷貝的是它在堆中的地址,如果改變其中一方的值,就是改變了堆中的值,另一方因為指向同一個堆,所以它的值也被同步修改了。
所以對於引用類型(array、function、obj等)在拷貝時為了使兩個引用類型互不影響,一般我們不直接將舊的array賦值給新的array,而是生成新的引用類型變量,將舊的數組中的值一個一個的分別拷貝到新的array中,這就是所謂的深拷貝,但是如果數組中的值又是引用類型的話,還要把這個引用類型中的值再分別取出付給又新生成的引用對象中,如果嵌套層次太多的話,處理起來就容易出錯。

二、不可變對象

 

var obj=object.preventExtensions({});
//直接定義新的屬性會報錯
object.defineProperty(obj,'content',{
value:'hello'
});//TypeError: Cannot define property:p, object is not extensible.
//非嚴格模式下通過點符號添加不會報錯,但會靜默失敗,原對象仍然沒有content屬性
object.content='Hello';
object.content://undefine

 

Object.isExtensible()可以判斷一個對象是否可擴展,即是否可以添加新的屬性。參數是目標對象,返回布爾值,true代表可擴展,false不可擴展。

2.Object.seal()

Object.seal()可以使一個對象無法添加新屬性的同時也無法刪除舊屬性,參數是目標對象,返回修改后的對象。
其本質是通過修改對象的configurable(可設置)為false來實現的。在屬性描述對象里講到,configurable為false時,其他配置不可改變,writable只能true變false,且屬性無法被刪除。只要writable或configurable其中之一為true時,value是可變的,所以密封之后的對象還是可以改屬性值的。

 1 var obj={content:'hello'};
 2 object.getOwnPropertyDescriptor(obj,'content');
 3 //obj{
 4 //configurable: true
 5 //enumerable: true
 6 //value: "hello"
 7 //writable: true
 8 //}
 9 object.seal(obj);
10 object.getOwnPropertyDescriptor(obj,'content');//seal后configurable變為false
11 //obj{
12 //configurable: false
13 //enumerable: true
14 //value: "hello"
15 //writable: true
16 //}

對應的Object.isSealed()可以檢測一個對象是否密封,參數是目標對象,返回布爾值,true代表被密封,不可增刪屬性,false代表沒被密封可增刪屬性。

var obj=new object();
object.isExtensible(obj);//true
object.isSealed(obj)//false
object.seal(obj);
object.isExtensible(obj)//false,seal后對象isExtensible()也隨之改變
object.isSealed(obj);//true

3.Object.freeze()
Object.freeze()可以使一個對象不能再添加新屬性,也不可以刪除舊屬性,且不能修改屬性的值。參數是目標對象,返回修改后的對象。

var obj=object.freeze({name:'example'});
//直接定義新的屬性會報錯
object.defineProperty(obj,'content',{
value:'hello'
})//TypeError:cannot define property:p,object is not extensible.
//非嚴格模式下通過點符號添加不會報錯,但會靜默失敗,原對象仍然沒有content屬性
obj.content='hello';
obj.content;//undefined;
delect obj.name;//刪除失敗,返回false
obj.name='hello';
obj.name;//仍然是‘example’

對應的Object.isFrozen()可以檢測一個對象是否凍結,即是否可以增刪改。參數是目標對象,返回布爾值,true表示已經凍結不可再增刪改,false表示未凍結。

var obj=new object()
object.isExtensible(obj);//true
object.isSealed(obj);//false
object.isFrozen(obj);//false
object.freeze(obj);
object.isExtensible(obj);//false,freeze后對象的isExtensible()也隨之改變
object.isSealed(obj);//true,freeze后對象的isSealed()也隨之改變
object.isFrozen(obj);//true

注意
無論是不可擴展,密封,還是凍結,都是淺層控制的,即只對對象本身屬性的增刪改。如果對象屬性是一個引用類型,比如數組subArr或對象subObj等,雖然subArr、subObj的不可被刪改,但他們的屬性可以增刪改。

var obj=object.freeze({
content:{name:'example'}
});
obj.content=new object();
obj.content;//{name:'example'},content本身不可修改
obj.content.name='test';
obj.content;//{name:'test'}但content的屬性仍可改,因為凍結的是obj不是它的屬性

由於每個對象都有一個_proto_該屬性的值是該對象的原型對象,也是引用類型,由於凍結是淺層的,所以原型對象並不會被連着凍結,仍然可以通過給對象的原型對象加屬性達到給當前對象新增屬性的效果。所以如果想進一步凍結還需要把原型對象也凍結上。

var obj=object.freeze({});
obj.content='hello';
obj.content;//undefine,增加失敗

var proto=object.getPrototypeOf(obj);
proto.content='hello'//obj的圓形屬性為hello
obj.content;//obj本身沒有content這個屬性的話,向它的原型找這個屬性,‘hello’增加成功

object.freeze(proto);
proto.name="example";
obj.name//undefine,凍結原型之后增加失敗

object.defineProperty()
object.defineProperty()方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回這個對象。

語法    object.definePoperty(obj,prop,descriptor)

參數
obj  要在其上定義屬性的對象
prop  要定義或修改的屬性的名稱
descriptor  將被定義或修改的屬性描述符

返回值
被傳遞給函數的對象
在ES6中,由於Symbol類型的特殊性,用Symbol類型的值來做對象的key與常規的定義或修改不同,而object.defineProperty是定義key為Symbol的屬性的方法之一。

描述
該方法允許添加或修改對象的屬性。通過賦值操作添加的普通屬性是可枚舉的,能夠在屬性枚舉間呈現出來(for...in 或 object.keys方法),這些屬性的值可以被改變,也可以被刪除。這個方法允許修改默認的額外選項(或配置)。默認情況下,使用 Object.defineProperty() 添加的屬性值是不可修改的。

屬性描述符
對象里目前存在的屬性描述符有兩種主要形式:數據描述符和存取描述符。數據描述符是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一;不能同時是兩者。

數據描述符和存取描述符**均具有以下可選鍵值:
configurable
當且僅當該屬性的 configurable 為 true 時,該屬性描述符才能夠被改變,同時該屬性也能從對應的對象上被刪除。默認為 false。
enumerable
當且僅當該屬性的enumerable為true時,該屬性才能夠出現在對象的枚舉屬性中。默認為 false。
數據描述符同時具有以下可選鍵值:
value
該屬性對相應的值。可以是有效的JavaScript值(數值、對象、函數)默認undefine
writable
當且僅當屬性的writable為true時,value才能被賦值運算符改變,默認為false
存取描述符同時具有以下可選鍵值:
get(默認為undefined)
一個屬性提供getter方法,如果沒有getter則為undefined。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,但是會傳入this對象(由於繼承關系,這里的this並不一定是定義該屬性的對象)。
set(默認為undefined)
一個屬性提供setter的方法,如果沒有setter則為undefined。當屬性值修改時,觸發執行該方法。該方法將接收唯一參數,即該屬性新的參數值


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM