Object.freeze()
該方法可以凍結(jié)一個(gè)對(duì)象,凍結(jié)對(duì)象指的是不能向這個(gè)對(duì)象,添加屬性、刪除屬性、修改屬性、以及不能修改該對(duì)象已有屬性的可枚舉型、可寫性、可配置性,該方法返回被凍結(jié)的對(duì)象。
演示:
var json = {
name:'秦司令'
};
var obj = Object.freeze(json);
obj.age = 20; // 不生效
obj.name = 'abc'; //不生效
delete obj.name // 不生效
console.log(obj.name); //秦司令
console.log(obj.age) // undefined
上面代碼給大家展示了一下操作 就是防止對(duì)象的屬性被修改 , 對(duì)象屬性的可枚舉性、可寫性、可配置性、下面我們會(huì)說到。
Object.is()
Es5比較兩個(gè)值是否相等,只有兩個(gè)運(yùn)算符 ,一個(gè)等于運(yùn)算符( == ) , 一個(gè)嚴(yán)格等于運(yùn)算符( === ) 。它們都有缺點(diǎn),前者會(huì)自動(dòng)轉(zhuǎn)換類型 , 后者的NaN不等于自身,以及+0 等于 -0。javascript缺乏一種運(yùn)算,在所有環(huán)境中,你們的值是一樣的就應(yīng)該相等。
Es6提出同值相等算法,用來解決這個(gè)問題。
Object.is就是部署這個(gè)算法的新方法。它用來比較倆個(gè)值是否嚴(yán)格相等,與嚴(yán)格運(yùn)算符(===) 的行為基本一致。
演示:
Object.is('foo','foo') // true
Object.is({},{}) // false
上面代碼中兩個(gè)空對(duì)象不相等,因?yàn)樗鼈兊囊玫刂凡煌?br> 不同之處只有兩個(gè): 一是 +0 不等于 -0 , 二是 NaN 等于自身
// Es5寫法
+0 === -0 // true
NaN === NaN //false
//Es6提出的新算法方法
Object.is(+0,-0) //false
Object.is(NaN,NaN) // true
上面代碼Object.is Es6新提出的算法 讓 NaN 等于自身
Object.assign()
該方法用于對(duì)象合并,將源對(duì)象(source)的所有可枚舉屬性,復(fù)制到target目標(biāo)對(duì)象里。
演示:
var json = {a:1};
var source = {b:2};
Object.assign(json,source) // {a:1,b:2}
Object.assign方法的第一個(gè)參數(shù)是目標(biāo)對(duì)象,其它的都是源對(duì)象,該方法返回值返回第一個(gè)參數(shù)。
注意,如果目標(biāo)對(duì)象和源對(duì)象的屬性有重復(fù)的話,那么源對(duì)象的屬性覆蓋目標(biāo)對(duì)象的屬性。
var json = {a:1,b:1}
var source = {b:2,c:3}
Object.assign(json,source) // {a:1,b:2,c:3}
如果只有一個(gè)參數(shù),Object.assign會(huì)直接返回該參數(shù)
Object.assign({name:'秦司令'}) // {name:'秦司令'}
如果該參數(shù)不是對(duì)象,而先會(huì)轉(zhuǎn)換成對(duì)象。
Object.assign(2) // Number({})
由于undefined和null無法轉(zhuǎn)換成對(duì)象,所以如果它們作為參數(shù),就會(huì)報(bào)錯(cuò)。
Object.assign(undefined) //報(bào)錯(cuò)
Object.assign(null) //報(bào)錯(cuò)
當(dāng)然,如果undefined和null不在首參數(shù)就不會(huì)報(bào)錯(cuò),因?yàn)樗鼈儾辉谑讌?shù),處理方法就不同,如果無法轉(zhuǎn)換成對(duì)象就會(huì)跳過。
Object.assign({},123,{name:'秦司令'}) // {name:'秦司令'}
其它類型的值 數(shù)字,布爾,字符串,不在首參數(shù)內(nèi)也不會(huì)報(bào)錯(cuò) 。但是,除了字符串會(huì)以數(shù)組的形式,拷貝到目標(biāo)對(duì)象里,其它值都不行,只有字符串可以。
Object.assign({},'str') // {0:'s',1:'t',2:'r'}
Object.assign({},123) // {}
Object.assign({},true) // {}
上面代碼中,數(shù)值,布爾值,字符串 結(jié)果只有字符串被拷貝進(jìn)去,這是因?yàn)橹挥凶址邪b類對(duì)象,會(huì)產(chǎn)生可枚舉屬性。
請(qǐng)看下個(gè)列子
Object(true) //{ [[PrimitiveValue]]:true }
Object(123) // { [[PrimitiveValue]]:123 }
Object('str') // {0:'s',1:'t',2:'r',length:3, [[PrimitiveValue]]:'str' }
上面代碼看對(duì)象的返回值 [[PrimitiveValue]] 這個(gè)是原始屬性 assign 是無法拿到的,只有字符串有可枚舉屬性,所以上面只能拷貝字符串。
Object.assign拷貝屬性也是有限制的,只拷貝源對(duì)象的自身屬性(不包含繼承屬性),也不拷貝不可枚舉屬性(enumerable:false)。
Object.assign({name:'秦司令'},
Object.defineProperty({},'age',{
enumerable:false,
value:20
})
)
上面代碼中,Object.assign要拷貝這個(gè)對(duì)象屬性,但是沒有被拷貝進(jìn)去,因?yàn)檫@個(gè)屬性里面設(shè)置了不可枚舉屬性,enumerable:false。
屬性名為Symbol值的也會(huì)被拷貝進(jìn)去
Object.assign({a:'b'},{[Symbol()]:'c'}) // {a:'b',[Symbol()]:'c'}
注意點(diǎn)
淺拷貝
Object.assign方法實(shí)行的是淺拷貝,而不是深拷貝。也就是說,如果源對(duì)象某個(gè)屬性發(fā)生改變,那么Object.assign的目標(biāo)對(duì)象也跟著改變。
var json = {a:{b:1}}
var obj = Object.assign({},json)
json.a.b = 2
obj.a.b // 2
上面代碼發(fā)生引用的原因是,json.a的屬性值是一個(gè)對(duì)象,Object.assign拷貝到的是這個(gè)對(duì)象的引用,這個(gè)對(duì)象的任何變化,都會(huì)映射到目標(biāo)對(duì)象上
數(shù)組的處理
Object.assign用來處理數(shù)組,會(huì)把數(shù)組視為對(duì)象處理。
Object.assign([1,2,3],[4,5,6]) // [4,5,6]
上面代碼中,Object.assign把數(shù)組視為屬性名為 0 1 2的對(duì)象,因此源數(shù)組的0號(hào)屬性 4 覆蓋了 目標(biāo)屬性的 1 值 , 以此類推 最后全部覆蓋。
取值函數(shù)的處理
Object.assign只能進(jìn)行值的復(fù)制,如果是要復(fù)制一個(gè)取值函數(shù),那么將求值后再復(fù)制
const source = {
get foo(){
return 1
}
}
const target = {};
Object.assign(target,source) // {foo:1}
上面代碼中,source對(duì)象的foo屬性是一個(gè)取值函數(shù),Object.assign不會(huì)復(fù)制這個(gè)取值函數(shù),只會(huì)拿到值以后,將這個(gè)值復(fù)制過去
常見用途
Object.assign方法有很多好處。
一、為對(duì)象添加屬性
class Point{
constructor(x,y){
Object.assign(this,{x,y})
}
}
new Point(1,2)
上面方法通過Object.assign方法,將x屬性和y屬性添加到Point類的對(duì)象實(shí)例。
二、給對(duì)象添加方法
function Fn(){}
Object.assign(Fn.Prototype,{
add(){
}
})
new Fn()
上面代碼使用Object.assign方法給對(duì)象添加方法,這樣寫法簡潔表達(dá)式
三、克隆對(duì)象
function clone(obj){
return Object.assign({},obj)
}
上面代碼將原始對(duì)象拷貝到一個(gè)空對(duì)象,就得到了原始對(duì)象的克隆。
不過,采用這種方法克隆,只能克隆原始對(duì)象自身的值,不能克隆它繼承的值,如果想要克隆它的原型,請(qǐng)看下列代碼。
function clone(){
var proto = Object.getPrototypeOf(obj)
return Object.assign(Object.create(proto),obj)
}
// Object.create方法是操作__proto__
四、合并多個(gè)對(duì)象
將多個(gè)對(duì)象合并到某個(gè)對(duì)象
const mer = (target,...source) => Object.assign(target,...source)
//上面的寫法 相當(dāng)于下面
function Demo(target,...source){
return Object.assign(target,...source)
}
Object.getOwnPropertyDescriptors()
Es5的Object.getOwnPropertyDescriptor()方法會(huì)返回某個(gè)對(duì)象屬性的描述對(duì)象(descriptor) , Es2017引入了Object.getOwnPropertyDescriptors()方法,返回指定對(duì)象所有自身屬性(非繼承屬性)的描述對(duì)象。
const obj = {
foo:123,
get bar(){
return 'aaa'
}
}
Object.getOwnPropertyDescriptors(obj)
//{ foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
上面代碼中,Object.getOwnPropertyDescriptors()方法返回一個(gè)對(duì)象,所有原對(duì)象的屬性名都是該對(duì)象的屬性名,對(duì)應(yīng)的屬性值就是該對(duì)象的描述對(duì)象。
該方法的引入,主要是為了解決Object.assign不能正??截?set屬性 和 get 屬性的取值函數(shù)。
const source = {
set foo(value){
console.log(value)
}
}
var obj = Object.assign({},source);
Object.getOwnPropertyDescriptor(obj,'foo')
// { value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }
上面代碼中,source對(duì)象的foo屬性的值是一個(gè)賦值函數(shù),Object.assign方法將這個(gè)屬性拷貝給target1對(duì)象,結(jié)果該屬性的值變成了undefined,這是因?yàn)镺bject.assign方法總是拷貝一個(gè)屬性的值,而不會(huì)拷貝它背后的賦值方法或取值方法。
這時(shí),Object.getOwnPropertyDescriptors()方法配合Object.defineProperties方法,就可以實(shí)現(xiàn)正確拷貝。
const source = {
set foo(value){
console.log(value)
}
}
var target = {}
Object.defineProperties(target,Object.getOwnPropertyDescriptors(source))
Object.getOwnPropertyDescriptor(target,'foo')
上面的代碼,將屬性描述對(duì)象添加到目標(biāo)對(duì)象里面。
下面講解一下Object.defineProperties該方法
var json = {
get foo(){
return 123
}
};
Object.defineProperties({},Object.getOwnPropertyDescriptors(json)) ;
//上面的寫法,等同于下面的寫法
Object.defineProperties({},{
foo:{
get(){
return 123;
},
set(value){
return value;
}
}
});
上面代碼, json是一個(gè)對(duì)象,里面是一個(gè)取值函數(shù) ,然后下面通過對(duì)象的屬性描述方法給它添加到Object.defineProperties該方法里面。
另外,Object.getOwnPropertyDescriptors()方法的另一個(gè)用處,是配合Object.create() 方法,將對(duì)象屬性克隆到一個(gè)新對(duì)象,這是淺拷貝。
var obj = {
name:'秦司令'
};
Object.create(Object.getPrototypeOf(obj),Object.getOwnPropertyDescriptors(obj));
__ proto __ 屬性(前后各兩個(gè)下劃線),用來讀取或設(shè)置當(dāng)前對(duì)象的prototype對(duì)象。目前,所有瀏覽器(包括IE11) 都部署了這個(gè)屬性。
演示:
//Es5的寫法
function demo(){}
var obj = {
method:function(){}
}
obj.__ proto __ = demo
new demo()
// Es6的寫法
var obj = Object.create(demo)
上面代碼中,Es6的寫法Object.create該方法直接是設(shè)置__ proto __ 屬性的
該屬性沒有寫入Es6的正文,而是寫入了附錄,原因是__ proto __前后的雙下劃線,說明它本質(zhì)上是一個(gè)內(nèi)部屬性,而不是一個(gè)正式對(duì)外的API,只是由于瀏覽器廣泛支持,才被加入Es6。標(biāo)準(zhǔn)明確規(guī)定,只有瀏覽器必須部署這個(gè)屬性,其它運(yùn)行環(huán)境不一定要部署。
Object.getPrototypeOf()
該方法與Object.setPrototypeOf方法配套,用于讀取一個(gè)對(duì)象的原型對(duì)象。
演示:
var obj = {}
Object.getPrototypeOf(obj)
下面是一個(gè)列子。
function Fn(){}
var f = new Fn()
Object.getPrototypeOf(f) === Fn.prototype //true
Object.setPrototypeOf(f,Object.prototype)
Object.getPrototypeOf(f) === Fn.prototype //false
如果參數(shù)不是對(duì)象,會(huì)自動(dòng)轉(zhuǎn)換為對(duì)象
Object.getPrototypeOf(1)
// 轉(zhuǎn)換過程 Object.getPrototypeOf(Number(1))
// Number{ [[PrimitiveValue]]: 0 }
Object.getPrototypeOf('foo')
//轉(zhuǎn)換過程 Object.getPrototypeOf(String('foo'))
// String{length:0,[[PrimitiveValue]]: ' ' }
Object.getPrototypeOf(1) == Number.prototype // true
Object.getPrototypeOf('foo') == String.prototype // true
如果參數(shù)是undefined和null,它們無法轉(zhuǎn)換為對(duì)象,會(huì)直接報(bào)錯(cuò)
Object.getPrototypeOf(null) //報(bào)錯(cuò)
// typeError: Cannot convert null or undefined to Object
Object.getPrototypeOf(undefined) //報(bào)錯(cuò)
// typeError: Cannot convert null or undefined to Object
如果一個(gè)對(duì)象本身部署了__ proto __屬性,該屬性的值就是對(duì)象的值。
Object.getPrototypeOf({__proto__:null}) // null
Object.setPrototypeOf()
Object.setPrototypeOf方法的作用與__ proto __ 相同, 用來設(shè)置一個(gè)對(duì)象的prototype對(duì)象,返回參數(shù)對(duì)象本身。它是Es6正式推薦設(shè)置原型的對(duì)象的方法。
演示:
//格式
Object.setPrototypeOf(object,prototype)
//用法
const obj = Object.setPrototypeOf({},null) //設(shè)置這對(duì)象沒有原型
//該方法等同于下面的函數(shù)
function setPrototypeOf(obj,proto1){
obj.__proto__ = proto1
return obj
}
下面是一個(gè)例子
let proto = {}
let obj = {name:'秦司令'}
Object.setPrototypeOf(obj,proto)
proto.x = 10;
proto.y = 20;
obj.name // 秦司令
obj.x // 10
obj.y // 20
上面代碼將proto對(duì)象設(shè)為obj的原型,所以從obj對(duì)象可以讀取proto對(duì)象的原型屬性。
如果第一個(gè)參數(shù)不是對(duì)象,會(huì)自動(dòng)轉(zhuǎn)換為對(duì)象,但是由于返回的還是第一個(gè)參數(shù),這個(gè)操作不會(huì)產(chǎn)生任何效果。
Object.setPrototypeOf(1,{}) === 1
Object.setPrototyoeOf('name' , {}) == 'name'
Object.setPrototypeOf(true,{}) == true
由于undefined和null無法轉(zhuǎn)換為對(duì)象,所以第一個(gè)參數(shù)是undefined或null的話,會(huì)直接報(bào)錯(cuò)。
Object.setPrototypeOf(undefined,{}) //報(bào)錯(cuò)
Object.setPrototypeOf(null,{}) //報(bào)錯(cuò)
Object.keys()
Es5引入了Object.keys方法,返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不包含繼承的) 所有可遍歷(enumerable)的屬性鍵名。
var obj = {name:'秦司令',age:20};
Object.keys(obj) //["name","age"]
Es2017引入了跟Object.keys配套的Object.values和Object.entries,作為遍歷一個(gè)對(duì)象的補(bǔ)充手段,供for....of循環(huán)使用
var obj = {a:1,b:2,c:3};
for(let key of Object.keys(obj)){
console.log(key) // a b c
}
for(let values of Object.values(obj)){
console.log(values) // 1 2 3
}
for(let [k,v] of Object.entries(obj)){
console.log([k,v]) // [name,'秦司令',age:20]
}
Object.values()
Object.values方法返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值。
var obj = {name:'秦司令',age:20};
Object.values(obj) // ["秦司令",20]
返回?cái)?shù)組的成員順序
var obj = {100:'a',2:'b',18:'c'}
Object.values(obj) // [b,c,d]
上面代碼中,屬性名為數(shù)值的屬性,是按照數(shù)值大小,從小到大遍歷的,因此返回的順序是b,c,d
Object.values只返回自身可遍歷屬性
var json = Object.create({},{p:{value:123}})
Object.values(json) //[]
上面代碼中,Object.create方法的第二個(gè)參數(shù)添加的對(duì)象屬性(屬性p),如果不顯式聲明,默認(rèn)是不可遍歷的,因?yàn)閜的屬性描述對(duì)象的enumerable默認(rèn)是false,Object.values不會(huì)返回這個(gè)屬性,只要把enumerable改成true,Object.values就返回這個(gè)屬性p值
var obj = Object.create({},{p:{
value:2,
enumerable:true
}})
Object.values(obj) //[2]
Object.values會(huì)過濾屬性名為Symbol值的屬性
Object.values({[Symbol()]:123,name:'秦司令'})
// ['秦司令']
如果Object.values方法的參數(shù)是一個(gè)字符串,會(huì)返回各個(gè)字符組成的一個(gè)數(shù)組。
Object.values('fooa')
// ['f','0','0','a']
上面代碼中,字符串會(huì)先轉(zhuǎn)換成一個(gè)類似數(shù)組的對(duì)象,字符串的每個(gè)字符,就是該對(duì)象的一個(gè)屬性,因此,Object.values返回每個(gè)屬性的鍵值,就是各個(gè)字符組成的一個(gè)數(shù)組
如果參數(shù)不是對(duì)象,Object.values會(huì)先將其轉(zhuǎn)換為對(duì)象。由于數(shù)值和布爾值的包裝類對(duì)象,都不會(huì)為實(shí)例添加非繼承的屬性,所以,Object.valus會(huì)返回空數(shù)組
Object.values(43) //[]
Object.values(true) // []
Object.entries()
Object.entries()方法返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對(duì)數(shù)組。
var obj = {foo:'bar',baz:43}
Object.entries(obj)
// [["foo","bar"],["baz",43]]
如果原對(duì)象的屬性名是一個(gè)Symbol值,該屬性會(huì)被忽略
Object.entries({[Symbol()]:123,foo:'abc'})
//[["foo","abc"]]
Object.entries的基本用途是遍歷對(duì)象的屬性
let obj = {one:1,tow:2}
for(let [k,v] of Object.entries(obj)){
console.log(`${JSON.stringify(k)}:${JSON.stringify(v)}`)
}
// "one":1
// "tow":2
Object.entries另一用處是將對(duì)象轉(zhuǎn)換成Map結(jié)構(gòu)
var obj = {foo:'bar',name:'秦司令'}
var map = new Map(Object.entries(obj))
// Map{foo:'bar',name:'秦司令'}