基本概念
- 对象是一个无序集合,每个属性都有名字和值。属性名通常是字符串(也可以是符号Symbol)
- 对象不简单的是key到value的映射,除了维持自己的属性之外,JavaScript对象也可以从其他对象继承属性。这个其他对象称之为其“
原型”,对象的方法通常是继承来的属性,而这种“原型式继承”也是JavaScript的主要特性。 - 对象不能包含两个同名的属性
- 默认情况下,我们所创建的所有属性都是可写、可枚举和可配置的。
原型
- 使用new关键字和构造函数调用创建的对象,使用构造函数prototype属性的值作为它们的原型
- 记住:几乎所有对象都有原型,但只有少数对象有prototype属性(
实际上只有函数对象才有这个属性)。正是这些有prototype属性的对象为所有对象定义了原型 - Object.prototype的原型是为数不多的没有原型的对象,因为他不继承任何属性。
创建对象
-
字面量形式
let obj = { key: value } -
new关键字
let obj = new Object() -
Object.create()
- Object.create()用于创建一个新对象,使用第一个参数作为新对象的原型
let obj = Object.create({x: 1, y: 2}) // obj继承属性x,y obj.x + obj.y // => 3- 传入null可以创建一个没有原型的对象
- 创建一个空对象就传入Object.prototype
基本操作
要创建或设置属性,与查询属性一样,可以使用点或方括号
var obj = {},追加属性是`可以直接用obj.xxx`去操作的,我以前一直认为不行,
必须用obj[xxx]去操作,这个obj[]这是用来操作当key是个变量的时候,或者说是特殊字符的时候
如果在追加之前就console.log这个obj,会在控制台看到一个{}
然后去追加,再输出就是有数据的{name:'汴禧'}这样,此时上面那个还是{},
但是点开就会发现里面竟然有追加的属性,这是因为浏览器的机制问题,再点击的时候相当于做了一次访问操作
- 自有属性和继承属性同名时,继承属性会被新创建的属性隐藏,而不会修改继承属性
- 查询属性时会用到原型链,而设置属性时不影响原型链,这是JavaScript的一个重要特性。
- 删除属性:
delete关键字, delete obj.xxx - 测试属性(检查是否有该属性):
in操作符,hasOwnProperty(),propertyIsEnmerable()方法,或者直接查询该属性- in操作符要求左边是一个属性名,右边是一个对象。这个对象有该属性的时候返回true,没有该属性的时候返回false
- hasOwnProperty()方法用来判断测试对象是否有给定名字的属性。对于继承的属性它返回false
- propertyIsEnmerable()方法用来判断是不是有给定的可枚举属性(因为某些内置属性是不可枚举的)
- 除了使用in操作符,通常简单的属性查询配合!==确保其不是未定义就可以了。但是in可以区分不存在的属性和存在但被设置为undefined的属性,而简单的属性访问做不到
循环:
for/in,除了使用for/in循环,还可以先获取对象所有属性名的数组,再结合for/of循环遍历数组。有四个函数可以用来获取属性名数组
- Object.keys()返回对象可枚举自有属性名的数组。
不包含不可枚举属性、继承属性或名字是符号的属性 - Object.values()返回对象的可枚举自有属性值的数组。
不包含不可枚举属性、继承属性或名字是符号的属性 - Object.entries()返回对象的可枚举自有属性键值对的数组。
不包含不可枚举属性、继承属性或名字是符号的属性 - Object.getOwnPropertyNames()与Object.keys()类似,
但也会返回不可枚举自有属性名的数组,只要他们的名字是字符串 - Object.getOwnPropertySymbols()返回
名字是符号的自有属性,无论可否枚举 - Reflect.ownKeys()返回
所有属性名,包括可枚举和不可枚举属性,以及字符串属性和符号属性
扩展对象
- Object.assign(): Object.assign() 是 JavaScript 中一个非常实用的方法,用于对象的合并和属性拷贝。它于 ES6(ECMAScript 2015)中被引入,可以将一个或多个源对象(source)的所有可枚举属性(enumerable properties)复制到目标对象(target),并返回修改后的目标对象
- 记住:总是把默认值放在中间,用户选项放在最后,目标对象用空对象!
📊 核心特点与注意事项
了解 Object.assign() 的特性,能帮你更好地使用它。
| 特性/事项 | 说明 | 示例/建议 |
|---|---|---|
| 浅拷贝 (Shallow Copy) | 如果源对象的属性值是对象,那么复制的是该对象的引用,而非副本。 | 修改复制后对象中的嵌套对象属性,会影响到源对象。需要深拷贝可使用 JSON.parse(JSON.stringify(obj))(有局限性)或第三方库如 Lodash 的 _.cloneDeep()。 |
| 同名属性覆盖 | 后续源对象的同名属性会覆盖之前的。 | Object.assign({a:1}, {a:2}) 结果为 {a:2}。 |
跳过null 和 undefined | 作为源对象时,null 和 undefined 会被忽略,不会报错。 | Object.assign({}, null, undefined) 返回空对象 {}。 |
| 处理原始值 | 源对象是原始值(字符串、数字、布尔值)会被转换为包装对象。只有字符串包装对象有可枚举的自有属性(索引对应字符)。 | Object.assign({}, 'abc') 结果为 {0: 'a', 1: 'b', 2: 'c'}。Object.assign({}, 42, true) 结果为 {}。 |
| 不拷贝继承和不可枚举属性 | 只拷贝源对象自身的可枚举属性。 | 继承属性和 enumerable: false 的属性不会被拷贝。 |
| 拷贝 Symbol 属性 | Symbol 类型的属性也会被拷贝。 | Object.assign({}, {[Symbol('foo')]: 'bar'}) 会拷贝该 Symbol 属性。 |
| 拷贝访问器属性 (Getters) | 会调用源对象的 getter 和方法取得的值,然后复制到目标对象。 | 复制的是 getter 执行后的返回值,而非 getter 函数本身。 |
如何准确判断一个变量是Object类型
| 方法 | 示例 | 优点 | 缺点 |
|---|---|---|---|
typeof | typeof {} === 'object' | 简单 | 对 null、数组都返回 'object' |
instanceof | {} instanceof Object | 检查原型链 | 跨框架时可能失效 |
constructor | {}.constructor === Object | 直接 | 可被修改,不安全 |
Object.prototype.toString | Object.prototype.toString.call({}) | 最准确 | 语法稍复杂 |
序列化对象(转换成字符串)
- 对象的序列化就是把对象转换为字符串的过程,之后可以从中恢复对象的状态。
- 方法JSON.sttringfy()和JSON.parse()用于序列化和恢复JavaScript对象
- 注意:
- 可以序列化的值包括对象、数组、字符串、有限数值、true、false和null。
- NaN。Infinity和-Infinity会被序列化为null。
- 日期对象会被序列化为ISO格式的日期字符串,但JSON.parse()会保持其字符串形式,不会恢复原始的日期对象。
- 函数、RegExp和Error对象以及undefined值不能被序列化或恢复。
- JSON.sttringfy()只序列化对象的可枚举自有属性。如果属性名无法序列化,则该属性会从输出的字符串中删除
对象的方法
- toString(),一般都是返回[object object]
- toLocalString(),Object未重写就是简单的调用了toString()方法,但是Array、Date和Number都重新定义了自己的toLocalString()方法
- valueOf(),valueOf()与toString()很类似,但会在JavaScript需要把对象转换为某些非字符串原始值(通常为数值)时被调用。
- toJSON(),Object.prototype并未定义toJSON()方法,但JSON.sttringfy()方法会从要序列化的对象上寻找toJSON()方法。如果要序列化的对象上存在这个方法,就会调用它,然后序列化该方法的返回值,而不是原始对象
扩展语法
- 简写属性,假设变量x,y中保存着值,而你想创建一个具有x和y属性且值分别为相应变量值的对象,在ES6之后就可以这样let obj = {x, y}
- 计算的属性名:ES6之后,就是说属性名可以通过计算属性(一个函数返回这个属性名)来定义
- 符号Symbol()作为属性名(ES6以后)
- 扩展运算符:
- 注意:扩展运算符(
...)执行的是浅拷贝,而不是深拷贝 - 还有一点要注意的,虽然扩展运算服务在我们的代码中只是三个小圆点,但它可能给JavaScript解释器带来巨大的工作量。如果对象有n个属性,把这个属性扩展到另一个对象可能是一种O(n)的操作。这意味着,如果在循环递归函数中通过...向一个大对象不断追加属性,则很可能我们是在写一个低效的O(n²)算法。随着n越来越大,这个可能会成为性能瓶颈。
- 注意:扩展运算符(
- 简写方法,与简写属性类似,就是ES6之后,我们写方法没必要再func: function(){}这样子,可以直接func() {}
- 属性的获取和设置: get()和set(),
注意:这不是ES6新增的,这是ES5中引入的