1、基础属性
先在Chrome(版本 87.0.4280.141 正式版本)控制台打印一个对象
console.log({})
// 结果
// ▶ {}
我们点开这个三角形会看到
▼ {} i
▶ __proto__: Object
在点开 __proto__ 左边的三角
▼ {} i
▶ __proto__: Object
▶ constructor: 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实现深拷贝
- 掘金文章# 如何写出一个惊艳面试官的深拷贝juejin.cn/post/684490…
- lodash源码github.com/lodash/loda… 查看deepClone。
6、Object和Map
let obj = {
a: 1,
b: 2
}
let map = new Map()
map.set('a', 1)
map.set('b', 2)
多数情况下两者性能差距不大。但是也有特别的情况:
-
向 Object 和 Map 中插入新键/值对的性能消耗差不多,不过 Map 的插入在所有浏览器中一般会稍微快一点。如果代码涉及大量插入操作,那么显然 Map 的性能更快些。
-
从大型 Object 和 Map 中查找键/值对的查找性能差异极小,但如果只包含少量键/值对, 则 Object 有时候速度更快。在把 Object 当成数组使用的情况下(比如使用连续整数作为属性),浏览器引擎可以进行优化,在内存中使用更高效的布局。这对 Map 来说是不可能的。如果代码涉及大量查找操作,那么某些情况下可能选 择 Object 更好一些。
-
使用 delete 删除 Object 属性的性能一直以来都不太好。为此, 出现了一些操作,把属性值设置为 undefined 或 null。但很多时候,这都是一种迂回的策略。而对大多数浏览器引擎来说,Map 的 delete()操作都比插入和查找更快。 如果代码涉及大量删除操作,那么应该选择 Map。