Object 是 JavaScript 的一种数据类型。它用于存储各种键值集合和更复杂的实体。可以通过 Object() 构造函数或者使用对象字面量的方式创建对象。

        在 JavaScript 中,几乎所有的对象都是 Object 的实例;一个典型的对象从 Object.prototype 继承属性(包括方法),尽管这些属性可能被覆盖(或者说重写)。唯一不从 Object.prototype 继承的对象是那些 null 原型对象,或者是从其他 null 原型对象继承而来的对象。

        通过原型链,所有对象都能观察到 Object.prototype 对象的改变,除非这些改变所涉及的属性和方法沿着原型链被进一步重写。尽管有潜在的危险,但这为覆盖或扩展对象的行为提供了一个非常强大的机制。为了使其更加安全,Object.prototype 是核心 JavaScript 语言中唯一具有不可变原型的对象——Object.prototype 的原型始终为 null 且不可更改。

        在代码开发中,应该避免调用任何 Object.prototype 方法,特别是那些不打算多态化的方法(即只有其初始行为是合理的,且无法被任何继承的对象以合理的方式重写)。

一. 创建对象

// 1.使用字面量创建对象
const obj1 = { } // 空对象 {}

// 2.使用Object()构造函数创建对象
const obj2 = new Object(); // 空对象 {}
const obj3 = new Object(undefined); // 空对象 {}
const obj4 = new Object(null); // 空对象 {}

二. 对象的基本运算符

.  运算符

可以通过 . 运算符向对象进行增删改查操作 。

const obj = {a:1}
obj.b = 2          // 增 向obj中添加属性b,值位2
delete obj.a       // 删 obj中删除属性a
obj.b = '2b'       // 改 obj中修改属性b的值位'2b'
console.log(obj.b) // 查 获取obj中属性b的值

[] 运算符

用于访问对象的属性,可以使用表达式作为属性名。

const obj = {}
const key = 'a'
obj[key] = 1 // 等同于obj.a = 1
console.log(obj[key])

in 运算符

如果指定的属性或方法在指定的对象或其原型链中,则 in 运算符返回 true。若存在则返回 true,否则返回false。

const sym = Symbol("属性名也可以是Symbol类型")
const fa = {
    a: '属性名可以是字符串',
    [sym]() { }
}
const son = {
    1: '属性名也可以是数值',
    __proto__: fa
}
console.log('a' in son)  // true
console.log(1 in son)    // true
console.log(sym in son)  // true

instanceof 运算符
instanceof
 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。若存在则返回 true,否则返回false。

class Father {}
class Son extends Father {}
const f1 = new Father(),s1 =  new Son();
console.log(f1 instanceof Father)   // true
console.log(f1 instanceof Son)      // false
console.log(s1 instanceof Father)   // true
console.log(s1 instanceof Son)      // true

delete 运算符 

delete 运算符用于删除对象的一个属性;如果该属性的值是一个对象,并且没有更多对该对象的引用,该属性所持有的对象最终会自动释放。(垃圾回收机制)

const obj = {
    a:{
        b:1
    }
}
console.log(obj)  // {a: {…}}
delete obj.a
console.log(obj)  // {}

typeof 运算符

typeof 运算符返回一个字符串,表示操作数的类型。当变量为对象时返回 object 。null 也返回object是历史遗留问题,JS使用一个32位的标签来表示数据类型,其中的一些标签被分配给了对象(包括 null)。在这个标签系统中,null 被标记为 0x00,而对象被标记为其他值。因此,当 typeof 操作符被设计时,它检查标签的值,如果是 0x00,则返回 "object"。

const obj = {}
console.log(typeof obj)  // 'object'
console.log(typeof null) // 'object' 很特殊 历史遗留bug

三. 属性描述

        JavaScript 中的对象属性可以包含属性描述符,属性描述符是一组用于定义属性行为的特殊属性。对象中存在的属性描述符有两种主要类型:数据描述符 和 访问器描述符。每个属性描述符都包括以下属性:configurable,enumerable,value,writable,get,set

  • configurable : 如果此属性描述符的类型可以更改并且属性可以从相应的对象中删除,则为 true。默认为 false
  • enumerable : 如果此属性在枚举相应对象的属性时应显示出来,则为 true。默认为 false
  • value : 与属性关联的值。可以是任何有效的 JavaScript 值(数字、对象、函数等)。默认为 undefined。
  • writable : 如果与属性关联的值可以使用赋值运算更改,则为 true。默认为 false
  • get : 作为该属性的 getter 函数,如果没有 getter 则为 undefined。函数返回值将被用作属性的值。默认为 undefined
  • set : 作为该属性的 setter 函数,如果没有 setter 则为 undefined。该函数将只接收一个参数,即被分配给属性的新值。默认为 undefined

        如果描述符没有 valuewritableget 和 set 键中的任何一个,它将被视为数据描述符。如果描述符同时具有 [value 或 writable] 和 [get 或 set] 键,则会抛出异常。

