javscript基础语法之对象

508 阅读4分钟

1、基础属性

先在Chrome(版本 87.0.4280.141 正式版本)控制台打印一个对象

console.log({})
// 结果
// ▶ {}

我们点开这个三角形会看到

▼ {} i
   ▶ __proto__: Object

在点开 __proto__ 左边的三角

▼ {} i
   ▶ __proto__: Objectconstructor: f Object()
     ▶ hasOwnProperty: f hasOwnProperty()
     ▶ isPrototypeOf: f isPrototypeOf()
     ▶ propertyIsEnumberable: f propertyIsEnumerable()
     ▶ toLocaleString: f toLocaleString()
     ▶ toString: f toString()
     ▶ valueOf: f valueOf()
     ▶ __defineGetter__: f __defineGetter__()
     ▶ __defineSetter__: f __defineSetter__()
     ▶ __lookupGetter__: f __lookupGetter__()
     ▶ __lookupSetter__: f __lookupSetter__()
     ▶ get __proto__: f __proto__()
     ▶ set __proto__: f __proto__()

发现 __proto__ 指向一个Object 然后Object下面就没有__proto__了,但是我们发现有一个constructor指向一个Object()的函数(前面指向的是Object,这里指向的是Object()函数)其他ƒ开头的就是一些我们平常使用对象的一些(原型)方法。我们继续点开这个constructor

▼ {} i
   ▶ __proto__: Object                      <--------------------------------------------------
     ▼ constructor: ƒ Object()
     	arguments: (...)
        ▶ assign: ƒ assign()
        caller: (...)
        ▶ create: ƒ create()
        ▶ defineProperties: ƒ defineProperties()
        ▶ defineProperty: ƒ defineProperty()
        ▶ entries: ƒ entries()
        ▶ freeze: ƒ freeze()
        ▶ formEntries: ƒ fromEntries()
        ▶ getOwnPropertyDescriptor: ƒ getOwnPropertyDescriptor()
        ▶ getOwnPropertyNames: ƒ getOwnPropertyNames()
        ▶ getOwnPropertySymbols: ƒ getOwnPropertySymbols()
        ▶ getPrototypeOf: ƒ getPrototypeOf()
        ▶ is: ƒ is()
        ▶ isExtensible: ƒ isExtensible()
        ▶ isFrozen: ƒ isFrozen()
        ▶ isSealed: ƒ isSealed()
        ▶ keys: ƒ keys()
        length: 1
        name: "Object"preventExtensions: ƒ preventExtensions()
        ▼ prototype: 
            ▶ constructor: ƒ Object()
            ▶ hasOwnProperty: ƒ hasOwnProperty()
            ▶ isPrototypeOf: ƒ isPrototypeOf()
            ▶ propertyIsEnumerable: ƒ propertyIsEnumerable()
            ▶ toLocaleString: ƒ toLocaleString()
            ▶ toString: ƒ toString()
            ▶ valueOf: ƒ valueOf()
            ▶ __defineGetter__: ƒ __defineGetter__()
            ▶ __defineSetter__: ƒ __defineSetter__()
            ▶ __lookupGetter__: ƒ __lookupGetter__()
            ▶ get __proto__: ƒ __proto__()
            ▶ set __proto__: ƒ __proto__()
        ▶ seal: ƒ seal()
        ▶ setPrototypeOf: ƒ setPrototypeOf()
        ▶ values: ƒ valuse()
        ▼ __proto__: ƒ ()                  <--------------------------------------------------
            ▶ apply: ƒ apply()
            arguments: (...)
            ▶ bind: ƒ bind()
            ▶ call: ƒ call()
            caller: (...)
            ▶ constructor: ƒ Function()
            length: 0
            name: ""toString: ƒ toString()
            ▶ Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()
            ▶ get arguments: ƒ ()
            ▶ set arguments: ƒ ()
            ▶ get caller: ƒ ()
            ▶ set caller: ƒ ()
            ▼ __proto__:          <--------------------------------------------------
            	▶ constructor: ƒ Object()
                ▶ hasOwnProperty: ƒ hasOwnProperty()
                ▶ isPrototypeOf: ƒ isPrototypeOf()
                ▶ propertyIsEnumerable: ƒ propertyIsEnumerable()
                ▶ toLocaleString: ƒ toLocaleString()
                ▶ toString: ƒ toString()
                ▶ valueOf: ƒ valueOf()
                ▶ __defineGetter__: ƒ __defineGetter__()
                ▶ __defineSetter__: ƒ __defineSetter__()
                ▶ __lookupGetter__: ƒ __lookupGetter__()
                ▶ __lookupSetter__: ƒ __lookupSetter__()
                ▶ get __proto__: ƒ __proto__()
                ▶ set __proto__: ƒ __proto__()
            [[FunctionLocation]]: <unknown>
            ▶ [[Scopes]]: Scopes[0]
        ▶ [[Scopes]]: Scopes[0]
     ▶ hasOwnProperty: ƒ hasOwnProperty()
     ▶ isPrototypeOf: ƒ isPrototypeOf()
     ▶ propertyIsEnumberable: ƒ propertyIsEnumerable()
     ▶ toLocaleString: ƒ toLocaleString()
     ▶ toString: ƒ toString()
     ▶ valueOf: ƒ valueOf()
     ▶ __defineGetter__: ƒ __defineGetter__()
     ▶ __defineSetter__: ƒ __defineSetter__()
     ▶ __lookupGetter__: ƒ __lookupGetter__()
     ▶ __lookupSetter__: ƒ __lookupSetter__()
     ▶ get __proto__: ƒ __proto__()
     ▶ set __proto__: ƒ __proto__()
     

