JavaScript对象(Object)研究_02_Object()构造函数_静态方法_assign、create、defineProperties、defineProperty、entries
在JavaScript中,Object
构造函数是所有对象的原型,是构建对象的基础。Object
不仅可以作为构造函数来创建对象实例,还提供了一系列静态方法,帮助我们操作和管理对象属性。本篇博客将详细介绍Object
的几个重要静态方法,包括:assign
、create
、defineProperties
、defineProperty
、entries
,并配以示例代码,帮助您深入理解它们的用法和注意事项。
一、Object() 构造函数概述
1. 基础介绍
Object
构造函数用于创建一个新的对象。当以构造函数的方式调用时,它可以将传入的参数转换为对象。如果没有提供参数,则创建一个空对象。
// 使用Object构造函数创建对象
const obj1 = new Object();
// 等价于对象字面量
const obj2 = {};
2.示例
获取 BigInt 和 Symbol 的封装对象
当用 new
调用BigInt()
和Symbol()
构造函数时会抛出一个错误,以阻止创建封装对象而不是基本类型值的常见错误。为这些类型创建封装对象的唯一方法是使用它们调用 Object()
:
const numberObj = new Number(1);
console.log(typeof numberObj); // "object"
const bigintObj = Object(1n);
console.log(typeof bigintObj); // "object"
const symbolObj = Object(Symbol("foo"));
console.log(typeof symbolObj); // "object"
二、Object.assign()
1. 基础介绍
Object.assign()
方法用于将所有可枚举的自有属性(即对象自身的属性,不包括继承的属性)从一个或多个源对象复制到目标对象。该方法返回目标对象。
2. 语法
Object.assign(target, ...sources)
- target:目标对象。
- sources:一个或多个源对象。
3. 示例代码
示例1:简单对象合并
const target = { a: 1 };
const source = { b: 2 };
const returnedTarget = Object.assign(target, source);
console.log(target); // 输出: { a: 1, b: 2 }
console.log(returnedTarget); // 输出: { a: 1, b: 2 }
示例2:合并多个源对象
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
console.log(target); // 输出: { a: 1, b: 2, c: 3 }
示例3:属性覆盖
const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };
Object.assign(target, source);
console.log(target); // 输出: { a: 1, b: 3, c: 4 }
4. 拷贝访问器
Object.assign()
只能拷贝源对象的属性值,对于源对象的访问器属性(getter 和 setter),会被直接复制其返回值或undefined
,而不会复制访问器本身。
const source = {
get foo() {
return 'bar';
}
};
const target = {};
Object.assign(target, source);
console.log(target.foo); // 输出: 'bar'
在这个例子中,target
对象的foo
属性是一个普通的值属性,而不是访问器属性。
解决方法
要正确复制访问器属性,可以使用Object.getOwnPropertyDescriptors()
和Object.defineProperties()
方法。
const source = {
get foo() {
return 'bar';
}
};
const target = {};
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
console.log(Object.getOwnPropertyDescriptor(target, 'foo'));
// 输出: { get: [Function: get foo], set: undefined, enumerable: true, configurable: true }
5. 注意事项
-
浅拷贝:
Object.assign()
执行的是浅拷贝,对于嵌套的对象和数组,复制的是引用。const target = { a: { b: 1 } }; const source = { a: { c: 2 } }; Object.assign(target, source); console.log(target); // 输出: { a: { c: 2 } }
-
处理原始类型:如果源对象是原始类型(数字、字符串、布尔值等),会被包装为对象,但由于原始类型没有可枚举的自有属性,因此不会对目标对象产生影响。
const target = {}; const source = 'abc'; Object.assign(target, source); console.log(target); // 输出: {}
6. 扩展知识点
-
实现对象的克隆:
const obj = { a: 1, b: { c: 2 } }; const clone = Object.assign({}, obj); console.log(clone); // 输出: { a: 1, b: { c: 2 } }
-
合并默认配置:
const defaultConfig = { host: 'localhost', port: 80 }; const userConfig = { port: 8080 }; const finalConfig = Object.assign({}, defaultConfig, userConfig); console.log(finalConfig); // 输出: { host: 'localhost', port: 8080 }
三、Object.create()
1. 基础介绍
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
。这对于实现原型继承非常有用。
2. 语法
Object.create(proto, [propertiesObject])
- proto:新创建对象的原型对象。
- propertiesObject(可选):要添加到新对象的属性,格式与
Object.defineProperties()
的第二个参数相同。
3. 示例代码
示例1:创建一个以某个对象为原型的新对象
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person);
me.name = 'Alice';
me.isHuman = true;
me.printIntroduction();
// 输出: My name is Alice. Am I human? true
示例2:不带原型的对象
const obj = Object.create(null);
console.log(Object.getPrototypeOf(obj)); // 输出: null
示例3:使用 propertiesObject
参数
propertiesObject
参数允许在创建对象的同时定义属性的特性。
const obj = Object.create(Object.prototype, {
a: {
value: 1,
writable: true,
enumerable: true,
configurable: true
},
b: {
value: 2,
writable: false,
enumerable: true,
configurable: true
}
});
console.log(obj.a); // 输出: 1
console.log(obj.b); // 输出: 2
obj.a = 3;
console.log(obj.a); // 输出: 3
obj.b = 4;
console.log(obj.b); // 输出: 2(无法修改,因为writable为false)
4. 注意事项
-
属性描述符:
propertiesObject
参数中的每个属性都需要使用属性描述符来定义,包括value
、writable
、enumerable
、configurable
等。 -
null
原型对象:创建一个没有原型的对象,可以避免原型链带来的干扰,适用于创建纯粹的字典对象。const dictionary = Object.create(null); dictionary.apple = 'A fruit'; dictionary.car = 'A vehicle'; console.log(dictionary); // 输出: { apple: 'A fruit', car: 'A vehicle' }
四、Object.defineProperties()
1. 基础介绍
Object.defineProperties()
方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。与Object.defineProperty()
不同的是,它可以同时定义或修改多个属性。
2. 语法
Object.defineProperties(obj, props)
- obj:要在其上定义属性的对象。
- props:包含一个或多个属性描述符的对象。
3. 属性描述符的类型
在定义属性时,属性描述符分为两种类型:
1. 数据描述符(Data Descriptor)
用于描述具有值的属性,其属性包括:
- value:属性的值。
- writable:
true
表示属性的值可被修改,false
则不可修改。 - enumerable:
true
表示属性可枚举,false
则不可枚举。 - configurable:
true
表示属性的特性(除了value
外)可被修改,属性可被删除;false
则不可。
2. 存取描述符(Accessor Descriptor)
用于描述通过getter
和setter
方法访问的属性,其属性包括:
- get:获取属性值的函数,在读取属性时调用,无参数。
- set:设置属性值的函数,在写入属性时调用,接收单个参数。
- enumerable:同上。
- configurable:同上。
注意:一个属性描述符要么是数据描述符,要么是存取描述符,不能同时存在value
或writable
与get
或set
。
4. 示例代码
示例1:定义数据属性
const obj = {};
Object.defineProperties(obj, {
property1: {
value: 42,
writable: true,
enumerable: true,
configurable: true
},
property2: {
value: 'Hello',
writable: false,
enumerable: true,
configurable: false
}
});
console.log(obj.property1); // 输出: 42
console.log(obj.property2); // 输出: Hello
obj.property1 = 100;
console.log(obj.property1); // 输出: 100
obj.property2 = 'World';
console.log(obj.property2); // 输出: Hello(无法修改)
示例2:定义存取属性
let internalValue = 0;
Object.defineProperties(obj, {
property3: {
get() {
return internalValue;
},
set(value) {
console.log(`Setting property3 to ${value}`);
internalValue = value;
},
enumerable: true,
configurable: true
}
});
obj.property3 = 10; // 输出: Setting property3 to 10
console.log(obj.property3); // 输出: 10
5. 注意事项
- 默认值:如果不指定
writable
、enumerable
、configurable
,它们的默认值为false
。 - 互斥性:数据描述符和存取描述符不能同时使用。
- 属性特性:通过属性描述符,可以精确控制属性的行为,非常适合在需要严格控制属性访问和修改的情况下使用。
五、Object.defineProperty()
1. 基础介绍
Object.defineProperty()
方法直接在对象上定义一个新属性,或者修改对象的现有属性,并返回该对象。
2. 语法
Object.defineProperty(obj, prop, descriptor)
- obj:要在其上定义属性的对象。
- prop:要定义或修改的属性的名称。
- descriptor:属性描述符,格式同上。
3. 示例代码
示例1:定义数据属性
const obj = {};
Object.defineProperty(obj, 'a', {
value: 1,
writable: true,
enumerable: true,
configurable: true
});
console.log(obj.a); // 输出: 1
示例2:定义只读属性
Object.defineProperty(obj, 'b', {
value: 2,
writable: false,
enumerable: true,
configurable: true
});
obj.b = 3;
console.log(obj.b); // 输出: 2(无法修改)
示例3:定义存取属性
let value = 0;
Object.defineProperty(obj, 'c', {
get() {
return value;
},
set(newValue) {
console.log(`Setting value to ${newValue}`);
value = newValue;
},
enumerable: true,
configurable: true
});
obj.c = 5; // 输出: Setting value to 5
console.log(obj.c); // 输出: 5
4. 注意事项
-
不可枚举属性:如果
enumerable
设置为false
,属性将不会出现在for...in
循环和Object.keys()
中。Object.defineProperty(obj, 'd', { value: 4, enumerable: false }); console.log(Object.keys(obj)); // 输出: ['a', 'b', 'c']
-
不可配置属性:如果
configurable
设置为false
,无法删除属性或更改属性描述符。Object.defineProperty(obj, 'e', { value: 5, configurable: false }); delete obj.e; console.log(obj.e); // 输出: 5(无法删除)
六、Object.entries()
1. 基础介绍
Object.entries()
方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用for...in
循环遍历该对象时返回的顺序一致。
2. 语法
Object.entries(obj)
- obj:要返回其可枚举属性的对象。
3. 示例代码
const obj = { a: 1, b: 2, c: 3 };
const entries = Object.entries(obj);
console.log(entries);
// 输出: [['a', 1], ['b', 2], ['c', 3]]
示例:遍历对象的键值对
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`);
}
// 输出:
// a: 1
// b: 2
// c: 3
4. 注意事项
-
顺序问题:
Object.entries()
的返回顺序与for...in
循环一致,但for...in
循环也会遍历原型链上的可枚举属性,而Object.entries()
只返回对象自身的可枚举属性。 -
非对象参数:如果传入的参数不是对象,则会被强制转换为对象。
console.log(Object.entries('hi')); // 输出: [['0', 'h'], ['1', 'i']]
5. 扩展知识点
-
将对象转换为Map
const obj = { a: 1, b: 2 }; const map = new Map(Object.entries(obj)); console.log(map.get('a')); // 输出: 1
-
从键值对数组创建对象
const entries = [['a', 1], ['b', 2]]; const objFromEntries = Object.fromEntries(entries); console.log(objFromEntries); // 输出: { a: 1, b: 2 }
七、总结
本文详细介绍了Object
构造函数的几个重要静态方法:assign
、create
、defineProperties
、defineProperty
、entries
。通过这些方法,我们可以更灵活地操作对象的属性,实现对象的克隆、合并、继承以及属性的精细控制。
在使用这些方法时,需要注意:
Object.assign()
进行的是浅拷贝,无法深度复制嵌套对象,对访问器属性会将其转换为普通的值属性。- 使用
Object.create()
时,如果要添加属性,需要使用propertiesObject
参数,并使用属性描述符定义属性的特性。 Object.defineProperty()
和Object.defineProperties()
允许精确控制属性的特性,如可写性、可枚举性等。在定义属性时,需要了解数据描述符和存取描述符的区别。Object.entries()
提供了一种方便的方法遍历对象的键值对,但只包括自身的可枚举属性。