Object.defineProperties 和 Object.defineProperty() 用于在指定对象上定义一个新属性或修改现有属性,并返回此对象。

const obj = {};
Object.defineProperties(obj, {
    key1: {
        configurable: true,
        enumerable: true,
        value: 1,
        writable: true,
    },
    key2: {
        configurable: false,
        enumerable: true,
        value: 2,
        writable: true,
    },
    key3: {
        configurable: true,
        enumerable: false,
        value: 3,
        writable: true,
    },
    key4: {
        configurable: true,
        enumerable: true,
        value: 4,
        writable: false,
    }
});

// value 
console.log(obj) // {key1: 1, key2: 2, key3: 3, key4: 4}

// enumerable
console.log(Object.keys(obj)) // ['key1', 'key2', 'key4']; 因为key3的enumerable描述为false所以key3无法被枚举到

// writable
obj.key1 = 11
obj.key4 = 44
console.log(obj) // {key1: 11, key2: 2, key3: 3, key4: 4}; 因为key4的writable描述为false所以无法修改

// configurable
Object.defineProperty(obj, 'key2', {
    enumerable:false
}) // Uncaught TypeError: Cannot redefine property: key2; 因为key2的configurable

getter setter 的作用

const person = {firstName:'John',lastName:'Doe'};
Object.defineProperty(person, 'fullName', {
    enumerable: true,
    configurable: true,
    get: function () {
        // getter 不可以与 value描述一起使用
        return `${this.firstName} ${this.lastName}`;
    },
    set: function (value) { // value是新的值
        const [firstName, lastName] = value.split(' ');
        this.firstName = firstName;
        this.lastName = lastName;
    }
});
person.fullName = 'John Doe'; // 会调用 set
console.log(person.fullName); // "John Doe"; 会调用 get

         Object.getOwnPropertyDescriptors()和Object.getOwnPropertyDescriptor()可以获取指定对象的属性描述配置信息。

语法 : Object.getOwnPropertyDescriptors(obj)

obj

要获取其所有自有属性描述符的对象。

返回值一个包含给定对象的所有自有属性描述符的对象。如果没有属性,则可能是一个空对象。

语法 : Object.getOwnPropertyDescriptor(obj, prop)

obj要查找其属性的对象。
prop要检索其描述的属性的名称或 Symbol。
返回值如果指定的属性存在于对象上,则返回其属性描述符,否则返回 undefined。

const obj = { a: 1 }

console.log(Object.getOwnPropertyDescriptors(obj)) // { a: { value: 1, writable: true, enumerable: true, configurable: true } } 

console.log(Object.getOwnPropertyDescriptor(obj, 'a')) // { value: 1, writable: true, enumerable: true, configurable: true }

四. Object的更多方法

1. Object.assign()

语法 : Object.assign(target, ...sources)

target需要应用源对象属性的目标对象,修改后将作为返回值。
sources一个或多个包含要应用的属性的源对象。
返回值修改后的目标对象。

        将一个或者多个源对象中所有可枚举自有属性复制到目标对象,并返回修改后的目标对象。当目标对象和源对象中拥有相同属性时,则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的同名属性。

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target); // { a: 1, b: 4, c: 5 }  b:2被b:4替换
console.log(returnedTarget === target); // true

        Object.assign仅仅会复制其引用值。当源对象中存在值为复杂类型的属性时,它仅仅会复制其引用值到目标对象。相当于浅拷贝

const obj1 = {}
const obj2 = { obj3: {} }
const result = Object.assign(obj1, obj2)
console.log(result === obj2, result.obj3 === obj2.obj3)  // false true

        基本类型会被转成对象,null和undefined会被忽略,并且只有字符串才有可枚举的自有属性会被assign复制。

const obj = Object.assign({}, "abc", 10, true, Symbol("foo"), undefined, null);
console.log(obj)  // { "0":"a", "1":"b", "2":"c" }

        当源对象中存在访问器时,只会复制访问器的返回值到目标对象上。

const obj1 = {
    firstName: '张',
    lastName: '四',
    get fullName() {
        return this.firstName + this.lastName
    },
    set splitName(value) {
        const [firstName, lastName] = value.split(" ")
        this.firstName = firstName
        this.lastName = lastName
    }
}
console.log(obj1)  // {firstName: '张', lastName: '四'}
const newObj = Object.assign({}, obj1)
console.log(newObj)  // {firstName: '张', lastName: '四', fullName: '张四', splitName: undefined}

2. Object.create()

        以一个现有对象作为原型对象,创建一个新对象。

语法 : Object.create(proto,propertiesObject)

proto新创建对象的原型对象。
propertiesObject如果该参数被指定且不为 undefined,则该传入对象的可枚举的自有属性将为新创建的对象添加具有对应属性名称的属性描述符。
返回值根据指定的原型对象和属性创建的新对象。

