获取取值函数(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
- 点语法扩展属性配置项
var obj = {};
obj.a = 2;
var descriptor = Object.getOwnPropertyDescritor(obj, "a");
console.log(descriptor); // { value: 2, writable: true, enumerable: true, configurable: true }
- 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()方法,只不过二者对属性修饰符对象属性做出的设置不同而已。
特点
- 密封后的对象,与需要密封的对象相同
var obj = { a: 2 };
var newObj = Object.seal(obj);
console.log(newObj === obj); // true
- 对象密封后,通过点语法尝试扩展对象,扩展程序静默失败,不会抛出异常
var obj = { a: 2 };
Object.seal(obj);
obj.b = 3; // 静默失败
console.log(obj); // {a:2}
- 对象密封后,通过 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
- 严格模式下,通过对象点语法进行对象扩展,程序会抛出异常 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()方法使用场景
- 原型方法的扩展(往原型上增加属性)
var age = 1;
function Person() {}
Object.assign(Person.prototype, {
eat: function () {},
age,
});
console.log(Person.prototype);
- 克隆拷贝对象
const obj = { a: 1, b: 2 };
const target = {};
Object.assign(target, obj);
console.log(target); // {a:1, b:2}
- 默认值
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,
})
);