描述

在JavaScript中,几乎所有的对象都是Object类型的实例,它们都会从Object.prototype继承属性和方法。Object 构造函数为给定值创建一个对象包装器。Object构造函数,会根据给定的参数创建对象,具体有以下情况:

  1. 如果给定的值是 null 或 undefined, 将会创建并返回一个空对象
  2. 如果传的是一个基本数据类型的值, 则会构造其包装类型的对象
  3. 如果传的是引用类型的值, 会返回这个值, 经他们复制的变量保有和源对象相同的引用地址

属性

  • length

Object.length 值为 1

  • prototype

Object.prototype 可以为所有的 Object 类型的对象添加属性

方法

  • assign()

复制一个或多个对象来创建一个新的对象

let obj = { a: 1 }
let obj1 = { b: 2 }
let obj2 = { c: 3 }
Object.assign(obj, obj1, obj2)
console.log(obj) // {a: 1, b: 2, c: 3}

注意:若目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性覆盖前面的属性

  • create()

创建一个新的对象, 将现有对象作为新对象的 proto

let obj = {
    name: 'tmc',
    age: 26
}

const newObj = Object.create(obj)
console.log(newObj) // {}
console.log(newObj.__proto__) // { name: 'tmc', age: 26 }
console.log(newObj.name, newObj.age) // tmc 26
  • defineProperty()

在一个对象上定义一个新属性,或者修改一个已经存在的属性

语法:

Object.defineProperty(obj, prop, desc)

名称解释:

  1. obj 需要定义属性的当前对象
  2. prop 当前需要定义的属性名
  3. desc 属性描述符

JavaScript 中有三种类型的属性:

  1. 命名数据属性:拥有一个确定的值的属性。这也是最常见的属性
  2. 命名访问器属性:通过getter和setter进行读取和赋值的属性
  3. 内部属性:由JavaScript引擎内部使用的属性,不能通过JavaScript代码直接访问到,不过可以通过一些方法间接的读取和设置。如:每个对象都有一个内部属性[[Prototype]],你不能直接访问这个属性,但可以通过Object.getPrototypeOf()方法间接的读取到它的值

属性描述符:有两种形式,数据描述符和存取描述符, 且两者不能共用

  1. 数据描述符 (value, writable)
  2. 存取描述符 (get, set)
属性名 默认值
value undefined
get undefined
set undefined
writable false
enumerable false
configurable false

注意:数据描述符和存取描述符都可以使用 configurable 和 enumerable 描述符

  • defineProperties()

在一个对象上定义一个或多个新的属性或修改现有属性,并返回该对象

语法:

Object.defineProperties(obj, props)

名称解释:

  1. obj:被添加属性或修改属性的对象
  2. props:该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置
let obj = {};
Object.defineProperties(obj, {
    name: {
        value: '小梦',
        configurable: false,
        writable: true,
        enumerable: true
    },
    age: {
        value: 25,
        enumerable: true
    }
})
console.log(obj) // { name: '小梦', age: 25 }
  • keys()

语法:

Object.keys(obj)

名称解释:

  1. obj: 要返回其枚举自身属性的对象
  2. 返回值:给定对象的所有可枚举属性的字符串数组

处理对象, 返回可枚举的属性数组

let person = { name: "小梦", age: 25, address: "深圳", getName: function() {} };
console.log(Object.keys(person)); // [ 'name', 'age', 'address', 'getName' ]

处理数组,返回索引值数组

let arr = [ 1, 2, 3, 4, 5, 6 ];
console.log(Object.keys(arr)); // [ '0', '1', '2', '3', '4', '5' ]

处理字符串,返回索引值数组

let str = "tmcsfm";
console.log(Object.keys(str)); // [ '0', '1', '2', '3', '4', '5' ]
  • values()

只会遍历对象自身的可遍历属性, 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值

let obj = {
    name : "tmc",
    age : 25
};
console.log(Object.values(obj)); // [ 'tmc', 25 ]

返回数组的成员顺序,与属性的遍历部分介绍的排列规则一致

const obj = { 100: 'a', 8: 'b', 12: 'c' };
console.log(Object.values(obj)) // [ 'b', 'c', 'a' ]

注意:属性名为数值的属性,是按照数值大小,从小到大遍历的。故,返回的顺序是b、c、a

Object.values() 只会遍历对象自身的可遍历属性

let obj = Object.create({}, { age: { value: 25 }})
console.log(Object.values(obj)) // []

因为属性描述符的 enumerable 的默认值为 false, 所以返回空数组
let obj = Object.create({}, { age: { value: 25, enumerable: true }})
console.log(Object.values(obj)) // [ 25 ]

Object.values会过滤属性名为 Symbol 值的属性

let obj = {
    [Symbol()]: 111,
    name: '222'
}
console.log(Object.values(obj)) // [ '222' ]