const obj1 = {
    name: '张三',
    say() {
        console.log("我的名字叫" + this.name)
    }
}
const obj2 = Object.create(obj1, {
    name: {
        value: '张小三',
        enumerable: true,
        writable: true,
        configurable: true,
    },
})
console.log(obj2)       // {name: '张小三'}
console.log(obj1 === Object.getPrototypeOf(obj2)) // true
console.log(obj2.name)  // 张小三
obj2.say()              // 我的名字叫张小三

        实际上,字面量初始化对象语法是 Object.create() 的一种语法糖。

const o = {};
// 等价于:
const o = Object.create(Object.prototype);

3. Object.entries()

        Object.entries() 静态方法返回一个二维数组,每个元素包含给定对象自有的可枚举属性的键值对。

语法 : Object.entries(obj)

obj一个对象。
返回值

一个由给定对象自有的可枚举属性的键值对组成的数组。每个键值对都是一个包含两个元素的数组:第一个元素是属性的键(始终是字符串),第二个元素是属性值。

注意 : Symbol类型作为对象的属性键时,虽然他的enumerable属性描述为true,但是仍然无法被枚举得到。在Object.keys(),Object.values(),for in中等也是一样的。

const object1 = {
  a: 'somestring',
  b: 42,
};
console.log(Object.entries(object1)) // [["a", "somestring"],["b", 42]]

4. Object.fromEntries()

        Object.fromEntries() 静态方法接收一个键值对列表,并返回一个新对象,该对象的属性由这些条目给定。

语法 : Object.fromEntries(iterable)

iterable

一个包含对象列表的可迭代对象,例如 Array 或者 Map。每个对象都要有两个属性 : 

  • 0 : 表示属性键的字符串或者 Symbol
  • 1 : 属性值。

通常,该对象被实现为二元数组,第一个元素是属性键,第二个元素是属性值。

返回值

一个新对象,其属性由可迭代对象的条目给定。

// 将Array转成对象
const arr1 = [['a','H'],['b','E'],['c','L'],['d','L'],['e','O']]
console.log(Object.fromEntries(arr1)) // { a:'H', b:'E', c:'L', d:'L', e:'O' }
const arr2 = [[{0:'a',1:'H'}],[{0:'b',1:'E'}],[{0:'c',1:'L'}],[{0:'d',1:'L'}],[{0:'e',1:'0'}]]
console.log(Object.fromEntries(arr2)) // { a:'H', b:'E', c:'L', d:'L', e:'O' }

// 将Map转成对象
const arr3 = new Map([['aa','W'],['bb','O'],['cc','R'],['dd','L'],['ee','D']])
console.log(Object.fromEntries(arr3)) // { aa:'W', bb:'O', cc:'R', dd:'L', ee:'D' }

5. Object.keys()

        Object.keys() 静态方法返回一个由给定对象自身的可枚举的属性名组成的数组,其元素是字符串。这与使用 for...in 循环迭代相同,只是 for...in 循环还会枚举原型链中的属性。Object.keys()返回的数组顺序和与 for...in 循环提供的顺序相同。

        如果你想要所有以字符串为键的自有属性,包括不可枚举的属性,请使用Object.getOwnPropertyNames()。

语法 : Object.keys(obj)
obj一个对象。
返回值

一个由给定对象自身可枚举的字符串键属性键组成的数组。

注意 : Symbol类型作为对象的属性键时,虽然他的enumerable属性描述为true,但是仍然无法被枚举得到。在Object.entries(),Object.values(),for in中等也是一样的。

const obj = {
    a: 'a!',
    1: '1!'
    [Symbol("sym")]: 'sym!'
    __proto__: {
        aa: 'aa!',
        bb: 'bb!'
    }
}

console.log(Object.keys(obj)) // ['1', 'a']

        非对象参数会强制转换为对象。只有字符串可以有自己的可枚举属性,而其他所有基本类型都返回一个空数组。在 ES5 中,将一个非对象传递给 Object.keys() 会抛出一个 TypeError。

// 字符串具有索引作为可枚举的自有属性
console.log(Object.keys("foo")); // ['0', '1', '2']

// 其他基本类型没有自有属性
console.log(Object.keys(100));       // []
console.log(Object.keys(true));      // []
console.log(Object.keys(undefined)); // Uncaught TypeError: Cannot convert undefined or null to object
console.log(Object.keys(null));      // Uncaught TypeError: Cannot convert undefined or null to object

6. Object.getOwnPropertyNames()

        Object.getOwnPropertyNames() 静态方法返回一个数组,其元素是与给定对象 obj 直接关联的可枚举和不可枚举属性对应的字符串。数组中可枚举属性的顺序与使用 for...in 循环(或 Object.keys())遍历对象属性时所暴露的顺序一致。对象的非负整数键(包括可枚举和不可枚举的)首先按升序添加到数组中,然后是按插入顺序排列的字符串键。

语法 : Object.getOwnPropertyNames(obj)

obj一个对象,其自有的可枚举和不可枚举属性的名称被返回。
返回值

在给定对象上找到的自有属性对应的字符串数组。

