【JS学习笔记】对象

322 阅读5分钟

这是我参与更文挑战的第9天,活动详情查看: 更文挑战

静态方法

Object.assign()

ES6新增

  1. 用法

    合并或混入对象。

    const target = { a: 1, b: 2 };
    const source1 = { b: 3, c: 4 };
    const source2 = { b: 5, c: 6 };
    Object.assign(target, source1, source2);
    

    先把source1中的属性混入到target中,重复的属性会覆盖。再把下一个参数source2的属性混入到target中,重复的覆盖。最后返回target。

    target
    //  {
    //      a: 1,
    //      b: 5,
    //      c: 6
    //  }
    
  2. 细节

    • 只会拷贝source自身的属性,原型上的不算,并且是可枚举的属性,包括符号属性。

      source.hasOwnProperty("b")source.propertyIsEnumerable("a")都要返回true才行。

      当然符号属性也会被复制,虽然符号属性不能被for-in遍历,但是上面两个函数都返回true。

    • 会调用target的set和source的get。

    • 访问器属性的getset不会被复制走。

    • 如果在调用Object.assign()期间,抛出了错误,混入的操作是没有回滚的,可能只复制了一半。

    • 浅复制,如果属性是引用类型的,只会复制其引用。

    • 枚举的顺序是确定性的,先以升序枚举数值键,再以插入顺序枚举字符串和符号键。

    • 原始类型会被包装成对象。

      const v1 = "abc";
      const v2 = true;
      const v3 = 10;
      const v4 = Symbol("foo")
      
      const obj = Object.assign({}, v1, null, v2, undefined, v3, v4); 
      // 原始类型会被包装,null 和 undefined 会被忽略。
      // 注意,只有字符串的包装对象才可能有自身可枚举属性。
      console.log(obj); // { "0": "a", "1": "b", "2": "c" }
      
  3. 作用

    Object.assign({}, obj1, obj2);
    

    在创建新对象时,可以把obj1和obj2的属性复制过去。

    Object.assign(oldObj, newObj);
    

    可以更新对象中已有属性的值。

    {...obj1, ...obj2}
    

    可以实现相同的效果。

Object.is()

ES6新增。判断同值相等。

在ES6之前,有些==操作符也无能为力的事情。

NaN == NaN     // false
+0  == -0      // true

这是不符合常理的。

Object.is(NaN, NaN);   // true
Object.is(+0, -0);     // false

这样才符合常理。

Object.keys()

传入一个对象,返回一个数组,由对象的可枚举的属性名组成。不包含原型上的属性名,也不包含符号属性名

const person = {
    name: "herb",          // 普通属性
    [Symbol("age")]: 18,   // 符号属性
    high: "1.8米"          // 不可枚举属性
}
Object.defineProperty(person, "high", { enumerable: false });
person.__proto__.father = "hhhhhh";    // 原型上的属性
Object.keys(person);
// ["name"]

数组里不包含符号和不可枚举属性。

Object.keys("abcd");
// ["0", "1", "2", "3"]

ES6之前传入原始值会报错,现在会把原始类型转换成对象,再处理。

Object.values()

传入一个对象,返回一个数组,由对象的可枚举属性值组成。不包含原型上的属性值,也不包含符号属性值

Object.values(person);
// ["herb"]
Object.values("abc");
// ["a", "b", "c"]

参数为原始值会转换成对象处理。

Object.entries()

返回一个二维数组,每一个一维数组都有两个元素,键值对。不包含原型上的键值对,也不包含符号键值对

Object.entries(person);
// [ ["name", "herb"] ]
Object.entries("abc");
// [ ["0", "a"], ["1", "b"], ["2", "c"] ]

in操作符

  1. for-in

    无论属性是在实例上还是原型上,只要可枚举

    for(let prop in person) {
        console.log(prop);
    }
    // name
    // father
    

    遍历对象及其原型上的所有可枚举的属性名,但不包括符号属性

    for(let prop in person) {
        console.log(person[prop]);
    }
    // herb
    // hhhhhh
    
  2. in

    无论属性是在实例上还是原型上,不管能不能枚举,只要有,就返回true

    "name" in person   // true
    "father" in person // true
    

Object.getOwnPropertyNames()

传入一个对象,返回一个数组,由对象上所有属性名(无论是否可枚举)组成,不包括符号属性,不包括原型上的。

Object.getOwnPropertyNames(person);
// ["name", "high"]

high作为不可枚举属性也返回了。

Object.getOwnPropertyNames("abc");
// ["0", "1", "2", "length"]

原始值转换成对象。

const obj = {
   "31": "d",
   "0": "a",
   "4": "e",
    c:  "c",
    b:  "b"
}
Object.getOwnPropertyNames(obj)
// ["0", "4", "31", "c", "b"]

对象内的键名如果有数字的话,会按照数字大小排列,其他的按照先后顺序排列。

Object.getOwnPropertySymbols()

传入一个对象,返回一个数组,由所有自身的符号属性名(无论是否可枚举)组成,不包括原型上的。

const sym = {
    [Symbol("s1")]: "s1",
    [Symbol.for("s2")]: "s2",
    [Symbol.iterator]() {
        return {
            next() {
                return {
                    value: 1,
                    done: false
                }
            }
        }
    }
}
Object.getOwnPropertySymbols(sym);
// [Symbol(s1), Symbol(s2), Symbol(Symbol.iterator)]

Object.getPrototypeOf

传入一个对象,返回其隐式原型。

Object.getPrototypeOf({}) === Object.prototype
// true

{}是由Object创建的,所以{}的隐式原型指向Object.prototype

不建议使用对象上的__porto__属性来获取隐式原型了。

setPrototypeOf()

传入一个对象A和一个对象B,把对象A的原型改为对象B。

Object.setPrototypeOf(obj, prototype)

不建议使用,详见MDN,建议使用Object.create()

Object.create()

传入一个对象,创建一个新对象,其隐式原型是传进来的对象。

例子详见MDN

Object.create(null);

let obj = {};
obj.__proto__ = null;

传入一个null,创建一个空对象,没有原型,没有任何属性。上面两个写法效果是一样的。

Object.create({}, {
    prop: {
        value: "hhhh"
    }
})

如果传入了两个参数,那么创建好的对象和第二个参数,相当于传给Object.defineProperties()

相当于创建一个对象的同时,还可以赋值。

function inherit(father, son) {
    return son.prototype = Object.create(father.prototype, {
        constructor: {
            value: son,
            writable: true,
            configurable: true
        }
    })
}

可以通过这种方式,实现继承,fatherson的父类。

实例方法

hasOwnProperty()

传入一个参数,判断实例上是否有这个属性,返回一个布尔值。不包括原型上的。

const obj = {};
obj.hasOwnProperty("constructor");               // false
Object.prototype.hasOwnProperty("constructor");  // true

isPrototypeOf()

传入一个对象,判断这个对象的原型链上有没有调用这个函数的对象。

const obj = {};
Object.prototype.isPrototypeOf(obj);   // true

obj的原型链上肯定有Object.prototype

obj.isPrototypeOf(Object.prototype);   // false

Object.prototype上面已经没有原型了,自然不可能有obj

propertyIsEnumerable()

传入一个参数,判断这个参数在对象中是否可以枚举。

const person = {
    name: "herb",              // 普通属性
    [Symbol.for("age")]: 18,   // 符号属性
    high: "1.8米"              // 不可枚举属性
}
Object.defineProperty(person, "high", { enumerable: false });
person.propertyIsEnumerable("name");             // true
person.propertyIsEnumerable(Symbol.for("age"));  // true
person.propertyIsEnumerable("high");             // false