ES6笔记 9.对象密封4种方式、assign、取值函数的拷贝

获取取值函数(getter)与赋值函数(setter)的函数名

对于以下代码,直接使用 obj.foo 执行的是取值操作,获取到的是 foo 函数的返回值而不是函数名

const obj = {
  get foo() {},
  set foo(val) {},
};
console.log(obj.foo.name); // TypeError: Cannot read property 'name' of undefined

利用 Object.getOwnPropertyDescriptor()获取属性描述符的方法获取

const obj = {
  get foo() {},
  set foo(val) {},
};
var descriptor = Object.getOwnPropertyDescriptor(obj, "foo");
console.log(descriptor.get.name); // get foo
console.log(descriptor.set.name); // set foo

对象密封的方法

第一种方式:对象常量

对象常量,常量意味着不可修改,不可删除;也就对应着属性描述符中 writable:false,configurable:false;

// 此时obj对象中的属性a属于对象常量,无法删除,无法修改。
var obj = {};
Object.defineProperty(obj, "a", {
  value: 3,
  writable: false,
  configurable: false,
});
obj.a = 4; // 静默失败
delete a; // 静默失败

第二种方式:阻止对象扩展,Object.preventExtensions()

缺点:对象原本存在的属性不是对象常量的方式,可能虽然通过阻止对象扩展的方式禁止对象扩展,但是原本存在的属性依旧能够有被删除或者修改的风险。

  • 通过对象点语法的方式进行对象扩展,扩展的程序会静默失败不会报错。
var obj = { a: 2 };
Object.preventExtensions(obj);
obj.b = 3; // 尝试对象扩展失败,静默失败
console.log(obj);
  • 通过定义属性的方式进行对象扩展,扩展的程序会抛出异常 TypeError: Cannot define property b, object is not extensible
var obj = { a: 2 };
Object.preventExtensions(obj);
Object.defineProperty(obj, "b", {
  value: 3,
}); // 尝试对象扩展,程序抛出异常
console.log(obj); // TypeError: Cannot define property b, object is not extensible
  • 严格模式下,通过对象点语法进行对象扩展,程序会抛出异常 TypeError: Cannot define property b, object is not extensible
"use strict";
var obj = { a: 2 };
Object.preventExtensions(obj);
obj.b = 3;
console.log(obj); // TypeError: Cannot define property b, object is not extensible

Object.isExtensible(),判断对象是否处于扩展的状态

var obj = { a: 2 };
Object.preventExtensions(obj);
console.log(Object.isExtensible(obj)); // false 不可扩展

var obj2 = { a: 2 };
console.log(Object.isExtensible(obj2)); // true可扩展

对象点语法扩展的属性配置项集合默认是 true,defineProperty 或者 defineProperties 方法扩展的属性默认是 false

  1. 点语法扩展属性配置项
var obj = {};
obj.a = 2;
var descriptor = Object.getOwnPropertyDescritor(obj, "a");
console.log(descriptor); // { value: 2, writable: true, enumerable: true, configurable: true }
  1. defineProperty()、defineProperties()扩展对象
//defineProperty:
var obj = {};
Object.defineProperty(obj, "a", {
  value: 2,
});
var descriptor = Object.getOwnPropertyDescriptor(obj, "a");
console.log(descriptor); // { value: 2, writable: false, enumerable: false, configurable: false }

// defineProperties:
var obj = {};
Object.defineProperties(obj, {
  a: {
    value: 2,
  },
  b: {
    value: 3,
  },
});
var descriptor = Object.getOwnPropertyDescriptors(obj);
console.log(descriptor);
// {
// 	a: { value: 2, writable: false, enumerable: false, configurable: false },
// 	b: { value: 3, writable: false, enumerable: false, configurable: false }
// }

第三种方式:密封对象,Object.seal()

通常一个对象是可扩展的(可添加新的属性)。密封一个对象会让整个对象变得不可扩展,且所有已经拥有的属性会变得不可配置。属性不可配置的效果就是属性变得不可删除,以及一个数据属性不能被重新定义为访问器属性。但是属性的值仍然可以修改。尝试删除一个密封对象的属性或者将某个密封对象的属性从数据属性转换成访问器属性,结果会静默失败或者抛出 TypeError

Object.seal()与 Object.preventExtensions()的区别在于:
preventExtensions()方法将对象变为不可扩展,也就是说不能够再向对象中添加属性。但是现有的属性依然可以被枚举、删除、修改。seal()方法是将对象密封,被密封的对象变得不可扩展、不可配置。但是现有的属性依旧存在可枚举、可修改的特点。其实本质上 seal()方法还是调用的 preventExtensions()方法,不同的是 seal()方法将属性修饰符的 configurable 属性设置为 false