注意 : Symbol类型作为对象的属性键时,虽然他的enumerable属性描述为true,仍无法被枚举得到。在Object.entries(),Object.keys(),Object.values(),for in中等也是一样的。

const obj = {
    __proto__: {
        c: 'c!',
        3: '3!',
        [Symbol('sym1')]: 'sym1!',
    }
}
Object.defineProperties(obj, {
    a: { value: 'a!', enumerable: true },
    b: { value: 'b!', enumerable: false },
    1: { value: '1!', enumerable: true },
    2: { value: '2!', enumerable: false },
    [Symbol('sym')]: { value: 'sym!', enumerable: true },
})
console.log(Object.keys(obj))  // ['1', 'a']
console.log(Object.getOwnPropertyNames(obj))  // ['1', '2', 'a', 'b']

        在 ES5 中,如果该方法的参数不是一个对象(而是基本类型值),则会导致 TypeError。在 ES6 中,非对象参数会被强制转换为对象。

// ES5
Object.getOwnPropertyNames("foo"); // TypeError: "foo" is not an object 
// ES6
Object.getOwnPropertyNames("foo"); // ["0", "1", "2", "length"]

        当我们需要获取指定对象上自有的不可枚举属性时,可以通过Object.getOwnPropertyNames(),Object.keys()和 Array.filter()搭配得到。由于Object.getOwnPropertyNames和Object.keys()无法枚举到Symbol类型属性名,所以Symbol类型属性名依然无法得到。

const obj = {}
Object.defineProperties(obj, {
    a: { value: 'a!', enumerable: true },
    b: { value: 'b!', enumerable: false },
    1: { value: '1!', enumerable: true },
    2: { value: '2!', enumerable: false },
    [Symbol('sym')]: { value: 'sym!', enumerable: true },
})
const allKeys = Object.getOwnPropertyNames(obj);
const enumKeys = new Set(Object.keys(obj));
const nonenumKeys = allKeys.filter((key) => !enumKeys.has(key));
console.log(nonenumKeys); // ['2', 'b']

7. Object.getOwnPropertySymbols()

        虽然在Object.keys(),Object.getOwnPropertyNames()中无法枚举得到Symbol类型属性名,但是可以通过Object.getOwnPropertySymbols() 静态方法返回一个包含给定对象所有自有 Symbol 属性的数组,无论其Symbol 属性的enumerable属性描述是否为true。

语法 : Object.getOwnPropertySymbols(obj)

obj要返回 Symbol 属性的对象。
返回值在给定对象找到的所有自有 Symbol 属性的数组。
const obj = {};
Object.defineProperties(obj, {
    a: { value: 'a!', enumerable: true },
    1: { value: '1!', enumerable: true },
    [Symbol('sym1')]: { value: 'sym1!', enumerable: true },
    [Symbol('sym2')]: { value: 'sym2!', enumerable: false},
})
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(sym1), Symbol(sym2)]

8. Object.values()

        Object.values() 静态方法返回一个给定对象的自有可枚举字符串键属性值组成的数组。这与使用 for...in 循环迭代相同,只是 for...in 循环还枚举原型链中的属性。Object.values() 返回的数组顺序和 for...in 循环提供的数组顺序相同。

语法 : Object.values(obj)

obj一个对象。
返回值

一个包含了给定对象的自有可枚举字符串键属性值的数组。

注意 : Symbol类型作为对象的属性键时,虽然他的enumerable属性描述为true,仍无法被枚举得到。在Object.entries(),Object.keys(),for in中等也是一样的。

const obj = {}
Object.defineProperties(obj, {
    a: { value: 'a!', enumerable: true },
    b: { value: 'b!', enumerable: false },
    1: { value: '1!', enumerable: true },
    2: { value: '2!', enumerable: false },
    [Symbol('sym1')]: { value: 'sym1!', enumerable: true }
})
console.log(Object.values(obj)); // ['1!', 'a!']

        非对象参数会强制转换为对象。只有字符串可以有自己的可枚举属性,而其他所有基本类型都返回一个空数组。

// 字符串具有索引作为可枚举的自有属性
console.log(Object.values("foo")); // ['f', 'o', 'o']

// 其他基本类型没有自有属性
console.log(Object.values(100));  // []
console.log(Object.values(true)); // []
console.log(Object.values(100));  // []
console.log(Object.values(100));  // []

9. Object.freeze()

        Object.freeze()静态方法可以使一个对象被冻结。冻结对象可以防止扩展浅层(不能添加新的属性),并使现有的自有浅层属性不可配置(不能更改可枚举性、可配置性、可写性、值和移除)。对象的原型也不能被重新指定。freeze() 返回与传入的对象相同的对象。

        冻结一个对象是 JavaScript 提供的最高完整性级别保护措施。并且,当指定对象被冻结后无法解冻。

语法 : Object.freeze(obj)

obj要冻结的对象。
返回值传递给函数的对象。
const obj = {
  prop() {},
  foo: "bar",
};