2、Object.create(null) 和 {} 、 new Object()

/* Object.create(null) */
▼ {} i
     No properties

/* {} */
▼ {} i
  ▶ __proto__: Object
  
/* new Object() */
▼ {} i
  ▶ __proto__: Object

在 JavaScript 中创建一个空对象最简单的方法都是 Object.create(null)。Object.create(null) 和 {} 很像, 但是并不会创建Object.prototype 这个委托,所以它比 {}“更空”:

直接创建的{}和Object.create(Object.prototype)创建的才是一样的,__proto__也一样

3、new Object()

new Object() 和不用new的Object()没啥区别

MDN上对Object的描述

在JavaScript中,几乎所有的对象都是Object类型的实例,它们都会从Object.prototype继承属性和方法。Object 构造函数为给定值创建一个对象包装器。Object构造函数,会根据给定的参数创建对象,具体有以下情况:
1、如果给定值是 null 或 undefined,将会创建并返回一个空对象
2、如果传进去的是一个基本类型的值,则会构造其包装类型的对象
3、如果传进去的是引用类型的值,仍然会返回这个值,经他们复制的变量保有和源对象相同的引用地址
4、当以非构造函数形式被调用时,Object 的行为等同于 new Object()

4、hasOwnProperty方法

in 操作符会检查属性是否在对象及其原型链中。相比之下,hasOwnProperty(..) 只会检查属性是否在 obj 对象中,不会检查原型链。

let origin = {
  a: 1
}
let obj = {
  b: 2
}
obj.__proto__ = origin
console.log(obj)
/**
 * ▼ {b: 2} i
 *    b: 2
 *   ▶ __proto__:
 *    	a: 1
 *    ▶ __proto__: Object
 */


for (let i in obj) { // enumerable:false的不能被枚举出来,但是hasOwnPropery是可以检测到的
  console.log(i)
  // a
  // b
}
console.log('a' in obj) // true
console.log(Object.hasOwnProperty('a')) // false

判断对象是否是空 {}

JSON.stringify(obj) === '{}'

Object.keys(obj).length === 0

5、拷贝一个对象

javascript 有浅拷贝和深拷贝

5.1 浅拷贝

Object.assign()和 {...obj}的区别

let obj = {
  a: 1,
}
Object.keys(obj).forEach((key) => {
  let internalValue = obj[key];
  Object.defineProperty(obj, key, {
    get () {
       console.log(`getting key "${key}": ${JSON.stringify(internalValue)}`)
       return internalValue;
    },
    set(newValue) {
       console.log(`setting key "${key}": ${JSON.stringify(newValue)}`)
       internalValue = newValue
    }
  })
})
obj.a = 233

let cc = {...obj, ...{ val: '555' }}
// let cc = Object.assign(obj, { val: '555' })

cc.a = 666

5.2 深拷贝

JSON.parse(JSON.stringify(obj))的缺点

let obj = {
  a: null,
  b: undefined,
  c: function foo() {},
  d: () => {},
  e: NaN,
  f: Infinity,
  g: -Infinity,
}
console.log(JSON.parse(JSON.stringify(obj))); // {a: null, e: null, f: null, g: null};

使用最新API structuredClone 、不能copy函数,不能copy的会提示报错

let obj = {
  a: null,
  b: undefined,
  e: NaN,
  f: Infinity,
  g: -Infinity,
}
console.log(structuredClone(obj)); // {a: null, b: undefined, e: NaN, f: Infinity, g: -Infinity};

js实现深拷贝

6、Object和Map

let obj = {
  a: 1,
  b: 2
}

let map = new Map()
map.set('a', 1)
map.set('b', 2)

多数情况下两者性能差距不大。但是也有特别的情况:

  1. 向 Object 和 Map 中插入新键/值对的性能消耗差不多,不过 Map 的插入在所有浏览器中一般会稍微快一点。如果代码涉及大量插入操作,那么显然 Map 的性能更快些。

  2. 从大型 Object 和 Map 中查找键/值对的查找性能差异极小,但如果只包含少量键/值对, 则 Object 有时候速度更快。在把 Object 当成数组使用的情况下(比如使用连续整数作为属性),浏览器引擎可以进行优化,在内存中使用更高效的布局。这对 Map 来说是不可能的。如果代码涉及大量查找操作,那么某些情况下可能选 择 Object 更好一些。

  3. 使用 delete 删除 Object 属性的性能一直以来都不太好。为此, 出现了一些操作,把属性值设置为 undefined 或 null。但很多时候,这都是一种迂回的策略。而对大多数浏览器引擎来说,Map 的 delete()操作都比插入和查找更快。 如果代码涉及大量删除操作,那么应该选择 Map。