1鸡号、Object.assign
Object.assign
方法只會拷貝源對象自身的并且可枚舉
的屬性到目標對象。注意這里有個詞叫做可枚舉
Object.assign(target,...source)
1-1临梗、基礎(chǔ)用法
const t1 = {a:1,b:2};
const t2 = {b:3,c:4};
const t3 = {c:5,d:6};
const t4 = Object.assign(t1,t2,t3);
此時t1和t4是相同的 {a: 1, b: 3, c: 5, d: 6}
t2、t3此時是不變的
Object.assign
會改變目標對象上的屬性和值,所以我們一般對對象進行合并一般使用const result = Object.assign({},...source)
;給予目標對象為一個空對象扣典,這樣就可以不用改變已有的值了负甸。
1-2流强、改變結(jié)果的屬性/屬性值痹届,那么目標對象是否會改變?
如果此時我們改變t4上面的屬性值打月,那么想想t1會不會被改變队腐??
const t1 = {a:1,b:2};
const t2 = {b:3,c:4};
const t3 = {c:5,d:6};
const t4 = Object.assign(t1,t2,t3);
t4.d = 100;
console.log(t1)//想想為什么奏篙?
console.log(t4)
結(jié)果卻是都一起被改變了柴淘,為什么會被改變?我們知道在程序中秘通,任何一個變量其實都被引用了一段16位進制的空間地址为严,在上述程序中其實t4的引用了t1的空間地址,上述我們可以理解為
const t4 = t1;
而t1在Object.assign
運算中已被改變了
1-3肺稀、給其中一個源對象增加原型鏈增加屬性/方法
猜想下給其中一個源對象增加原型鏈增加屬性/方法,那么會有什么不同呢第股?
const t1 = {a:1,b:2};
const t2 = {b:3,c:4};
const t3 = {c:5,d:6};
t2.__proto__.e = 100;
t2.__proto__.print = function(){console.log(this)};
const t4 = Object.assign(t1,t2,t3);
console.log(t1.__proto__);
console.log(t2.__proto__);
console.log(t3.__proto__);
console.log(t4.__proto__);
結(jié)果我們發(fā)現(xiàn)竟然發(fā)現(xiàn)所有的源對象上的原型鏈包括結(jié)果和目標對象都有了新的屬性/方法這是為什么?话原?夕吻??
其實當(dāng)你真正的理解了什么是原型鏈這個結(jié)果就不意外了繁仁。
即使我們新建一個對象 不參與Object.assign
運算也會發(fā)現(xiàn)這個新建的對象的原型鏈也有了這個新的屬性
const t1 = {a:1,b:2};
const t2 = {b:3,c:4};
const t3 = {c:5,d:6};
t2.__proto__.e = 100;
t2.__proto__.print = function(){console.log(this)};
const t4 = Object.assign(t1,t2,t3);
const t5 = {test:100};//不參與運算
console.log(t5.__proto__)其實都有了
console.log(new Object());其實都有了
1-4梭冠、什么是可枚舉
可枚舉值得是可以遍歷到的屬性,在這里的案例我們需要用到
Object.defineProperty
的用法了
const t1 = { a: 1, b: 2 };
const t2 = { b: 3, c: 4 };
const t3 = Object.defineProperty({}, "c", {
value: 100
})
const t4 = Object.assign(t1, t2, t3);
console.log(t4); 此時這個值是多少?
我們可以知道
Object.defineProperty
的可枚舉屬性enumerable
默認為false改备,所以此時t4的屬性c
還是4控漠,但如果設(shè)置為true,可枚舉
const t1 = { a: 1, b: 2 };
const t2 = { b: 3, c: 4 };
const t3 = Object.defineProperty({}, "c", {
value: 100,
enumerable:true//可枚舉
})
const t4 = Object.assign(t1, t2, t3)
那么這里的t4屬性c就會變?yōu)?00
這就是說拷貝可枚舉的屬性
1-5、不會拷貝原型上的屬性或者方法
當(dāng)然這個前提是你不能更改Object上的屬性和方法悬钳,因為這個
1-3
的原因是一樣的盐捷,你改變了祖父上的屬性和方法,那么任何子類上都會繼承
function t1() {
this.a = 1;
this.b = 2
}
t1.prototype.c = 3;
t1.prototype.say = function () { };
const t2 = Object.assign({}, new t1());
console.log(t2);此時t2上沒有c默勾、say的屬性
1-6碉渡、面試題
猜想下下面的結(jié)果會是什么?
const t1 = 123;
const t2 = "456";
const t3 = false;
const t4 = function () { };
const t5 = Object.assign({},t1, t2, t3, t4)
console.log(t5);
解析母剥,Object.assign是復(fù)制對象滞诺,如果不是對象那么則會強制轉(zhuǎn)為對象
const r1 = new Number(t1);
const r2 = new String(t2);
const r3 = new Boolean(t3);
const r4 = new Function(t4);
for(let k in r1){console.log(k,r1[k]);}//沒打印
for(let k in r2){console.log(k,r2[k]);}//打印了
for(let k in r3){console.log(k,r3[k]);}//沒打印
for(let k in r4){console.log(k,r4[k]);}//沒打印
所以這道題的t5值真正的為t2的對象{0:4,1:5,2:6}
2、Object.create
Object.create
方法創(chuàng)建一個新對象环疼,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的__proto__
习霹。其實這個語法和接下來學(xué)習(xí)的Object.defineProperty
語法有關(guān)系
2-1、語法
@param proto
新創(chuàng)建對象的原型對象炫隶。
@param propertiesObject
可選淋叶。需要傳入一個對象,該對象的屬性類型參照Object.defineProperties()的第二個參數(shù)伪阶。
如果該參數(shù)被指定且不為undefined煞檩,
該傳入對象的自有可枚舉屬性(即其自身定義的屬性处嫌,而不是其原型鏈上的枚舉屬性)
將為新創(chuàng)建的對象添加指定的屬性值和對應(yīng)的屬性描述符。
Object.create(proto斟湃,[propertiesObject])
2-2熏迹、基礎(chǔ)用法
let a = Object.create({b:1});
console.log(a);
我們會發(fā)現(xiàn)這個b屬性會在原型鏈上而不是本身的對象屬性上。
2-3凝赛、我們加上第二參數(shù)
當(dāng)然你要熟悉Object.defineProperty
的用法
let a = Object.create({ a: "a" }, {
b: {
value: "b",
writable : true
},
c: {
value: "c",
configurable:false
},
d: {
enumerable:false,
value: "d"
}
})
console.log(a);
2-4注暗、思考題
const t1 = Object.create(null)
console.log(t1);
此時你會發(fā)現(xiàn)當(dāng)前這個t1連原型都沒有了
3、Object.defineProperty
我相信這個應(yīng)該有不少人都熟悉它哄酝,因為
vue2.x
的響應(yīng)式原理用的就是該屬性,當(dāng)然vue3.x
已經(jīng)全面改成Proxy
了,當(dāng)然這個不在本章節(jié)范圍內(nèi)祷膳。
3-1陶衅、語法
@param obj 要定義屬性的對象。
@param prop 要定義或修改的屬性的名稱直晨。
@descriptor 要定義或修改的屬性描述符搀军。
Object.defineProperty(obj, prop, descriptor)
3-2 、descriptor詳解
其實
Object.defineProperty
真正的靈魂就是descriptor
這個參數(shù)了
該參數(shù)有六個屬性:
value
:該屬性對應(yīng)的值勇皇。
configurable
:該屬性是否可以被刪除,默認值為false罩句,不可被刪除
enumerable
:該屬性是否可以被枚舉,默認值為false,不可被枚舉(也就是不能被循環(huán)遍歷到);想到這個應(yīng)該能想到Object.assign了吧
writable
:該屬性是否可以被重新賦值敛摘,默認置為false,不可被改寫
get
屬性的 getter 函數(shù)门烂,如果沒有 getter,則為 undefined兄淫。當(dāng)訪問該屬性時屯远,會調(diào)用此函數(shù)。
set
屬性的 setter 函數(shù)捕虽,如果沒有 setter慨丐,則為 undefined。當(dāng)屬性值被修改時泄私,會調(diào)用此函數(shù)房揭。
3-3、用法
要注意的是
writable晌端、value
不能和get捅暴、set
方法同時出現(xiàn)
const t1 = Object.defineProperty({},"a",{
value:"1",
enumerable:true,//可枚舉
configurable : true ,//可刪除
writable : true,//可賦值
})
console.log(t1);
//有g(shù)et、set方法
let tValue = 1;
const t1 = Object.defineProperty({},"a",{
enumerable:true,//可枚舉
configurable : true ,//可刪除
get(){
return tValue
},
set(v){
tValue = v
}
})
console.log(t1);
3-4咧纠、面試題
請設(shè)計當(dāng)前程序讓它滿足一下的判斷條件
if(a === 1 && a=== 2 && a === 3){
console.log("條件判斷正確");
console.log(a)?
}
答案:
要想一個變量同時滿足三個條件常規(guī)操作肯定不行伶唯,我們知道每次訪問變量其實就是
調(diào)用了getter方法獲取這個值,二全局變量都在window對象上所以
let _a = 0;
Object.defineProperty(window,"a",{
get (){
return ++_a;
}
})
為什么這樣就可以了惧盹?因為我們每次進行
a
變量比較都是在獲取這個a的值乳幸,但是我們的getter
方法每次訪問都會加1.
4瞪讼、Object.entries
Object.entries
意義:方法返回一個給定對象自身可枚舉屬性的鍵值對數(shù)組(白話就是,這個方法返回一個數(shù)組粹断,且每個數(shù)組元素就是該對象的key和value),請注意必須是一個可枚舉的屬性/方法才能轉(zhuǎn)換,但是它不會轉(zhuǎn)換原型上的屬性/方法
4-1符欠、用法
let a = {a:"1",b:"b",c:"測試"};
let r = Object.entries(a);
console.log(r);這是一個二維數(shù)組
4-2 、測試不能枚舉的屬性能否轉(zhuǎn)換
let a = Object.defineProperties({},{
a:{
value:1
},
b:{
value : 2,
enumerable:true
},
c:{
value : 3,
enumerable : true
}
})
console.log(Object.entries(a));
會發(fā)現(xiàn)屬性a沒有在數(shù)組中
4-3瓶埋、將對象轉(zhuǎn)為map對象
let a = {a:1,b:2,c:3};
let b= new Map(a);//直接轉(zhuǎn)換會報錯的希柿。
let b = new Map(Object.entries(a));//成功
4-4、測試原型上的屬性养筒、方法
function Test(){
this.a = 1;
this.b = 2
}
Test.prototype.c = 3;
Test.prototype.say=function(){}
console.log(Object.entries(new Test()));
for (let k in t) {
console.log(k);雖然c和say能打印但是卻不能轉(zhuǎn)換
}
會發(fā)現(xiàn)c和say都不會出現(xiàn)在結(jié)果中
5曾撤、Object.fromEntries
其實這個方法就是
Object.entries
的對應(yīng)方法,一個是將對象轉(zhuǎn)為數(shù)組晕粪,一個將數(shù)組轉(zhuǎn)為json
5-1挤悉、用法
let a = [["a",1],["b",2],["c",3]]
console.log(Object.fromEntries(a));
//{a: 1, b: 2, c: 3}結(jié)果
5-2、將Map對象轉(zhuǎn)為json
let a = new Map();
a.set("a",1)
a.set("b",2)
a.set("c",3)
console.log(Object.fromEntries(a));
6巫湘、Object.freeze
Object.freeze
凍結(jié)一個對象装悲,并且返回源對象(返回值和源對象是一個引用),且這個凍結(jié)是淺凍結(jié)尚氛。
什么叫凍結(jié)诀诊?也就是這個對象不可被操作了
6-1、用法
let a = { a: 1, b: 2, c: 3 };
Object.freeze(a)
a.a = 2//不可改
console.log(a);
a.d = 4;//不可增
console.log(a);
delete a.c;//不可刪
console.log(a);
6-2阅嘶、利用構(gòu)造函數(shù)的原型鏈改變值
function Test(){
this.a = 1;
this.b = 2;
}
Test.prototype.c = 3
let t= new Test();
Object.freeze(t)
t.c = 5//即使是實例原型上的屬性/或者方法也不能操作
console.log(t);
//我們改變構(gòu)造函數(shù)的值
Test.prototype.c = 100;
console.log(t.c);//發(fā)現(xiàn)是可以修改的
如果一個對象被凍結(jié)了属瓣,其原型鏈上的方法/屬性也不會被修改,但是可通過其構(gòu)造函數(shù)的原型進行操作讯柔。
6-3奠涌、為什么說Object.freeze是淺凍結(jié)?
function Test(){
this.a = 1;
this.b = 2;
this.d = {
d:"這是子對象"
}
this.e = ["a","b","c"]
}
Test.prototype.c = 3
let t= new Test();
//我們是不可以直接改變d的值
t.d = "d"
console.log(t);
//但是我們可以改變d屬性里面的值
t.d.d = "我改變了值"
console.log(t);確實被改變了
通過這個案例我們會發(fā)現(xiàn)
Object.freeze
它只會凍結(jié)源對象的屬性磷杏,但是他不會凍結(jié)源對象中的對象和數(shù)組
6-4溜畅、被凍結(jié)的對象屬性即使通過getter/setter方法也不能被修改刪除
let a = { _a: 1, b: 2, get a() {
return this._a;
}, set a(v) {
this._a = v;
} }
Object.freeze(a)
a.a = 3;//a屬性并不會改變包括_a
console.log(a);
6-5、編寫深凍結(jié)方法
Object.deepFreeze = function (obj) {
//let keys = Object.keys(obj);
//注意Object.keys拿不到不可枚舉的key而getOwnPropertyNames能拿到
let keys = Object.getOwnPropertyNames(obj);
keys.forEach(k => {
if (typeof obj[k] === "object" && obj[k] != null) {
Object.deepFreeze(obj[k])
}
})
return Object.freeze(obj);
}
7极祸、Object.isFrozen
Object.isFrozen
判斷一個對象是否被凍結(jié)
7-1慈格、判斷Object.freeze
到底是不是淺凍結(jié)
let a = {
a:1,
b:2,
c:{
c:3,
d:4
}
}
Object.freeze(a);
console.log(Object.isFrozen(a.c))//false
Object.freeze(a.c);
console.log(Object.isFrozen(a.c))//true
8、Object.seal遥金、Object.isSealed
Object.seal
封閉對象它和Object.freeze
差不多浴捆,但是它是可以修改值得
let a = {
a:1,
b:2,
}
Object.prototype.c = 3;
Object.seal(a)
a.d = 5;//不可增加
delete a.a//不可刪除
a.b = 100;//可以修改
a.__proto__.c = 333;//可以修改
console.log(a);
8-1、為什么說Object.seal是淺封閉稿械?
let a = {
a:1,
b:2,
c:{
c:3
}
}
a.__proto__.d = 4;
Object.seal(a);
//為true
console.log(Object.isSealed(a));
//為false
console.log(Object.isSealed(a.c));
a.c.d = 5;
console.log(a.c);//比沒有添加d屬性
Object.seal(a.c);
//為true
console.log(Object.isSealed(a.c));
8-2选泻、編寫深封閉
Object.deepSeal = function (obj) {
//let keys = Object.keys(obj);
//注意Object.keys拿不到不可枚舉的key而getOwnPropertyNames能拿到
let keys = Object.getOwnPropertyNames(obj);
keys.forEach(k => {
if (typeof obj[k] === "object" && obj[k] != null) {
Object.deepSeal (obj[k])
}
})
return Object.seal(obj);
}
9、Object.preventExtensions
Object.preventExtensions
方法讓一個對象變的不可擴展,也就是永遠不能再添加新的屬性页眯。
let a = {};
Object.preventExtensions(a)
a.a = 1;
a.__proto__.b = 2;//增加原型鏈上的屬性是可行的梯捕,但是切記不要這樣做,你會發(fā)現(xiàn)新的對象上也會有該屬性
console.log(a);
let b = {}
consoole.log(b) b屬性也會存在的
10窝撵、總結(jié)Object.freeze傀顾、Object.seal、Object.preventExtensions;
Object.freeze
:凍結(jié)對象碌奉。不可修改短曾、不可刪除、不可擴展
Object.seal
:封閉對象赐劣〖倒眨可修改、不可刪除魁兼、不可擴展
Object.preventExtensions
:禁止擴展對象婉徘。可修改璃赡、可刪除判哥、不可擴展