ES6-9【对象密封4种方式、assign、无法拷贝getter/setter的问题】

一、对象密封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 不可拓展

特性

  1. . 添加属性:会静默失败,严格模式下会报错,属性描述符默认是true
  2. defineProperty添加属性:不会静默失败,直接报错,属性描述符默认是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)部署对象的方式:

  1. 对象字面量

    const obj = {foo:123}
    
  2. create方法

    const obj = Object.create({protoKey:'protoVal'});
    obj.foo = 123;
    
  3. assign方法

    const obj = Object.assign(Object.create({protoKey:'protoVal'}){
    	foo: 123;
    })
    
  4. create、getOwnPropertyDescriptors组合

    const obj = Object.create({protoKey:'protoVal'},getOwnPropertyDescriptors({foo:123}))
    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值