Object.getOwnPropertyDescriptors()
ES5中Object.getOwnPropertyDescriptor 返回对象中某个属性的详细描述对象,ES6引入Object.getOwnPropertyDescriptors 返回对象中所有属性(非继承)的详细描述对象
let obj = {
foo : '123',
get bar () {
return 'abc'
}
}
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
上面代码返回了obj所有属性的详细描述
该方法的实现也是很简单
function getOwnPropertyDescriptors (obj) {
// 放置对象属性的描述对象的容器
const result = {}
for (let key of Reflect.ownKeys(obj)) {
// key 代表每一次循环时调用完拿到的对象键值
// Reflect.ownKeys 除了继承属性纤细描述都会返回
// 拿出每个属性的详细描述,放到之前准备好的容器中
result[key] = Object.getOwnPropertyDescriptor(obj, key)
}
// 把这个对象全部的描述抛出
return result
}
该方法的目的,主要用于解决Object.assign 无法正确拷贝set 和 get方法 ,因为 assign 总是拷贝值, 而不会管对象内容里是函数
let source = {
set foo (value) {
console.log(value)
}
}
let target = {}
Object.assign(target, source);
Object.getOwnPropertyDescriptor(target, 'foo')
// { value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }
上面的foo 属性描述对象中value之所以是undefined,是因为 Object.assign() 总是返回值,而不会考虑对象中是函数
这时,使用Object.defineProperties 和 Object.getOwnPropertyDescriptors 就能达到理想结果
let source = {
set foo (value) {
console.log(value)
}
}
let target = {}
// 重新使用Object.getOwnPropertyDescriptors 定义属性描述
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source))
// getOwnPropertyDescriptors定义的描述对象中 有着set get详细描述
Object.getOwnPropertyDescriptor(target, 'foo')
// { get: undefined,
// set: [Function: set foo],
// enumerable: true,
// configurable: true }
上面代码中,两个对象合并的逻辑可以写成一个函数
let source = (target, origin) => {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(origin)
)
}
Object.getOwnPropertyDescriptors 方法的另一个用处就是配合 Object.create 方法, 将对象克隆到另一个对象,这属于浅拷贝
let clone =
// 创建一个对象,拿到obj的原型,再拿到obj的属性详细描述对象
Object.create(Object.getPrototypeOf(obj),
// 然后返回这个对象,但这是一个浅拷贝
Object.getOwnPropertyDescriptors(obj))
另外,Object.getOwnPropertyDescriptors 可以实现继承
以前的写法
let obj = {
__proto__ : obj1,
foo : 123
}
ES6 规定__proto__只有浏览器要部署,其他环境不用部署。如果去除__proto__,上面代码就要改成下面这样。
let obj = Object.create(obj1);
obj.foo = 123;
// 或
let obj = Object.assign(
Object.create(obj1),
{
foo : 123
}
)
有了 Object. getOwnPropertyDescriptors 就可以这样写
let obj = Object.create(
obj1,
Object.getOwnPropertyDescriptors({
foo : 123
})
)
__proto__属性,Object.setPrototypeOf(), Object.getPrototypeOf()
__proto__属性
用来读取当前对象的原型对象
// ES5
let obj = {
method () {
//...
}
}
// 设置原型对象指向
obj.__proto__ = newObj
// ES6
// 创建原型对象
let obj = Object.create(newObj);
obj.method = function () {}
ES6规定,严格意义上没有必要使用__proto__ ,因为它是一个内部属性,并且不是一个对外的API。
只有浏览器必须部署这个属性,其他环境不需要。
所以读取操作,使用getPrototypeOf, setPrototypeOf
创建使用 Object.create
如果一个对象本身部署了__proto__ ,该属性指向的就是原型对象
Object.getPrototypeOf({__proto__: null})
// null
Object.setPrototypeOf()
Object.setPrototypeOf() 与 proto 作用一样,都可以用来设置原型对象,它是ES6推荐的
// 格式
Object.setPrototypeOf(object, prototype)
// 写法
let o = Object.setPrototypeOf({}, null)
模拟源码
function setPrototypeOf (obj, proto) {
obj.__proto__ = proto;
return obj
}
写一个例子
let object = {
name : 'wei'
}
let proto = {}
Object.setPrototypeOf(object, proto)
proto.sex = 'male';
proto.car = 'BMW';
object.name // 'wei'
object.sex // 'male'
object.car // 'BMW'
上面的object对象可以访问到proto里的属性,因为proto是祖先
如果第一个参数不是对象,会转为对象,但是返回的还是第一个参数的值,这么做毫无意义,了解就好
Object.setPrototypeOf(1, {}) === 1 // true
Object.setPrototypeOf(false, {}) === false // true
Object.setPrototypeOf('foo', {}) === 'foo' // true
由于undefined和null 无法转为对象,所以第一个参数是undefined或者null 就会报错。
Object.setPrototypeOf(undefined, proto)
// error
Object.setPrototypeOf(null, proto)
// error
Object.getPrototypeOf()
该方法与Object.setPrototypeOf 方法配套,用于读取原型对象
Object.getPrototypeOf(object)
例子
function Person () {}
let person = new Person()
Object.getPrototypeOf(person) === Person.prototype
// true
// 修改原型指向
Object.setPrototypeOf(person, Object.prototype)
Object.getPrototypeOf(person) === Person.prototype
// false
Object.getPrototypeOf(person) === Object.prototype
// true
如果参数不是对象,会把自动转为对象,转不了的就报错(undefined, null)
Object.getPrototypeOf(1);
// Number {[[PrimitiveValue]]: 0}
Object.getPrototypeOf(false);
// Boolean {[[PrimitiveValue]]: false}
Object.getPrototypeOf('str');
// String {length: 0, [[PrimitiveValue]]: ""}
Object.getPrototypeOf(1) === Number.prototype
// true
Object.getPrototypeOf('STR') == String.prototype
// true
Object.getPrototypeOf(false) === Boolean.prototype
// true
Object.getPrototypeOf(undefined)
// error
Object.getPrototypeOf(null)
// error
上面代码都会返回自己对象的类型原型对象,只有null和undefined会报错, 因为它们压根就没原型,且转不了对象。
null.prototype
// TypeError: Cannot read property 'prototype' of null at
undefined.prototype
// TypeError: Cannot read property 'prototype' of undefined at