// 冻结前:可以添加新属性,也可以更改或删除现有属性
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;

// 冻结。
const o = Object.freeze(obj);

// 返回值和我们传入的对象相同。
o === obj; // true

// 对象已冻结。
Object.isFrozen(obj); // === true

// 现在任何更改都会失败。
obj.foo = "quux"; // 静默但什么都没做
// 静默且没有添加属性
obj.quaxxor = "the friendly duck";

// 严格模式下,这样的尝试会抛出 TypeError
function fail() {
  "use strict";
  obj.foo = "sparky"; // 抛出 TypeError
  delete obj.foo; // 抛出 TypeError
  delete obj.quaxxor; // 返回 true,因为属性‘quaxxor’从未被添加过。
  obj.sparky = "arf"; // 抛出 TypeError
}

fail();

// 尝试通过 Object.defineProperty 更改;
// 下面的两个语句都会抛出 TypeError。
Object.defineProperty(obj, "ohai", { value: 17 });
Object.defineProperty(obj, "foo", { value: "eit" });

// 同样无法更改原型
// 下面的两个语句都会抛出 TypeError。
Object.setPrototypeOf(obj, { x: 20 });
obj.__proto__ = { x: 20 };

        冻结一个对象相当于

  • 阻止其扩展 : 不能添加新的属性或方法
  • 所有的自有属性无法修改描述符和删除 : 所有现有的自有属性的描述符的 configurable 更改为 false
  • 属性值无法修改所有现有属性的描述符的 writable 更改为 false。

        任何这样的尝试都将失败,可能是静默失败,也可能抛出一个 TypeError 异常(通常情况下,在严格模式中抛出)。

        访问器属性(getter 和 setter)也相同——getter 返回的属性值仍然可以更改,setter 可以在设置属性时调用而不抛出错误。请注意,对象类型的值仍然可以修改,除非它们也被冻结。数组作为一种对象也可以被冻结;数组被冻结后,既不能更改它的元素,也不能向数组中添加或删除元素。

        还有要注意的是,被冻结的对象虽是不可变的,但这只是针对对象浅层而言。嵌套在对象内部的对象仍然可以随意增删改。

const employee = {
  name: "Mayank",
  designation: "Developer",
  address: {
    street: "Rohini",
    city: "Delhi",
  },
};

Object.freeze(employee);

employee.name = "Dummy"; // 在非严格模式下静默失败
employee.address.city = "Noida"; // 可以修改子对象的属性

console.log(employee.address.city); // "Noida"

        这是由于冻结对象时只会冻结浅层(字符串、数字和布尔值等基本类型值冻结其值,而函数和数组是复杂类型是冻结其引用)。并且而不会递归地冻结嵌套在内部的对象。这就是浅冻结

        若要完全的冻结一个对象需要递归地冻结每个对象类型的属性(深冻结)。

function deepFreeze(object) {
  // 获取对象的属性名
  const propNames = Reflect.ownKeys(object);
  // 冻结自身前先冻结属性
  for (const name of propNames) {
    const value = object[name];
    if ((value && typeof value === "object") || typeof value === "function") {
      deepFreeze(value);
    }
  }
  return Object.freeze(object);
}

const obj2 = {
  internal: {
    a: null,
  },
};

deepFreeze(obj2);

obj2.internal.a = "anotherValue"; // 非严格模式下会静默失败
obj2.internal.a; // null

10. Object.isFrozen()

        Object.isFrozen() 静态方法判断一个对象的浅层是否被冻结。一个对象,当且仅当它浅层不可拓展,且所有浅层属性都是不可配置的,所有的浅层数据属性(即不是有 getter 或 setter 的访问器属性的属性)都是不可写的时,它就是被冻结的。

语法 : Object.isFrozen(obj)

obj要检测的对象。
返回值指示给定对象是否被冻结的布尔值。

        在 ES5 中,若传递给此方法的参数不是一个对象而是一个基本类型值,则会导致TypeError
。在 ES6 中,如果传递了一个非对象类型的参数,它将返回 true 而不会出现错误,因为基本类型值在定义上是不可变的。

11. Object.seal()

        Object.seal() 静态方法密封一个对象。密封一个对象会防止扩展浅层并且使现有浅层属性不可配置(不能更改可枚举性和可配置性、不可移除)。只有现有属性的值是可写的,值仍然可以更改,但是不能重新分配其原型seal() 返回传入的同一对象。

语法 : Object.seal(obj)

obj要密封的对象。
返回值被密封的对象。

const obj = {
  prop() {},
  foo: "bar",
};

// 可以添加新属性,可以更改或删除现有属性。
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;

const o = Object.seal(obj);

o === obj; // true
Object.isSealed(obj); // true; 判断对象是否被密封

// 更改密封对象的属性值仍然有效。
obj.foo = "quux";

// 但不能将数据属性转换成访问者属性,反之亦然。
Object.defineProperty(obj, "foo", {
  get() {
    return "g";
  },
}); // 抛出 TypeError