如果传的是字符串,会返回各个字符组成的一个数组
console.log(Object.values('123')) // [ '1', '2', '3' ]

如果参数不是对象,Object.values会先将其转为对象

console.log(Object.values(66));  // []
console.log(Object.values(true));  // []
console.log(Object.values(undefined));   // error
console.log(Object.values(null));   // error
  • entries()

Object.entries 方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组

let obj = {
    name: 'txm',
    age: 25
}
console.log(Object.entries(obj)) // [ [ 'name', 'txm' ], [ 'age', 25 ] ]

原对象的属性名是一个 Symbol 值,该属性会被省略

let obj = {
    name: 'txm',
    age: 25,
    [Symbol()]: '武汉人'
}
console.log(Object.entries(obj)) // [ [ 'name', 'txm' ], [ 'age', 25 ] ]


同样,遍历也会过滤 Symbol 属性名
for(let [key, value] of Object.entries(obj)) {
    console.log(`${JSON.stringify(key)} : ${JSON.stringify(value)}`)
}
/**
 * "name" : "txm"
 * "age" : 25
 */ 

将对象转为真正的Map结构

let obj = {
    name: 'txm',
    age: 25
}
const map = new Map(Object.entries(obj))
console.log(map) // Map { 'name' => 'txm', 'age' => 25 }

实现Object.entries方法

const entries = (obj) => {
    let result = [];
    const objType = typeof(obj);
    if(obj === undefined || obj === null) {
        throw new TypeError();
    }
    if(objType === 'number' || objType === 'boolean') {
        return [];
    }
    for(let k of Object.keys(obj)) {
        result.push([k, obj[k]]);
    }
    return result
}
console.log(entries(obj)) // [ [ 'name', 'txm' ], [ 'age', 25 ] ]
  • freeze()

冻结对象:其他代码不能删除或更改任何属性

  • getOwnPropertyDescriptor()

返回对象指定的属性描述对象 (descriptor)

let obj = {
    name: 'txm',
    age: 25
}
console.log(Object.getOwnPropertyDescriptor(obj, 'name'))

{ value: 'txm',
  writable: true,
  enumerable: true,
  configurable: true }

ES2017 中引入了 Object.getOwnPropertyDescriptors(), 返回指定对象所有的自身属性(非继承属性)的描述对象

console.log(Object.getOwnPropertyDescriptors(obj))
{ name:
   { value: 'txm',
     writable: true,
     enumerable: true,
     configurable: true },
  age:
   { value: 25,
     writable: true,
     enumerable: true,
     configurable: true } }

====> 实现一个getOwnPropertyDescriptors方法
function getOwnPropertyDescriptors(obj) {
    const result = {};
    for(let key of Reflect.ownKeys(obj)) {
        result[key] = Object.getOwnPropertyDescriptor(obj, key);
    }
    return result;
}

解决Object.assign()无法正确拷贝get属性和set属性的问题?

const obj = {
    set name(value) {
        console.log(value)
    }
}

const target = {};
Object.assign(target, obj)
console.log(target)
console.log(Object.getOwnPropertyDescriptor(target, 'name'))

/**
 * { name: undefined }
 * { value: undefined,
 * writable: true,
 * enumerable: true,
 * configurable: true }
 */
obj 对象的 name 属性的值是一个赋值函数,Object.assign 方法将这个属性拷贝给 target 对象,结果该属性的值变成了undefined。这是因为Object.assign方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法

Object.getOwnPropertyDescriptors 方法配合 Object.defineProperties 方法,就可以实现正确拷贝

const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(obj));
console.log(target2)
console.log(Object.getOwnPropertyDescriptor(target2, 'name'))

/**
 * { name: [Setter] }
 * { get: undefined,
 * set: [Function: set name],
 * enumerable: true,
 * configurable: true }
 */
  • getOwnPropertyNames()

返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名, 不包括 Symbol 属性名

语法:

Object.getOwnPropertyNames(obj)
let arr = ['a', 'b', 'c']
console.log(Object.getOwnPropertyNames(arr)) // [ '0', '1', '2', 'length' ]

// 类数组对象
let likeArray = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.getOwnPropertyNames(likeArray)) // [ '0', '1', '2' ]
  • getOwnPropertySymbols()

返回一个数组,它包含了指定对象自身所有的符号属性

  • getPrototypeOf()

返回指定对象的原型对象

  • is()

比较两个值是否相同。所有 NaN 值都相等(这与==和===不同)

  • isExtensible()

判断对象是否可扩展

  • isFrozen()

判断对象是否被冻结

  • isSealed()

判断对象是否已经密封

  • preventExtensions()

防止对象的任何扩展

  • seal()

防止其他代码删除对象的属性

  • setPrototypeOf()

设置对象的原型(即内部 [[Prototype]] 属性)