记录对象拓展

96 阅读5分钟

Object.defineProperty(obj, key, descriptor)

在一个对象上定义或修改一个属性
返回修改后的对象

属性:

  • obj 要修改或定义属性的对象
  • key 要修改或定义的属性的名称
  • descriptor 要定义或修改的属性的描述符

下面在 obj 上定义一个不可赋值的属性 “name”:

const obj = {};
Object.defineProperty(obj, 'name', {
    writable: false,
    value: 'link'
})
obj.name = 'ok'; // 由于 obj.name 不可写入,所以该赋值无效
console.log(obj.name); // link

Object.create(proto, properties)

创建一个新对象
第一个参数会被作为新对象的原型
第二个参数是想要添加在新对象自身的属性,是可枚举的
let _proto = {
    sayHello(){
        console.log('Hello ', this.name)
    }
}
let o = Object.create(_proto, {
    'name': {
        writable: true,
        configurable: true,
        value: "link",
    }
})
o.sayHello(); // Hello link

可以用来创建一个纯净的对象,下面创建出来的对象是没有 toString 方法的

let o = Object.create(null)
o.toSrting; // undefined

Object.getOwnPropertyDescriptor(obj, 'key');

获取对象属性的描述
  • obj 目标对象
  • key 要获取描述的对象的属性

image.png

上面的代码中显示了 obj.name 的属性

  • enumerable 可枚举性
    • true 可枚举
    • false 不可枚举

如果属性设置了不可枚举,那么以下操作将会忽略该属性:

  • for...in 循环遍历可枚举属性
  • Object.key() 返回对象可枚举属性的值
  • JSON.stringify() 将对象转换为字符串对象,如果有不可枚举的属性会被忽略
  • Object.assign() 浅拷贝对象,忽略不可枚举的对象

如下: obj 对象原型上的 toString 就是不可枚举的,所以我们使用 for...in 是不能遍历到它的。

image.png

ES6 规定,所有的 Class 原型方法都是不可枚举的

属性的遍历

  • for...in
    • 遍历对象自身和继承来的可枚举的属性,不包含 Symbol 属性
  • Object.keys(obj)
    • 返回一个包含对象自身的可枚举的键的数组
  • Object.getOwnPropertyNames(obj)
    • 返回一个包含对象自身的键,包括不可枚举的键的数组,但不包括 Symbol 属性
  • object.getOwnPropertySysbols(obj)
    • 返回一个包含对象自身所有的 Symbol 属性的数组
  • Reflect.ownKeys(obj)
    • 返回一个包含对象自身所有属性的键名的数据,不管是否可枚举或是否是 Symbol 属性。

super 关键字

this 关键字总是指向函数所在的当前对象。
super 关键字指向当前对象的原型对象
super 关键字表示原型对象时,只能写在对象的方法之中。

super 关键字表示原型对象时,只能写在对象的方法之中。 以下下都是错误写法:

// 报错,不能写在属性之中
const obj = {
    foo: super.foo
}

// 报错
// 这个为什么报错呢,因为他是写在函数当中,然后再把这个函数赋值 foo
const obj = {
    foo: function(){
        return super.foo
    }
}

下面是正确写法

  • 目前只有对象方法的简写可以让 JavaScript 引擎确认定义的是对象方法。
const obj = {
    foo: 'world',
    find() {
        console.log(super.foo);
    }
};
obj.__proto__.foo = "Hello World";
obj.find(); // Hello World

链判断运算符(ES2020)

直接在链式的时候判断左侧的对象是否为 nullundefined,如果是,直接返回 undefined,不在继续往下走。

如下:常见做法就是一层一层的 &&。

const name = (
    message 
    && message.body
    && message.body.user
    && message.body.user.name) || undefined;
)

ES2020写法

const name = message?.body?.user?.name || undefined;

Object.is()

用来比较两个值是否严格相等
与严格比较符行为基本相等,但是有两个不同之处
    1Object.is(-0, 0)  // false
       +0 === -0  // true
    2Object.is(NaN, NaN) // true
       NaN === NaN  // false