// 除了属性值之外的任何更改都将失败。
obj.quaxxor = "the friendly duck";
// 静默不添加属性
delete obj.foo;
// 静默不添删除属性

// ...且严格模式下,这种尝试将会抛出 TypeError。
function fail() {
  "use strict";
  delete obj.foo; // 抛出一个 TypeError
  obj.sparky = "arf"; // 抛出一个 TypeError
}
fail();

// 尝试通过 Object.defineProperty 添加属性也会抛出错误。
Object.defineProperty(obj, "ohai", {
  value: 17,
}); // 抛出 TypeError
Object.defineProperty(obj, "foo", {
  value: "eit",
}); // 更改现有属性值

        在 ES5 中,如果该方法的参数不是一个对象(即基本类型),它将会抛出 TypeError。在 ES6 中,非对象参数将按原样返回,不会有任何错误,因为根据定义,基本类型已经是不可变的。

        同样,它也仅仅是浅密封。如果需要完整的密封对象,那么需要和深冻结一样递归密封对象下的所有属性。

12.Object.isSealed()

        Object.isSealed() 静态方法判断一个对象是否被密封。

语法 : Object.isSealed(obj)

obj要被检查的对象。
返回值一个表示给定对象是否被密封的布尔值。
const obj1 = {}, obj2 = {}, obj3 = {};
console.log(Object.isSealed(obj1), Object.isSealed(obj1), Object.isSealed(obj1)) // false false false

// 密封对象
Object.seal(obj1)
console.log(Object.isSealed(obj1)); // true

// 如果令一个空对象不可扩展,则它同时也会变成个密封对象。
Object.preventExtensions(obj2);
console.log(Object.isSealed(obj2)); // true

13. Object.preventExtensions()

        Object.preventExtensions() 静态方法可以防止浅层扩展(即防止新属性被添加到对象中)。它还可以防止对象的原型被重新指定

        如果一个对象可以添加新的属性,则这个对象是可扩展的。Object.preventExtensions() 将对象标记为不再可扩展,这样它将永远不会具有它被标记为不可扩展时持有的属性之外的属性。注意,一般来说,不可扩展对象的属性仍然可以被删除,并且删除的属性后续也不可再次被添加。尝试向不可扩展对象添加新属性将静默失败,或在严格模式中抛出 TypeError。

语法 : Object.preventExtensions(obj);

obj将要变得不可扩展的对象。
返回值已经不可扩展的对象。
// Object.preventExtensions 将原对象变的不可扩展,并且返回原对象。
const obj = {};
const obj2 = Object.preventExtensions(obj);
obj === obj2; // true

// 字面量方式定义的对象默认是可扩展的。
const empty = {};
Object.isExtensible(empty); // true

// 可以将其改变为不可扩展的。
Object.preventExtensions(empty);
Object.isExtensible(empty); // false

// 使用 Object.defineProperty 方法为一个不可扩展的对象添加新属性会抛出异常。
const nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
Object.defineProperty(nonExtensible, "new", {
  value: 8675309,
}); // 抛出 TypeError

// 在严格模式中,为一个不可扩展对象的新属性赋值会抛出 TypeError 异常。
function fail() {
  "use strict";
  // 抛出 TypeError
  nonExtensible.newProperty = "FAIL";
}
fail();

const obj1 = { a: 1 };
Object.preventExtensions(obj1);
delete obj1.a // 操作成功
obj1.a = 2 // 操作失败

14. Object.isExtensible()

        Object.isExtensible() 静态方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。

语法 : Object.isExtensible(obj)

        默认情况下,对象是可扩展的:可以向它们添加新属性,并且它们的 [[Prototype]] 可以被重新赋值。当通过 Object.preventExtensions()、Object.seal()、Object.freeze()、Reflect.preventExtensions()
 中的任一方法将对象标记为不可扩展。

obj要检查的对象。
返回值指示给定对象是否可扩展的一个布尔值。
// 新对象是可拓展的。
const empty = {};
Object.isExtensible(empty); // true

// 它们可以变为不可拓展的
Object.preventExtensions(empty);
Object.isExtensible(empty); // false

// 根据定义,密封对象是不可拓展的。
const sealed = Object.seal({});
Object.isExtensible(sealed); // false

// 根据定义,冻结对象同样也是不可拓展的。
const frozen = Object.freeze({});
Object.isExtensible(frozen); // false

        在 ES5 中,如果参数不是一个对象(即基本类型),将抛出 TypeError
。在 ES2015 中,如果传入的参数不是一个对象,那么它将返回 false 而不会报错,因为按照定义,原始类型是不可变的。

小结 : ferrze、seal、preventExtensions三者的相同与不同

ferrzesealpreventExtensions
重新指定原型对象
拓展性
删除自有属性✔️
修改自有属性描述                   ❌                    (可写性,值除外)✔️
修改自有属性值 (基本类型值)✔️✔️
修改自有属性值 (复杂类型值)                  ❌                    (值内部可以修改)✔️✔️

