对象
对象
对象是一个抽象的概念,我们可以通过观察现实世界中某种事物的外貌及表现,总结归纳其行为特点,然后用对象的方式来定义和描述它,将现实的事物转化为计算机中的数据结构。通过这种方式,我们几乎可以描述现实世界的万事万物
在 JavaScript 中,对象(Object
)是一种重要并且复杂的数据类型,它用于存储键值对和更复杂的实体,对象是引用类型的数据(非原始值),它的值存放在内存中,将内存中的引用地址存放在栈中赋值给变量
创建对象
在 JavaScript 中,有几种方法可以创建对象:
- 对象字面量:这是最简单的创建对象的方法。你可以使用花括号
{}
来定义一个对象,然后在其中定义属性和方法。例如:let myObject = { property1: 'value1', property2: 'value2', method1: function() { // do something } };
Object
构造函数:你可以使用new Object()
来创建一个新的对象实例,然后再为其添加属性和方法。例如:let myObject = new Object(); myObject.property1 = 'value1'; myObject.property2 = 'value2'; myObject.method1 = function() { // do something };
- 构造函数:你可以定义一个构造函数,然后使用
new
关键字来创建一个新的对象实例。构造函数通常用来创建具有相同属性和方法的多个对象。例如:function MyObject(property1, property2) { this.property1 = property1; this.property2 = property2; this.method1 = function() { // do something }; } let myObject1 = new MyObject('value1', 'value2'); let myObject2 = new MyObject('value3', 'value4');
对象创建后,会以键值对的形式将数据存储在内存中,在对象中,对象的键可以是任意字符形式的,对象的值可以是任意值
new 关键字
在上面的示例中,我们使用 new
关键字来创建了对象,那么这其中的过程是怎样的呢?
new
关键字首先会创建一个空对象- 如果构造函数的
prototype
属性是一个对象,则将新对象的prototype
属性指向构造函数的prototype
属性,否则指向Object.prototype
- 调用构造函数,并将新对象作为构造函数的
this
进行绑定,将参数传递给构造函数 - 如果构造函数返回了一个对象,则返回该对象;否则返回新对象
对象的基本操作
在创建对象后,我们可以在创建的对象上进行一些基本操作
添加属性和方法
我们可以通过对象来描述某种真实事物,而要想将之准确描述,为对象添加属性和方法是必不可少的,向对象添加属性或方法有两种形式:
- 直接赋值:通过
.
或[]
运算符来向对象添加属性或方法。例如:let myObject = {}; myObject.newProperty = '这是一个新属性'; console.log(myObject.newProperty); // 输出: 这是一个新属性
let myObject = {}; myObject['newProperty'] = '这是一个新属性'; console.log(myObject['newProperty']); // 输出: 这是一个新属性
- 对象方法:通过
Object.defineProperty()
方法向对象添加属性或方法。例如:let myObject = {}; Object.defineProperty(myObject, 'newProperty', { value: '这是一个新属性', writable: true, enumerable: true, configurable: true }); console.log(myObject.newProperty); // 输出: 这是一个新属性
删除对象的属性
我们可以通过 delete
运算符来删除对象的属性,例如:
let myObject = {
property1: 'value1',
property2: 'value2'
};
delete myObject.property1;
console.log(myObject.property1); // 输出: undefined
遍历对象的属性
我们可以通过 for...in
循环来遍历对象的属性,例如:
let myObject = {
property1: 'value1',
property2: 'value2'
};
for (let property in myObject) {
console.log(property + ': ' + myObject[property]);
}
判断属性是否存在
我们可以通过 in
运算符来判断对象是否存在某个属性,例如:
let myObject = {
property1: 'value1',
property2: 'value2'
};
console.log('property1' in myObject); // 输出: true
console.log('property3' in myObject); // 输出: false
对象的方法
JS 在对象上内置了一些常用的方法,我们可以通过这些方法来操作对象,例如:
Object.keys()
我们可以通过 Object.keys()
方法来获取对象的所有属性的键,例如:
let myObject = {
property1: 'value1',
property2: 'value2'
};
console.log(Object.keys(myObject)); // 输出: ['property1', 'property2']
Object.values()
我们可以通过 Object.values()
方法来获取对象的所有属性的值,例如:
let myObject = {
property1: 'value1',
property2: 'value2'
};
console.log(Object.values(myObject)); // 输出: ['value1', 'value2']
Object.entries()
我们可以通过 Object.entries()
方法来获取对象的所有属性的键值对,例如:
let myObject = {
property1: 'value1',
property2: 'value2'
};
console.log(Object.entries(myObject)); // 输出: [['property1', 'value1'], ['property2', 'value2']]
Object.assign()
我们可以通过 Object.assign()
方法来将一个或多个对象的所有属性和方法合并到目标对象中,例如:
let target = {
property1: 'value1',
property2: 'value2'
};
let source1 = {
property3: 'value3',
property4: 'value4'
};
let source2 = {
property5: 'value5',
property6: 'value6'
};
Object.assign(target, source1, source2);
console.log(target); // 输出: {property1: 'value1', property2: 'value2', property3: 'value3', property4: 'value4', property5: 'value5', property6: 'value6'}
Object.freeze()
我们可以通过 Object.freeze()
方法来冻结一个对象,即无法再修改该对象的属性和方法,例如:
let myObject = {
property1: 'value1',
property2: 'value2'
};
Object.freeze(myObject);
myObject.property1 = 'new value1'; // 无效
console.log(myObject.property1); // 输出: value1
Object.seal()
我们可以通过 Object.seal()
方法来密封一个对象,即无法再添加新的属性和方法,但可以修改已有的属性和方法,例如:
let myObject = {
property1: 'value1',
property2: 'value2'
};
Object.seal(myObject);
myObject.property1 = 'new value1'; // 有效
console.log(myObject.property1); // 输出: new value1
myObject.property3 = 'value3'; // 无效
console.log(myObject.property3); // 输出: undefined
Object.is()
我们可以通过 Object.is()
方法来判断两个值是否相等,例如:
let value1 = { property1: 'value1' };
let value2 = { property1: 'value1' };
console.log(Object.is(value1, value2)); // 输出: false
console.log(Object.is(0, -0)); // 输出: false
console.log(Object.is(NaN, NaN)); // 输出: true
对象的克隆
在使用对象时,由于对象引用数据类型的特点,有时我们不希望在使用对象时修改原对象,而是希望创建一个新的对象,来避免对原对象的影响,这时我们可以使用对象的克隆来创建一个新的对象
浅层克隆
浅层克隆是指只复制对象的第一层属性,如果属性值是基本类型,则直接复制;如果属性值是引用类型,则只复制引用地址,即两个对象指向同一个内存地址,修改其中一个对象的属性,会影响到另一个对象的属性。例如:
let originalObject = {
property1: 'value1',
property2: 'value2',
property3: {
nestedProperty1: 'nested value1',
nestedProperty2: 'nested value2'
}
};
let clonedObject = Object.assign({}, originalObject);
clonedObject.property1 = 'new value1'; // 修改克隆对象的属性
clonedObject.property3.nestedProperty1 = 'new nested value1'; // 修改克隆对象的嵌套对象的属性
console.log(originalObject); // 输出: {property1: 'value1', property2: 'value2', property3: {nestedProperty1: 'new nested value1', nestedProperty2: 'nested value2'}
console.log(clonedObject); // 输出: {property1: 'value1', property2: 'value2', property3: {nestedProperty1: 'new nested value1', nestedProperty2: 'nested value2'}
以上是对对象的浅层克隆(也叫浅拷贝)
深层克隆
深层克隆是指复制对象的所有层属性,如果属性值是基本类型,则直接复制;如果属性值是引用类型,则递归复制属性值,即两个对象指向不同的内存地址,修改其中一个对象的属性,不会影响另一个对象的属性。例如:
当对象的结构很复杂时,我们就需要使用深层克隆(也叫深拷贝)来创建一个新的对象,例如:
let originalObject = {
property1: 'value1',
property2: 'value2',
nestedObject: {
nestedProperty1: 'nested value1',
nestedProperty2: 'nested value2'
}
};
function deepClone(obj) {
if (obj === null) return null;
if (typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Array) {
const newArr = [];
for (let i = 0; i < obj.length; i++) {
newArr[i] = deepClone(obj[i]);
}
return newArr;
}
if (obj instanceof Object) {
const newObj = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}
return null;
}
let clonedObject = deepClone(originalObject);
clonedObject.nestedObject.nestedProperty1 = 'new nested value1'; // 修改克隆对象的嵌套对象
console.log(originalObject); // 输出: {property1: 'value1', property2: 'value2', nestedObject: {nestedProperty1: 'new nested value1', nestedProperty2: 'nested value2'}}
对象的类型转换
在 JS 中,对象也可以通过某些规则转换成其他类型的数据(通常是原始值),这对我们开发需求或者解决问题很有帮助
对象转换通常有以下三种类型:
string
:将对象转换成字符串number
:将对象转换成数字default
:针对少数运算,通常转换结果为number
转换规则
对象进行类型转换时,一般遵循如下规则:
首先会调用对象上的
[Symbol.toPrimitive](hint)
方法(如果存在这个方法),这个方法是内建的方法,用来给类型转换方法命名,具体用法如下:let obj = { text: "hello", num: 100, [Symbol.toPrimitive](hint) { console.log(`hint: ${hint}`); return hint == "string" ? this.text : this.num; } } console.log(obj + '') // 'hint:default' 100 console.log(++obj) // 'hint:number' 101 console.log(obj - 100) // 'hint:default' 1
如果没有
[Symbol.toPrimitive]
方法,则按照以下顺序进行转换:- 如果
hint
是string
,则先尝试调用toString()
方法,再尝试调用valueOf()
方法 - 如果
hint
是number
,则先尝试调用valueOf()
方法,再尝试调用toString()
方法
- 如果
这些方法都必须返回一个原始值
hint
参数的取值可以是 string
、number
、default
,分别表示将对象转换成字符串、数字、其他类型,更多详细信息请 点击这里