JavaScript 中实例化生成对象的相关探讨
JavaScript 中实例化生成对象的相关探讨
在 JavaScript 世界中,对象的实例化是一个关键且基础的概念。当我们使用构造函数创建对象时,会引发一系列关于对象之间联系、原型链以及相关概念的思考。
让我们通过一段代码来深入探讨这些问题:
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(`${this.name} 今年 ${this.age}岁`)
}
return this;
}
const p1 = new Person('张三', 20);
const p2 = new Person('李四', 30);
p1.say();
p2.say();
当我们实例化两个不同的对象 p1
和 p2
后,它们在表面上看起来是两个独立的个体。然而,通过 console.log(p1.__proto__ === p2.__proto__)
这行代码,我们发现它们的原型对象竟然指向同一个对象,这就引出了我们对原型链等概念的深入探讨。
一、constructor 的意义与作用
每个实例对象在创建时,都会自动拥有一个 constructor
属性,它指向创建该对象的构造函数。
- 其作用之一是用来记录当前对象的构造函数是谁,方便后续的查找。
- 可以用来判断当前对象是否为该构造函数的实例。
- 还能在创建对象时,用于初始化对象。
二、prototype 的意义与作用
prototype
的存在也具有重要意义:
- 它可以用来记录当前对象的原型是谁,便于进行查找。
- 能够判断当前对象的原型是谁。
- 可以获取当前对象的原型。
- 非常重要的一点是,它可以用来给对象添加共享的方法。
- 同时在实现继承方面也发挥着关键作用。
三、使用构造函数创建对象的问题及解决方案
使用构造函数创建对象存在一些问题,比如会浪费内存空间,因为每个实例对象都会生成一个相同的函数方法;代码会显得冗余,每个实例对象都有一套相同的函数方法;性能也会较低,因为每次调用方法都要重新创建一遍。
为了解决这些问题,我们可以采用如下方案:
function Person(name, age) {
this.name = name;
this.age = age;
return this;
}
Person.prototype.say = function () {
console.log(`${this.name} 今年 ${this.age}岁`)
}
const p1 = new Person('张三', 20);
const p2 = new Person('李四', 30);
p1.say();
p2.say();
这样通过将共享方法挂载到原型对象上,实现了原型共享,有效地解决了上述问题。
四、proto 和原型对象 prototype
的区别
__proto__
描述的是当前对象的原型是谁,指向的是构造函数的原型对象,它体现的是实例与原型之间的一种类似血缘关系的联系,即因为有这种联系所以继承了相关的特性。
而 prototype
描述的是模板的一种能力,类似父亲所具有的优良品质。
从下图可以清晰地看出这是一个链式传递的关系:
不难看出这是一个链式传递的关系,__proto__指向的是原型对象,原型对象指向的是构造函数的原型对象,而构造函数的原型对象指向的是 Object 的原型对象,Object 的原型对象指向的是 null。我们将这个链式传递的关系称之为原型链。
通过对这些概念的深入理解和掌握,我们能够更好地在 JavaScript 开发中运用对象的实例化、原型链等特性,编写出更加高效、简洁且易于维护的代码。