Javascript对象(Object)研究_01_基本介绍_创建_访问_修改_遍历_拷贝_比较_属性特性_null原型与继承_强制类型转换

JavaScript对象(Object)研究_01_基本介绍

在JavaScript中,对象(Object)是核心概念之一,是构建一切的基础。无论是数组、函数,还是DOM元素,都可以看作对象。本篇博客将详细介绍JavaScript对象的基本概念、创建方法、属性操作、原型等内容,配以代码示例,帮助您深入理解对象的工作原理。

一、什么是对象?

对象是由一组键值对(key-value pair)组成的数据结构,键(Key)是字符串(在ES6中也可以是Symbol),值(Value)可以是任何类型的数据,包括基本类型和引用类型。

const person = {
  name: 'Alice',
  age: 25,
  greet: function() {
    console.log('Hello!');
  }
};

在上述代码中,person是一个对象,包含了nameagegreet三个属性。其中,greet属性的值是一个函数,也称为方法。

二、创建对象的方法

1. 对象字面量

最常用、最简单的方式。

const obj = {}; // 创建一个空对象

const car = {
  brand: 'Toyota',
  model: 'Camry',
  year: 2020
};

2. 构造函数

使用内置的Object构造函数。

const obj = new Object();

obj.name = 'Bob';
obj.age = 30;

3. 自定义构造函数

通过函数构造特定类型的对象。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const person1 = new Person('Charlie', 28);

4. Object.create()

从现有对象创建新对象,实现原型继承。

const animal = {
  eats: true
};

const rabbit = Object.create(animal);
rabbit.jumps = true;

console.log(rabbit.eats); // 输出: true

5. 类(Class)创建对象(ES6+)

使用class关键字定义类,然后创建对象。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const person2 = new Person('Diana', 27);
person2.greet(); // 输出: Hello, I'm Diana

三、访问和修改对象属性

1. 点表示法(Dot Notation)

const user = {
  name: 'David',
  age: 22
};

console.log(user.name); // 输出: David

user.age = 23;
console.log(user.age); // 输出: 23

2. 方括号表示法(Bracket Notation)

适用于属性名包含特殊字符或变量。

const user = {
  'first-name': 'Eve',
  age: 24
};

console.log(user['first-name']); // 输出: Eve

const prop = 'age';
console.log(user[prop]); // 输出: 24

3. 动态添加属性

user.email = 'eve@example.com';
console.log(user.email); // 输出: eve@example.com

4. 从对象中删除属性

使用delete操作符可以从对象中删除属性。

delete user.age;
console.log(user.age); // 输出: undefined

需要注意的是,delete只能删除对象自身的属性,无法删除从原型链继承的属性。同时,删除属性可能会影响程序的逻辑,应谨慎使用。

四、遍历对象属性

1. for...in 循环

for (let key in user) {
  console.log(`${key}: ${user[key]}`);
}

2. Object.keys()Object.values()Object.entries()

const keys = Object.keys(user);
console.log(keys); // 输出: ['first-name', 'email']

const values = Object.values(user);
console.log(values); // 输出: ['Eve', 'eve@example.com']

const entries = Object.entries(user);
console.log(entries); // 输出: [['first-name', 'Eve'], ['email', 'eve@example.com']]

五、对象的方法

方法是对象属性中的函数。

const calculator = {
  add(a, b) {
    return a + b;
  },
  subtract(a, b) {
    return a - b;
  }
};

console.log(calculator.add(5, 3)); // 输出: 8
console.log(calculator.subtract(5, 3)); // 输出: 2

六、对象的拷贝

1. 浅拷贝

只复制对象的第一层属性,内部的嵌套对象仍然是引用。

方法1:Object.assign()
const original = { a: 1, b: { c: 2 } };
const copy = Object.assign({}, original);

copy.b.c = 3;
console.log(original.b.c); // 输出: 3
方法2:展开运算符(Spread Operator)
const copy2 = { ...original };
copy2.b.c = 4;
console.log(original.b.c); // 输出: 4

2. 深拷贝

彻底复制对象,内部嵌套的对象也会被复制,互不影响。

方法1:JSON序列化
const deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.b.c = 5;
console.log(original.b.c); // 输出: 4

注意JSON.parse(JSON.stringify())无法复制函数、undefinedSymbol等特殊类型。

方法2:使用递归或第三方库

可以使用递归函数或诸如lodash_.cloneDeep()方法。

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  
  if (obj instanceof Array) {
    let arr = [];
    for (let item of obj) {
      arr.push(deepClone(item));
    }
    return arr;
  }
  
  if (obj instanceof Object) {
    let clonedObj = {};
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        clonedObj[key] = deepClone(obj[key]);
      }
    }
    return clonedObj;
  }
}