Object.seal()与 Object.freeze()的区别在于:
seal()方法将一个对象进行密封,被密封的对象变得不可扩展,不可配置,但是现有的对象属性依然可以枚举、可以修改。freeze()方法将一个对象进行冻结,被冻结的对象变得不可扩展,不可配置、不可修改,但是现有的对象依然可以枚举。其实二者本质上都会调用 Object.preventExtensions()方法,只不过二者对属性修饰符对象属性做出的设置不同而已。

特点

  1. 密封后的对象,与需要密封的对象相同
var obj = { a: 2 };
var newObj = Object.seal(obj);
console.log(newObj === obj); // true
  1. 对象密封后,通过点语法尝试扩展对象,扩展程序静默失败,不会抛出异常
var obj = { a: 2 };
Object.seal(obj);
obj.b = 3; // 静默失败
console.log(obj); // {a:2}
  1. 对象密封后,通过 defineProperty 方式扩展对象,扩展程序抛出异常 TypeError: Cannot define property b, object is not extensible
var obj = { a: 2 };
Object.seal(obj);
Object.defineProperty(obj, "b", {
  value: 3,
});
console.log(obj); // {a:2} TypeError: Cannot define property b, object is not extensible
  1. 严格模式下,通过对象点语法进行对象扩展,程序会抛出异常 TypeError: Cannot define property b, object is not extensible
var obj = { a: 2 };
Object.seal(obj);
obj.b = 3;
console.log(obj); // TypeError: Cannot define property b, object is not extensible

Object.isSealed()方法,判断当前的对象是否密封

true 对象密封, false 对象未密封

var obj = { a: 2 };
Object.seal(obj);
console.log(Object.isSealed(obj)); // true 对象密封

var obj = { a: 1 };
console.log(Object.isSealed(obj)); // false 对象未密封

第四种方式:冻结对象,Object.freeze()

返回值:冻结后的对象,与原对象相同

var obj = { a: 2 };
var newObj = Object.freeze(obj);
console.log(obj === newObj); // true

Object.freeze()方法与上述的 seal 和 preventExtensions 一样,点语法扩展对象静默失败,defineProperty 方法扩展抛出异常,严格模式下扩展程序抛出异常。

Object.isFrozen(); 判断对象冻结状态

返回值:布尔值,true 对象冻结,false 对象未冻结

深拷贝冻结对象

Object.freeze()被冻结的对象只是浅程度的,如果对象属性是对象类型,那么冻结是不会深度影响到对象属性的。Object.seal()、Object.preventExtensions()也是相同的道理。

var obj = {
  a: { b: 3 },
};

// 深度拷贝冻结对象
function myFreeze(obj) {
  Object.freeze(obj);
  for (var key in obj) {
    if (Object.hasOwnProperty(key)) {
      if (typeof obj[key] === "object" && obj[key] !== null) {
        myFreeze(obj[key]);
      }
    }
  }
}

Object 上的静态方法与 prototype 上的方法

1.Object.is(): 判断两个值是否为同一个值

本质上调用的 sameValueZero 算法(===);和全等的运算方式类型,只是在对待有符号的零和 NaN 不同;

null == null; // true
NaN == NaN; // false
+0 == -0; // true
undefined == undefined; // true

Object.is(+0, -0); // false
Object.is(NaN, NaN); // true
Object.is(0, -0); // false

Object.is(NaN, 0 / 0); // true
Object.is(NaN, Number.NaN); // true

兼容性写法

if (!Object.is) {
  Object.defineProperty(Object, "is", {
    value: function (x, y) {
      if (x === y) {
        // 处理+0 -0的问题,利用正无穷与负无穷
        return x !== 0 || 1 / x === 1 / y;
      } else {
        // 处理NaN与NaN的问题
        return x !== x && y !== y;
      }
    },
  });
}

2.Object.assign(): 对象的克隆,合并方法

Object.assign()方法将所有可枚举(Object.prototype.propertyIsEnumerable()返回 true)和自有(Object.hasOwnProperty()返回 true)属性从一个或者多个源对象复制到目标对象,返回修改后的对象

let obj = { a: 1 };
let tar = {};
let copy = Object.assign(tar, obj);
console.log(copy); // {a:1}
console.log(copy === obj); // false
console.log(copy === tar); // true
const tar = { a: 1 };
const tar2 = { b: 2 };
const tar3 = { c: 3 };
Object.assign(tar, tar2, tar3);
console.log(tar); // {a:1, b:2, c:3}

对象合并时,重复的对象存在覆盖的现象,后面覆盖前面

const tar = { a: 1, b: 1 };
const tar2 = { b: 2, c: 2 };
const tar3 = { c: 3 };
console.log(tar); // {a:1, b:2, c:3}

Object.assign()参数 1,target 目标对象的注意事项

target 至少是对象才能进行合并,此时存在包装类的现象,又有 undefined,null 没有包装类,会抛出异常;TypeError: Cannot convert undefined or null to object