15. Object.getPrototypeOf()

        Object.getPrototypeOf() 静态方法返回指定对象的原型(即内部 [[Prototype]] 属性的值)。

语法 : Object.getPrototypeOf(obj)

obj要返回其原型的对象。
返回值给定对象的原型,可能是 null。
class Demo {
    constructor(){}
}
const obj = new Demo()
console.log(Object.getPrototypeOf(obj) === Demo.prototype) // true
console.log(Object.getPrototypeOf(obj) === obj.__proto__)  // true

        在 ES5 中,如果 obj 参数不是对象,则会抛出 TypeError
 异常。在 ES2015 中,该参数将被强制转换为 Object

16. Object.setPrototypeOf()

        Object.setPrototypeOf() 静态方法可以将一个指定对象的原型(即内部的 [[Prototype]] 属性)设置为另一个对象或者 null。

        由于现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的 [[Prototype]] 在各个浏览器和 JavaScript 引擎上都是一个很慢的操作。此外,修改继承的影响是微妙和广泛的,并不仅限于在 Object.setPrototypeOf(...) 语句上的时间花费,而是可能扩展到任何访问已更改 [[Prototype]] 属性的对象的代码。

        由于这个特性是语言的一部分,因此引擎开发人员实现该特性的性能(理想情况下)仍然是一个负担。在引擎开发人员解决这个问题之前,如果你担心性能问题,应该避免设置对象的 [[Prototype]] 属性。而是使用 Object.create() 创建一个具有所需 [[Prototype]] 属性的新对象。

语法 : Object.setPrototypeOf(obj, prototype)

obj要设置其原型的对象。
prototype该对象的新原型(一个对象或 null)。
返回值指定的对象。

class Human {}
const obj = {}
console.log( Object.getPrototypeOf(obj) === Object.prototype ) // true

const newObj = Object.setPrototypeOf(obj,Human.prototype) // true
console.log(obj === newObj)
console.log(Object.getPrototypeOf(obj) === Human.prototype) // true

17. Object.prototype.hasOwnProperty()

        hasOwnProperty() 方法返回一个布尔值,表示对象自有属性(而不是继承来的属性)中是否具有指定的属性。

        注意,JavaScript 并不保护属性名称 hasOwnProperty;依次当对象中存在自有的hasOwnProperty方法时,可能会返回不正确的结果。

语法 : hasOwnProperty(prop)

prop要测试的属性的字符串名称或者 Symbol 。
返回值如果对象有指定属性作为自有属性,则返回 true;否则返回 false
const obj = {
    name: '张三',
    __proto__: {
        age: 18    
    }
};
console.log(obj.hasOwnProperty('name')); // true
console.log(obj.hasOwnProperty('age'));  // false

         如果指定的属性是对象的直接属性——即使值为 null 或者 undefined,hasOwnProperty() 方法也会返回 true。如果属性是继承的,或者根本没有声明该属性,则该方法返回 false。与 in 运算符不同的是,该方法不会在对象原型链中检查指定的属性。

        该方法可以在大多数 JavaScript 对象中使用,因为大多数对象都是从 Object 派生而来,因此会继承该方法。例如 Array是一个 Object,所以你可以使用 hasOwnProperty() 方法来检查索引是否存在:

["Apple", "Banana", "Watermelon", "Orange"].hasOwnProperty(3) // true
["Apple", "Banana", "Watermelon", "Orange"].hasOwnProperty(3) // false

18. Object.hasOwn()

        如果指定的对象自身有指定的属性,则静态方法 Object.hasOwn() 返回 true。如果属性是继承的或者不存在,该方法返回 false。该方法旨在取代 Object.prototype.hasOwnProperty()。

语法 : Object.hasOwn(obj, prop)

obj要测试的 JavaScript 实例对象。
prop要测试的属性名称。
返回值如果指定的对象中直接定义了指定的属性,则返回 true;否则返回 false
const obj = {
    name: '张三',
    __proto__: {
        age: 18
    }
};
console.log(Object.hasOwn(obj, 'name')); // true
console.log(Object.hasOwn(obj, 'age'));  // false

19. Object.is()

        Object.is() 静态方法确定两个值是否为相同值。

语法 : Object.is(value1, value2)

value1要比较的第一个值。
value2要比较的第二个值。
返回值一个布尔值,指示两个参数是否为相同的值。
// 案例 1:评估结果和使用 === 相同
Object.is(25, 25);                 // true
Object.is("foo", "foo");           // true
Object.is("foo", "bar");           // false
Object.is(null, null);             // true
Object.is(undefined, undefined);   // true
Object.is(window, window);         // true
Object.is([], []);                 // false
const foo = { a: 1 },bar = { a: 1 };
const sam = foo;
console.log(Object.is(foo, foo));  // true
console.log(Object.is(foo, bar));  // false
console.log(Object.is(foo, sam));  // true