Object.is('foo', 'foo'); // true
Object.is({}, {}); // false

Object.assign()

用于合并对象,将源对象(用来合并的对象)的所有可枚举属性复制到目标对象
是浅拷贝
如果参数不是对象,会先转换为对象
let a = {name: 'link'}
let b = {age: 17}
let c = {gender: 'boy'}

Object.assign(a, b, c)
console.log(a) // { name: 'link', age: 17, gender: 'boy' }

如果参数不是对象,自动转换为对象再合并

let a = Object.assign({}, 'hello')
console.log(a); // { '0': 'h', '1': 'e', '2': 'l', '3': 'l', '4': 'o' }

如果被合并的源是其他类型的值,那么只有字符串会被转换为数组形式合并进来,其他的会被忽略

let a = Object.assign({}, 1,2,true, 'hello')
console.log(a);
// { '0': 'h', '1': 'e', '2': 'l', '3': 'l', '4': 'o' }

同名属性会被后面合并进来的替换

let a = {
    name: 'ok'
}

let b = {
    name: 'link'
}

Object.assign(a, b)
console.log(a);
// { name: 'link' }

如果参数都是数组

如果参数都是数组,会把数组中元素的下标当做键,然后合并

let a = [1, 2, 3];
let b = [4, 5];
Object.assign(a, b)
console.log(a);
// [ 4, 5, 3 ]

Object.getOwnPropertyDescriptors() 【ES2017】

返回某个对象的所有自身属性的描述对象
与 Object.getOwnPropertyDescript() 的区别是:
    Object.getOwnPropertyDescriptor() 只能获取对象自身的某个属性的描述
    Object.getOwnPropertyDescriptors() 是获取对象自身的所有属性的描述
{
  name: {
    value: 'link',
    writable: true,
    enumerable: true,
    configurable: true
  },
  bar: {
    get: [Function: get bar],
    set: undefined,
    enumerable: true,
    configurable: true
  }
}

可以用来解决 Object.assign() 无法合并 get 和 set 属性

const a = {
    set foo(v){
        console.log(v);
    }
}

const t = Object.assign({}, a)
console.log(t); // { foo: undefined }

使用 Object.getOwnPropertyDescriptots() 配合 Object.defineProperties() 来解决这个问题

const a = {
    set foo(v){
        console.log(v);
    }
}

const t = Object.defineProperties({}, Object.getOwnPropertyDescriptors(a))
console.log(t);  // { foo: [Setter] }

__ proto __ 属性

该属性没有写入 ES6 正文,只写入到附录中,标准明确规定浏览器必须部署这个属性,
而其他环境就可以不部署,所以我们尽量忽略这个属性

__ proto __ 使实例能够访问到 prototype

function Parent (){}

const p = new Parent()

console.log(p.__proto__ === Parent.prototype)

Object.setPrototypeOf(object, prototype)

Object.setPrototypeOf() 用来设置对象的原型对象,返回参数本身 
Object.setPrototypeOf(object, prototype)

// 等价于

function(object, prototype){
    object.__proto__ = prototype;
    return object;
}

Object.getPrototypeOf(obj)

Object.getPrototypeOf() 用于获取对象的原型
function Parent(){}
const p = new Parent()
Object.getPrototypeOf(p) === Parent.prototype; // true

Object.keys()

返回一个包含对象自身的可枚举属性的数组
var obj = { foo: 'bar', baz: 'bar' };
Object.keys(obj)
// ["foo", "baz"]

Object.values()

返回一个包含对象自身的可枚举值的数组
var obj = { foo: 'bar', baz: 'opq' };
Object.values(obj)
// ["bar", "opq"]

Object.entries()

返回一个数组,元素是对象的的键值对数组
var parent = {
    name: 'link',
    age: 12
}
Object.entries(parent) // [ [ 'name', 'link' ], [ 'age', 12 ] ]

Object.fromEntries()

Object.entries 的方向操作,将一个键值对数组转换为对象
Object.fromEntries([  ['foo', 'bar'],
  ['baz', 42]
])
// { foo: "bar", baz: 42 }