// undefined or null
Object.assign(undefined, { a: 1 }); // TypeError: Cannot convert undefined or null to object
Object.assign(null, { a: 1 }); // TypeError: Cannot convert undefined or null to object

// 三大包装类:Number String Boolean

Object.assign(true, { a: 1 }); // Boolean {true, a:1}
Object.assign("abc", { a: 1 }); // String {'abc', a:1}
Object.assign(1, { a: 1 }); // Number{1, a:1}

Object.assign()参数 2,sources 源对象的注意事项

sources 参数,如果不能转为对象就忽略,如果说能够转成对象,还需要注意对象的属性是否是可枚举性;也就是说属性描述符 enumberable:true;

Object.assign({ a: 1 }, 1); // {a:1}
Object.assign({ a: 1 }, "123"); // {0:1, 1: 2, 2:3, a:1}
Object.assign({ a: 1 }, true); // {a:1}

const test1 = "abc";
const test2 = true;
const test3 = 10;
const obj = Object.assign({}, test1, test2, test3); // {0:'a', 1:'b', 2:'c'};

Object.assign(),并不能拷贝原型上的属性,不可枚举属性

var obj = Object.create(
  { foo: 1 },
  {
    bar: {
      value: 2,
    },
    baz: {
      value: 3,
      enumerable: true,
    },
  }
);
var copy = Object.assign({}, obj);
console.log(copy); // {baz: 3}

assign 合并 Symbol 数据类型

通过 Symbol 函数进行创建一个完全不重复的原始值数据类型

var a = Symbol('a');
var b = Symbol('a');
console.log(a == b); // false
Object.assign({a: 'b'}, {
	[Symbol('c')]: 'd' // 这里必须加中括号
})  ------->     // { a: 'b',  symbol('c'): 'd' }

assign 复制的对象是一个浅拷贝的对象

const obj1 = { a: { b: 1 } };
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
console.log(obj2); // {a: {b: 2}}

assign 合并数组

注意,Object.assign 合并数组的时候,合并的是数组的下标索引,因为数组是对象的另一种形式,索引下标也就对应着对象的属性

// 数组拷贝
var a = Object.assign([1, 2, 3],[4, 5]);
console.log(a); // [4, 5, 3]
{
	0:1,
	1:2,
	2:3
}
{
	0:4,
	1:5
}

-->
{
	0:4,
	1:5,
	2:3
}

3.Object.prototype.propertyIsEnumerable(): 表示指定的属性是否可枚举

var obj = { a: 1 };
obj.propertyIsEnumerable("a"); // true

var obj = {};
Object.defineProperty(obj, "b", {
  value: 3,
  enumerable: false,
});
obj.propertyIsEnumerable("b"); // false

4.Object.create(): 创建一个对象,并且指定该对象的 prototype

var obj = Object.create(
  {},
  {
    a: {
      value: 3,
      writable: true,
      enumerable: true,
      configurable: true,
    },
    b: {
      value: 4,
    },
  }
);
console.log(obj); // {a:3, b:4}

指定原型是原始类型值,抛出异常 Uncaught TypeError: Object prototype may only be an Object or null: undefined

Object.assign()方法使用场景

  1. 原型方法的扩展(往原型上增加属性)
var age = 1;
function Person() {}
Object.assign(Person.prototype, {
  eat: function () {},
  age,
});
console.log(Person.prototype);
  1. 克隆拷贝对象
const obj = { a: 1, b: 2 };
const target = {};
Object.assign(target, obj);
console.log(target); // {a:1, b:2}
  1. 默认值
const Default = {
  url: {
    host: "www.baidu.com",
    port: 7070,
  },
};
function test(option) {
  option = Object.assgin({}, Default, option);
}
test({ url: { port: 8080 } });

Object.defineProperties Object.getOwnPropertyDescriptors 方法

Object.defineProperties 定义多个属性,Object.getOwnPropertyDescriptors 获取对象上所有属性的描述集合

var obj = {};
Object.defineProperties(obj, {
  a: {
    value: true,
    writable: true,
  },
  b: {
    value: "hello",
    writable: false,
  },
});
Object.getOwnPropertyDescriptors(obj);

Object.assign()方法拷贝取值函数和赋值函数

const sourse = {
  set foo(value) {
    console.log(value);
  },
};

const tar = {};
Object.defineProperties(tar, Object.getOwnPropertyDescriptors(sourse));
console.log(Object.getOwnPropertyDescriptors(tar));

Object.getOwnPropertyDescriptors()实现浅克隆

var obj = { a: 1, b: 2, c: 3 };
const clone = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);

部署对象方式

// 1
const obj = { a: 1 };
// 2
const obj = Object.create(prototype);
obj.foo = 123;
// 3
const obj = Object.assign(Object.create(prototype), { foo: 123 });
// 4
const obj = Object.create(
  prototype,
  Object.getOwnPropertyDescriptors({
    foo: 123,
  })
);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值