const deepCopy2 = deepClone(original);
deepCopy2.b.c = 6;
console.log(original.b.c); // 输出: 4

七、对象比较

1. 引用比较

对象是引用类型,直接比较两个对象实际上是比较它们的引用是否相同。

const obj1 = { x: 1 };
const obj2 = { x: 1 };

console.log(obj1 === obj2); // 输出: false

const obj3 = obj1;
console.log(obj1 === obj3); // 输出: true

2. 内容比较

需要手动比较每个属性的值。

function isEqual(objA, objB) {
  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) return false;

  for (let key of keysA) {
    const valA = objA[key];
    const valB = objB[key];

    const areObjects = isObject(valA) && isObject(valB);

    if (
      (areObjects && !isEqual(valA, valB)) ||
      (!areObjects && valA !== valB)
    ) {
      return false;
    }
  }

  return true;
}

function isObject(object) {
  return object != null && typeof object === 'object';
}

console.log(isEqual(obj1, obj2)); // 输出: true

八、对象的属性特性

每个属性都有一些内置的特性:valuewritableenumerableconfigurable

1. 查看属性描述符

const descriptor = Object.getOwnPropertyDescriptor(user, 'name');
console.log(descriptor);
// 输出: { value: 'Eve', writable: true, enumerable: true, configurable: true }

2. 定义或修改属性特性

Object.defineProperty(user, 'gender', {
  value: 'female',
  writable: false,
  enumerable: true,
  configurable: false
});

console.log(user.gender); // 输出: female

user.gender = 'male';
console.log(user.gender); // 输出: female(无法修改)

通过设置writable: false,属性变为只读。设置configurable: false后,无法再删除或修改该属性的特性。

九、对象的原型与继承

1. 原型链

每个对象都有一个原型对象,属性和方法可以沿着原型链访问。

const parent = {
  sayHello() {
    console.log('Hello from parent');
  }
};

const child = Object.create(parent);
child.sayHello(); // 输出: Hello from parent

2. 构造函数的原型

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log(`Hello, I'm ${this.name}`);
};

const person = new Person('Frank');
person.greet(); // 输出: Hello, I'm Frank

3. null原型对象

在JavaScript中,几乎所有的对象都是Object的实例;一个典型的对象从Object.prototype继承属性(包括方法),尽管这些属性可能被覆盖(或者说重写)。唯一不从Object.prototype继承的对象是那些null原型对象,或者是从其他null原型对象继承而来的对象。

可以使用Object.create(null)创建一个没有原型的对象:

const obj = Object.create(null);

console.log(Object.getPrototypeOf(obj)); // 输出: null

console.log(obj.toString); // 输出: undefined

由于obj没有Object.prototype上的属性和方法,所以toString方法是undefined

4. 原型链的影响

通过原型链,所有对象都能观察到Object.prototype对象的改变,除非这些改变所涉及的属性和方法沿着原型链被进一步重写。尽管有潜在的危险,但这为覆盖或扩展对象的行为提供了一个非常强大的机制。为了使其更加安全,Object.prototype是核心JavaScript语言中唯一具有不可变原型的对象——Object.prototype的原型始终为null且不可更改。

示例

Object.prototype.customMethod = function() {
  console.log('This is a custom method');
};

const arr = [];
arr.customMethod(); // 输出: This is a custom method

const func = function() {};
func.customMethod(); // 输出: This is a custom method

const obj = {};
obj.customMethod(); // 输出: This is a custom method

上述代码在Object.prototype上添加了一个方法,所有对象都能访问到它。但需要谨慎,因为这可能会引发命名冲突或影响第三方库的行为。

十、对象的强制类型转换

当需要将对象转换为原始类型(如字符串、数字或布尔值)时,JavaScript会尝试调用对象的toStringvalueOf方法。

1. toString()方法

对象默认的toString()方法返回[object Object]

const obj = { a: 1 };
console.log(obj.toString()); // 输出: [object Object]

可以自定义toString()方法:

const obj = {
  a: 1,
  toString() {
    return `a is ${this.a}`;
  }
};

console.log(String(obj)); // 输出: a is 1

2. valueOf()方法

valueOf()方法通常返回对象本身,但可以自定义以返回一个原始值。

const obj = {
  a: 10,
  valueOf() {
    return this.a;
  }
};

console.log(obj + 5); // 输出: 15

3. Symbol.toPrimitive

在ES6中,可以使用Symbol.toPrimitive定义对象的强制类型转换行为。

const obj = {
  a: 100,
  [Symbol.toPrimitive](hint) {
    if (hint === 'number') {
      return this.a;
    }
    if (hint === 'string') {
      return `Value is ${this.a}`;
    }
    return this.a;
  }
};

console.log(+obj); // 输出: 100
console.log(`${obj}`); // 输出: Value is 100
console.log(obj + 10); // 输出: 110
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值