- [] 表达式
- 属性描述符
- 新增方法
- Object.preventExtensions(object) / Object.isExtensible(object)
- Object.seal(object) / Object.isSealed(object)
- Object.freeze(object) / Object.isFrozen(object)
- Object.is(param, param)
- Object.assign(target,sources)
- Object.setPrototypeOf() / Object.getPrototypeOf()
- Object.keys() / Object.values() / Object.entries()
- super
[] 表达式
在ES5
中相关给对象新增一个属性可以通过.
的方式来操作:
var name = "张三";
var person = {
name: name
};
ES6
在对象属性名和变量同名的时候可以简写:
var name = "张三";
var person = {
name
};
对象方法也可以进行简写:
var name = "张三";
var person = {
name,
eat(){} // eat: function(){} 一样
};
ES6
容许定义对象属性的时候使用[]
放入表达式:
let name = "张三";
var person = {
["na"+"me"]: name; // 和 name: name 一样
};
使用[]
表达式获取属性的时,可以通过变量来访问:
let name = "n_a_m_e";
let obj = {};
obj[name] = 'zhangsan';
console.log(obj); // {n_a_m_e: 'zhangsan'}
console.log(obj[name]); // zhangsan
[]
底层都是通过toString()
方法进行转换:
var myObject = {};
myObject[true] = "foo";
myObject[3] = "bar";
myObject[myObject] = "baz";
console.log(myObject["true"]); // foo
console.log(myObject["3"]); // bar
console.log(myObject["[object Object]"]); // baz
consoloe.log(Object.prototype.toString.call(myObject)); // [object Object]
所以以下情况将会覆盖:
const a = { a: 1 };
const b = { b: 2 };
const obj = {
[a]: "valueA",
[b]: "valueB",
};
console.log(obj); // {[object Object]: 'valueB'}
属性描述符
ES5
通过Object.defineProperty()
方法来对对象的属性进行描述定义,ES6
新增了Object.getOwnPropertyDescriptor
来获取属性的描述。
Object.defineProperty() / defineProperties()
let obj = { a: 1 };
console.log(Object.getOwnPropertyDescriptor(obj, "a"));
// {value: 1, writable: true, enumerable: true, configurable: true}
// writable可写 enumerable可枚举 configurable可配置(可删除) value值
let obj = {};
Object.defineProperty(obj, "a", {
value: 10,
writable: false,
enumerable: true,
configurable: false, // configurable 为false 的时候不能删除
});
// writable:false 且 configurable:false 的时候不能更改属性
console.log(Object.getOwnPropertyDescriptor(obj, "a"));
新增方法
之前我们学习过ES5
通过Object.defineProperty()
可以对象的对象进行描述定义
var obj = {};
Object.defineProperty(obj, "name", {
value: "张三",
enumerable: false,
writable: false,
configurable: false,
});
console.log(obj); // {name: '张三'}
obj.name = "李四";
console.log(obj); // {name: '张三'}
for (const key in obj) {
console.log(key); // 空的,for 循环不会执行
}
delete obj.name;
console.log(obj); // {name: '张三'}
obj.age = 20;
console.log(obj); // {age: 20, name: '张三'}
可以看到以上的代码,我们对obj
的name
属性进行了定义,这个时候name
属性不能更改、枚举、删除,但是我们却可以为obj
对象新增属性。
Object.preventExtensions(object) / Object.isExtensible(object)
:::info
**Object.preventExtensions(object)**
用来禁止对象拓展,使用该方法后对象不可新增属性,可以更改、删除和枚举属,返回当前对象。Object.isExtensible(object)
用来判断一个对象是否可以拓展,返回布尔值
:::
let obj = { a: 10 };
let res = Object.preventExtensions(obj);
console.log(Object.isExtensible(obj)); // flase
console.log(obj === res); // true
console.log(obj); // { a: 10 }
obj.a = 11;
obj.b = 20;
console.log(obj); // { a: 11 };
// 使用 defineProperty 会报错!
// Cannot define property c, object is not extensible
Object.defineProperty(obj, "c", {
value: 30,
});
可以看到obj
对象不能新增属性了。
// 对数组进行包装
var arr = [];
Object.preventExtensions(arr);
console.log(arr);
arr.push(1); // Cannot add property 0, object is not extensible
console.log(arr);
Object.seal(object) / Object.isSealed(object)
:::info
**Object.seal(object)**
用来密封对象,使用该方法后对象不可新增、删除属性,可以更改、枚举属性,返回当前对象。Object.isSealed(object)
用来判断一个对象是否被密封,返回布尔值
:::
let obj = { name: "李四" };
let res = Object.seal(obj);
console.log(Object.isSealed(obj)); // true
console.log(obj === res); // true
obj.age = "30";
console.log(obj); // {name: '李四'}
obj.name = "王五";
console.log(obj); // {name: '王五'}
delete obj.name;
console.log(obj); // {name: '王五'}
for (const key in obj) {
console.log(obj[key]); // 王五
}
Object.freeze(object) / Object.isFrozen(object)
:::info
**Object.freeze(object)**
用来冻结对象,使用该方法后对象不可新增、更改、删除属性,可以枚举属性,返回当前对象。Object.isFrozen(object)
用来判断一个对象是否被冻结,返回布尔值
:::
let obj = { name: "李四" };
let res = Object.freeze(obj);
console.log(Object.isFrozen(obj)); // true
console.log(obj === res); // true
obj.age = "30";
console.log(obj); // {name: '李四'}
obj.name = "王五";
console.log(obj); // {name: '李四'}
delete obj.name;
console.log(obj); // {name: '李四'}
for (const key in obj) {
console.log(obj[key]); // 李四
}
:::danger
⚠️ 注意
以上三个方法都是浅拷贝!!!需要自己进行封装递归。
:::
var obj = {
name: "张三",
age: 40,
son: {
name: "李四",
},
};
Object.freeze(obj);
obj.age = 50;
obj.son.age = 20;
console.log(obj); // {name: '张三', age: 40, son: {name: '李四', age: 20}}
封装一个简单的冻结方法:
function myFreeze(obj) {
Object.freeze(obj);
for (const key in object) {
if (Object.hasOwnProperty.call(object, key)) {
if (typeof obj[key] === "object" && obj[key] !== null) {
myFreeze(obj[key]);
}
}
}
}
Object.is(param, param)
在ES5
的时候值比较的时候有两个bug
:
1、NaN
不和任何值相等,包括自己
2、-0
和+0
相比返回真值Object.is()
方法就是用来比较两个值是否相等。
console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(-0, +0)); // false
console.log(Object.is({}, {})); // false
console.log(Object.is(false, 0)); // false
console.log(Object.is(undefind, null)); // false
Object.assign(target,sources)
:::info
Object.assign
用于将多个对象进行合并,返回目标对象。
:::
target
合并到哪里,sourses
把谁进行合并
let target = {};
let obj = { a: 1 };
let ob2 = { b: 2 };
let res = Object.assign(target, obj, ob2);
console.log(target); // { a: 1; b: 2 };
console.log(target === res); // true
同名的属性,后面的属性会覆盖前面的属性:
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}
当target
是undefind
或null
的时候,无法进行合并
Object.assign(undefined, { a: 1 });
// Cannot convert undefined or null to object
当target
是基本数据的时候,会进行将数据进行包装类
let res = Object.assign(1, { a: 1 });
console.log(res); // { 1 , a: 1 }
let res1 = Object.assign(true, { a: 1 });
console.log(res1); // { true , a: 1 }
当source
是undefind
或null
的时候会静默失败
let res = Object.assign({}, undefined, null, { a: 1 });
console.log(res); // {a: 1}
当source
不能进行枚举遍历的时候,也会静默失败
let res = Object.assign({}, 1);
let res1 = Object.assign({}, "123");
console.log(res); // {}
console.log(res1); // {0: '1', 1: '2', 2: '3'}
var obj = { a: 3 };
Object.defineProperty(obj, "b", {
value: 4,
});
let res = Object.assign({}, obj);
console.log(res); // {a: 3}
不能克隆原型上的属性
Object.create
的第二个参数也是对属性进行描述定义
var obj = Object.create(
{ foo: 1 },
{
bar: {
value: 2,
},
baz: {
value: 3,
enumerable: true,
},
}
);
console.log(obj); // {baz: 3, bar: 2}
console.log(obj.foo); // 1
let res = Object.assign({}, obj);
console.log(res); // {baz: 3}
console.log(res.foo); // undefined
Symbol
数据类型也可以进行合并
let res = Object.assign({ a: 1 }, { [Symbol("b")]: 2 });
console.log(res); // {a: 1, Symbol(b): 2}
⚠️ 注意:合并的数据都是浅拷贝
const obj1 = { a: { b: 2 } };
const obj2 = Object.assign({}, obj1);
obj1.a.b = 20;
console.log(obj2); // {a: {b: 20}}
⚠️ 注意:当合并数组的时候,是根据数组的下标进行合并,因为数组是一种特殊的对象,下标就是数组元素的key
值
let res = Object.assign([1, 2, 3], [4, 5]);
console.log(res); // [4, 5, 3]
给Object.assign
设置默认值:
function test(option) {
let defaultObj = {
url: "www.baidu.com",
method: "post",
};
option = Object.assign({}, defaultObj, option);
return option;
}
let res = test({
url: "www.zhidao.com",
method: "get",
});
console.log(res); // {url: 'www.zhidao.com', method: 'get'}
Object.setPrototypeOf() / Object.getPrototypeOf()
:::info
用于设置/获取对象的原型
:::
在ES5
的时候想要获取对象的原型需要使用隐式属性__proto__
,__proto__
有一下几个缺点:
1、不够语义化
2、访问速度比较慢
3、当修改原型的时候所有继承自该原型的对象都会受到影响
所以ES6
新增了Object.getPrototypeOf()
用来获取对象的原型。
let obj = {};
let res = Object.getPrototypeOf(obj);
console.log(res)
Object.setPrototypeOf()
用来设置一个对象的原型,返回原对象。
let obj = {};
let res = Object.setPrototypeOf(obj, { b: 20 });
console.log(obj, res, obj === res); // true
当目标不是对象的时候,会进行包装类进行转换,然后静默失败:
let num = 1;
let res = Object.setPrototypeOf(num, { b: 20 });
console.log(Object.getPrototypeOf(num));
string
、boolean
都会如number
数据类型一样静默失败,但是当目标是undefind
或null
的时候,会抛出异常:
Object.setPrototypeOf(undefined, { b: 20 });
// Object.setPrototypeOf called on null or undefined
Object.keys() / Object.values() / Object.entries()
:::info
Object.keys()
以数组的形式返回对象的key
Object.values()
以数组的形式返回对象的value
Object.entries()
以二维数组的形式返回对象的key
、value
⚠️ 注意
以上三个方法只返回可枚举的属性、非原型上的属性
:::
const foo = {
a: 10,
b: 20,
c: 30,
};
Object.defineProperty(foo, "d", { value: 40 });
Object.setPrototypeOf(foo, {j: 100, z: 200 });
console.log(foo);
不会返回原型上的属性,也不会返回不可枚举的属性。
console.log(Object.keys(foo)); // ['a', 'b', 'c']
console.log(Object.values(foo)); // [10, 20, 30]
console.log(Object.entries(foo)); // [['a', 10], ['b', 20], ['c', 30]]
方法也可以用于其他数据类型:
console.log(Object.keys([1, 2, 3])); // ['0', '1', '2'],数组的下标
console.log(Object.keys(1)); // []
console.log(Object.keys(true)); // []
console.log(Object.keys("abc")); // ['0', '1', '2']
super
:::info
super
和this
类似,但是「指向」不同。super
指向对象的原型对象,this
的指向之前说过很多次,这里不再赘述。
⚠️ 注意
使用super
有以下限制:
1、必须是对象的方法
2、方法必须是简写的形式
:::
let proto = {
b: 20,
bar() {
console.log("this is proto: " + this.b);
},
};
let obj = {
a: 10,
// foo: super.y, // 属性不行
// 普通函数不能使用
// foo: function() {
// console.log(super.b); // 报错
// },
// 箭头函数不能使用
// foo: () => {
// console.log(super.b); // 报错
// },
// 必须是对象的方法,方法必须是简写的方式才能使用 super
foo() {
console.log(super.b);
super.bar();
},
};
Object.setPrototypeOf(obj, proto);
console.log(obj);
obj.foo();
更多细节: