一、对象密封4种方式
(1)查看getter和setter对应的函数名
可以通过getOwnPropertyDescriptor查看
const obj = {
get foo(){},
set foo(x){}
}
var descriptor = Object.getOwnPropertyDescriptor(obj,'foo');
console.log(descriptor.get.name);//打印get foo
console.log(descriptor.set.name);//打印set foo
getter和setter可以叫做取值函数和存值函数
(2)对象常量【密封方法一】
定义
对象常量即属性不可删除和修改
当一个对象的属性描述配置为configurable: false, writable: false的时候就把这个叫做对象常量
obj = { a:2 }
Object.defineProperty(obj,'a',{
value: 6,
configurable: false,
writable: false,
enumerable: true
});
缺点
可以添加属性
(3)preventExtensions/isExtensible【密封方法二】
禁止对象的拓展
var obj = {a: 2};
Object.preventExtensions(obj);
obj.b = 3;
console.log(obj);//{a:2}
查看是否可拓展
log(Object.isExtensible(obj)); //false 不可拓展
特性
.
添加属性:会静默失败,严格模式下会报错,属性描述符默认是truedefineProperty
添加属性:不会静默失败,直接报错,属性描述符默认是false
'use strict'
obj.b = 6; // 报错
Object.defineProperty(obj,'b',{
value: 6,
}); // 直接报错
缺点
可以删除属性
(4)seal/isSealed【密封方法三】
原理
底层调用了preventExtensions,且把所有的configurable改为false
密封对象
var obj = {a : 2};
Object.seal(obj);
查看是否密封
log(Object.isSealed(obj))//true
缺点
可以修改属性
(5)freeze/isFrozen【密封方法四】
冻结对象
此方法密封级别最高,属性不能增删改
var obj = {a : 2};
Object.freeze(obj);
查看是否冻结
log(Object.isFrozen(obj));//为true则代表已经冻结
深度冻结
对象里面是对象的情况就需要深度冻结了
function myFreeze(obj){
Object.freeze(obj);
for(var key in obj){
if(typeof(obj[key]) === 'object' && obj[key] !== null){
myFreeze(obj[key])
}
}
}
(6)is
除了下面两种情况,和===没什么区别
console.log(NaN == NaN);//false
console.log(+0 == -0);//true
console.log(Object.is(NaN,NaN))//true
console.log(Object.is(+0,-0))//false
二、assign
(1)作用
用于合并一个对象,返回对象是目标对象(同一个引用)
(2)基本使用
Object.assign(目标对象,…原对象);
let obj = {a: 1};
let tar = {};
let copy = Object.assign(tar,obj);
log(copy);//{a: 1}
log(copy === tar) //true
log(copy === obj) //false
// 证明assign返回的对象就是原对象
(3)合并与覆盖
合并
const tar = {a: 1};
const tar2 = {b: 2};
const tar3 = {c: 3};
Object.assign(tar,tar2,tar3)
log(tar);//{a:1,b:2,c:3}
覆盖
const tar = {a:1,b:1};
const tar2 = {b:2,c:2};
const tar3 = {c:3} // 自下向上覆盖
Object.assign(tar, tar2, tar3);
console.log(tar);//{a:1,b:2,c:3}
(4)第一个参数为原始值
第一个参数只要求值是对象
undefined和null无法进行合并因为没有包装类
Object.assign(undefined,{a:1}); //报错
Object.assign(null,{a:1}); //报错
原始值有包装类的调用所以可以合并
var test = Object.assign(1,{a:1}); //Number{1,a:1}
var test = Object.assign(true,{a:1}); //Boolean{1,a:1}
var test = Object.assign("1",{a:1}); //String{1,a:1}
(5)第二个参数为原始值
第二个参数值是对象,且必须是可枚举的才可以合并
字符串经过隐式转化后就是一个可枚举的对象,所以可以合并
var test = Object.assign({a:1},"123"); //{0: '1', 1: '2', 2: '3', a: 1}
属性呈浅紫色说明enumerable为false,即不可枚举
以下经过隐式转化成对象后没有可遍历的属性,剩下的都是私有变量,所以相当于合并一个空对象,合并后就等于返回原对象
Object.assign({a:1}, undefined); //{a:1}
Object.assign({a:1}, null); //{a:1}
var test = Object.assign({a:1},1); //{a:1}
var test = Object.assign({a:1},true); //{a:1}
(6)拷贝Object.create()
原型属性和不可枚举属性都不能拷贝
var obj = Object.create({foo:1},{ // 参数:原型属性配置,对象属性配置(属性描述符)
bar:{
value:2
},
baz:{
value:3,
enumberable:true
}
})
console.log(obj)
// {
// baz:3
// bar:2
// __proto__:
// foo:1
// }
var copy = Object.assign({},obj);
console.log(copy);//{baz:3}
(7)拷贝symbol()
可以生成一个永远都不会重复的字符串
var a = symbol();
var b = symbol();
console.log(a === b);//打印false
var a = symbol('a');
var b = symbol('a')
console.log(a == b)//打印false
拷贝
var test = Object.assign({a:'b'},{[Symbol('c')]:'d'}); //symbol返回的是symbol类型,包上[]里面就会有包装类,隐式转换toString
console.log(test);
//{a:"b",Symbol(c):"d"}
(8)浅拷贝
assign只能浅拷贝对象
const obj1 = {a:{b:1}};
const obj2 = Object.assign({},obj1);
obj1.a.b = 2;
log(obj2);//{a:{b:2}}
(9)同名属性替换
assign只能替换整个属性,无法替换单个深入替换
const target = {a:{b:'c',d:'e'}};
const sourse = {a:{b:'hello'}};
Object.assign(target, sourse);
log(target);//{a:{b:hello}}
(10)数组的替换
对象根据属性名替换,数组根据索引替换(隐式转换后索引就是属性名)
var a = Object.assign([1, 2, 3],[4,5]);
log(a);//输出[4,5,3]
(11)扩充属性和方法
往原型上扩充属性和方法
var age = 1;
function Person(){}
Object.assign(Person.prototype,{
eat(){},
age,
})
console.log(Person.prototype); // {age: 1, eat: ƒ, constructor: ƒ}
(12)默认值
通过三个参数来实现
const DEFAULT = {
url: {
host: 'www.baidu.com',
port: 7070
}
}
function test(option){
option = Object.assign({},DEFAULT,option); // 参数: 空对象,默认对象,配置对象
console.log(option);
}
// 传参
test({url:{port:8080}}); //{url:{port:8080}}
// 不传参
test(); //{url:{host:'www.baidu.com',port:7070}}
三、无法拷贝getter/setter的问题
(1)问题描述
由于assign无法拷贝getter和setter(取值函数和赋值函数),于是defineProperties/getOwnPropertyDescriptors就诞生了,它们的存在就是为了解决这个问题 。
const sourse = {
get foo(){
return 1;
}
}
const target = {};
Object.assign(target,sourse);
console.log(target); //{foo:1}
//这样就不行
const sourse = {
set foo(value){
console.log(value);
}
}
const target = {};
Object.assign(target,sourse);
console.log(target); //{foo:undefined}
(2)defineProperties/getOwnPropertyDescriptors
defineProperties
定义多个属性
var obj = {}
Object.defineProperties(obj,{
a:{
value: true,
writable: true
},
b:{
value: 'hello',
writable: false
}
})
getOwnPropertyDescriptors
获取多个属性描述符
console.log(Object.getOwnPropertyDescriptors(obj));
// {
// a:
// configurable: false
// enumerable: false
// value: true
// writable:true
// }
// {
// b:
// configurable: false
// enumerable: false
// value: 'hello'
// writable:true
// }
(3)解决方案
使用defineProperties拷贝(定义)多个属性,再通过Object.getOwnPropertyDescriptors(sourse)
配置多个属性描述配置对象,从而实现拷贝的效果(实则是重新定义了属性)
setter拷贝
const sourse = {
set foo(value){
console.log(value);
}
}
const target = {};
Object.defineProperties(target, Object.getOwnPropertyDescriptors(sourse));
console.log(Object.getOwnPropertyDescriptors(sourse))
//输出:
// {
// foo: {
// get: undefined,
// set: [Function: set foo],
// enumerable: true,
// configurable: true
// },
// }
console.log(Object.getOwnPropertyDescriptor(target,'foo'));
// 输出:
// {
// configurable: true
// enumerable: true
// get: undefined
// set: ƒ foo(value)
// }
console.log(target);
//输出:
// {
// set foo: ƒ foo(value) // 这里set foo是自定义属性,呈浅紫色
// [[Prototype]]: Object
//}
getter拷贝
const sourse = {
get foo(){
return 1;
}
}
const target = {};
Object.defineProperties(target, Object.getOwnPropertyDescriptors(sourse));
console.log(Object.getOwnPropertyDescriptors(sourse))
// 输出:
// {
// foo: {
// get: [Function: get foo],
// set: undefined,
// enumerable: true,
// configurable: true
// },
// }
console.log(Object.getOwnPropertyDescriptor(target,'foo'));
// 输出:
// {
// configurable: true
// enumerable: true
// get: ƒ foo()
// set: undefined
//}
console.log(target);
//输出
// {
// foo: (...), // (...)说明描述符配置了get
// get foo: ƒ foo(),
// [[Prototype]]: Object
// }
(4)浅拷贝新方法
传统的浅拷贝只能拷贝其属性,这种方法除了属性还能拷贝原型
var obj = {a:1, b:2, c:3}
// 拷贝obj原型 拷贝obj属性
const clone = Object.create(Object.getPrototypeOf(obj),Object.getOwnPropertyDescriptors(obj))
console.log(clone) // {a:1,b:2,c:3}
//优化写法
const clone = (obj) => Object.create(Object.getPrototypeOf(obj),Object.getOwnPropertyDescriptors(obj))
(5)部署对象的方式:
-
对象字面量
const obj = {foo:123}
-
create方法
const obj = Object.create({protoKey:'protoVal'}); obj.foo = 123;
-
assign方法
const obj = Object.assign(Object.create({protoKey:'protoVal'}),{ foo: 123; })
-
create、getOwnPropertyDescriptors组合
const obj = Object.create({protoKey:'protoVal'},getOwnPropertyDescriptors({foo:123}))