// 案例 2: 带符号的 0
console.log(0 === +0, Object.is(0, +0));   // true true
console.log(0 === -0, Object.is(0, -0));   // true false
console.log(+0 === +0, Object.is(+0, +0)); // true true
console.log(-0 === -0, Object.is(-0, -0)); // true true
console.log(+0 === -0, Object.is(+0, -0)); // true false

// 案例 3: NaN
console.log(NaN === NaN, Object.is(NaN, NaN)); // false true

Object.is() 确定两个值是否为相同值。如果以下其中一项成立,则两个值相同: 

  • 都是 undefined
  • 都是 null
  • 都是 true 或者都是 false
  • 都是长度相同、字符相同、顺序相同的字符串
  • 都是相同的对象(意味着两个值都引用了内存中的同一对象)
  • 都是 BigInt
     且具有相同的数值
  • 都是 Symbol
     且引用相同的 symbol 值
  • 都是数字且,都是+0 或 都是-0 或 都是NaN 或 都有相同的值并且非0和非NaN

       Object.is() 与 == 运算符并不等价。== 运算符在测试相等性之前,会对两个操作数进行类型转换(如果它们不是相同的类型),这可能会导致一些非预期的行为,例如 "" == false 的结果是 true,但是Object.is()不会对其操作数进行类型转换。

        Object.is() 也不等价于 === 运算符。Object.is() 和 === 之间的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符(和 == 运算符)将数值 -0 和 +0 视为相等,但是会将 NaN 视为彼此不相等。

20. Object.prototype.isPrototypeOf() 

        isPrototypeOf() 方法用于检查一个对象是否存在于另一个对象的原型链中。

        isPrototypeOf() 与 instanceof 运算符不同。在表达式 object instanceof AFunction 中,会检查 object 的原型链是否与 AFunction.prototype 匹配,而不是与 AFunction本身匹配。

语法 : Object.isPrototypeOf(obj)

obj要搜索其原型链的对象。
返回值一个布尔值,指示调用 isPrototypeOf() 方法的对象(即 this)是否位于 object 的原型链中。当 object 不是一个对象(即基本类型)时,直接返回 false

class A {}
class B extends A {}
class C extends B {}

const a = new A(); // 原型链:A --> Object --> null
const b = new B(); // 原型链:B --> A --> Object --> null
const c = new C(); // 原型链:C --> B --> A --> Object --> null

console.log(C.prototype.isPrototypeOf(c));       // true
console.log(C.prototype.isPrototypeOf(b));       // false
console.log(C.prototype.isPrototypeOf(a));       // false

console.log(B.prototype.isPrototypeOf(c));       // true
console.log(B.prototype.isPrototypeOf(b));       // true
console.log(B.prototype.isPrototypeOf(a));       // false

console.log(A.prototype.isPrototypeOf(c));       // true
console.log(A.prototype.isPrototypeOf(b));       // true
console.log(A.prototype.isPrototypeOf(a));       // true

console.log(Object.prototype.isPrototypeOf(c));  // true

21. Object.prototype.propertyIsEnumerable()

        propertyIsEnumerable() 方法返回一个布尔值,表示指定的属性是否是对象的可枚举自有属性。大多数内置属性默认情况下是不可枚举的,而用户创建的对象属性通常是可枚举的,除非明确指定为不可枚举。请注意,大多数枚举方法只访问字符串属性;当使用 Object.assign()
 或展开语法时,symbol 属性的可枚举性才有用。propertyIsEnumerable中依然不可枚举。

语法 : propertyIsEnumerable(prop)

prop需要测试的属性名,可以是字符串或 Symbol。
返回值一个布尔值,指示指定的属性是否可枚举并且是对象自有的属性。
const o = {
    a: "自有属性",
    __proto__: {
        b: "继承属性"
    }
};
console.log(o.propertyIsEnumerable("a")); // true
console.log(a.propertyIsEnumerable("b")); // false

22. Object.prototype.toString()

        toString() 方法返回一个表示该对象的字符串。该方法旨在重写(自定义)派生类对象的类型转换的逻辑。

语法 : Object.toString()

23. Object.prototype.toLocaleString()

        toLocaleString() 方法返回一个表示对象的字符串。该方法旨在由派生对象重写,以达到其特定于语言环境的目的。

语法 : Object.toLocaleString()

返回值调用 this.toString() 的返回值。
const obj = {
  toString() {
    return "My Object";
  },
};

console.log(obj.toLocaleString()); // "My Object"

24. Object.prototype.valueOf()

        Object实例的 valueOf() 方法将 this 值转换成对象
。该方法旨在被派生对象重写,以实现自定义类型转换
逻辑。JavaScript 调用 valueOf 方法来将对象转换成基本类型值
。你很少需要自己调用 valueOf 方法;当遇到需要基本类型值的对象时,JavaScript 会自动的调用该方法。

语法 : Object.valueOf()

返回值转换成对象的 this 值。
const obj = {}
const val = obj.valueOf()
// 证明 valueOf() 返回对象的this指针
console.log(obj === val)  // true

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