探究JavaScript中对象原型与原型链的基础概念
一.前言
当谈论 JavaScript 中对象的原型时,我们需要理解几个关键概念和特例。大多数情况下,JavaScript 中的对象通过原型链进行属性和方法的继承。然而,使用 Object.create(null)
创建的对象是一个例外,它们不具有原型链,因此不继承任何属性或方法。
二.原型 (显式原型)
-
定义与用途:
- 原型是函数对象的一个属性,称为
prototype
。它定义了通过该构造函数创建的实例对象的共享属性和方法。 - 实例对象可以访问并继承构造函数的原型对象的属性和方法。
- 原型是函数对象的一个属性,称为
-
简单示例:
// 构造函数
function Person(name) {
this.name = name;
}
// 在构造函数的原型上定义方法
Person.prototype.greet = function() {
return 'Hello, ' + this.name;
};
// 创建实例
let person1 = new Person('Alice');
let person2 = new Person('Bob');
// 调用原型上的方法
console.log(person1.greet()); // 输出: Hello, Alice
console.log(person2.greet()); // 输出: Hello, Bob
在这个示例中:
Person
是一个构造函数,它有一个prototype
属性,该属性上定义了greet
方法。person1
和person2
是通过Person
构造函数创建的实例。它们通过原型链继承了greet
方法,因此可以直接调用greet()
方法。
三. 原型链
- 机制与作用:
- 原型链是对象间委托的机制,用于属性和方法的查找。
- 当访问对象的属性或方法时,如果当前对象没有该属性或方法,JavaScript 引擎会沿着对象的
__proto__
(隐式原型)链向上查找,直到找到该属性或方法或链结束(即__proto__
为 null)
- 简单示例:
// 定义一个父构造函数
function Animal(name) {
this.name = name;
}
// 在父构造函数的原型上定义方法
Animal.prototype.walk = function() {
return this.name + ' is walking.';
};
// 定义一个子构造函数,继承自 Animal
function Bird(name, canFly) {
Animal.call(this, name); // 调用父构造函数的方法
this.canFly = canFly;
}
// 设置 Bird 的原型为 Animal 的实例,实现继承
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird; // 修复 constructor 指向
// 在 Bird 的原型上定义方法
Bird.prototype.fly = function() {
if (this.canFly) {
return this.name + ' is flying.';
} else {
return this.name + ' cannot fly.';
}
};
// 创建 Bird 实例
let eagle = new Bird('Eagle', true);
// 调用继承自 Animal 的方法和 Bird 自身的方法
console.log(eagle.walk()); // 输出: Eagle is walking.
console.log(eagle.fly()); // 输出: Eagle is flying.
在这个示例中:
Animal
是一个父构造函数,定义了walk
方法。Bird
是一个子构造函数,继承自Animal
构造函数。通过Object.create
方法将Bird.prototype
设置为Animal.prototype
的实例,实现了原型链的继承关系。eagle
是Bird
构造函数创建的实例,它可以访问Animal
的方法walk
和自身定义的方法fly
。
四.隐式原型
-
定义与关联:
- 对象的
__proto__
属性指向其构造函数的prototype
属性。 - 通过
__proto__
属性,对象可以访问构造函数的原型对象。
- 对象的
-
简单示例:
// 创建一个对象
let obj = {
a: 1,
b: 2
};
// 使用 __proto__ 来访问对象的隐式原型
console.log(obj.__proto__ === Object.prototype); // 输出: true
// 扩展对象的原型方法
Object.prototype.printProperties = function() {
for (let key in this) {
if (this.hasOwnProperty(key)) {
console.log(key + ': ' + this[key]);
}
}
};
// 调用扩展的原型方法
obj.printProperties();
在这个示例中:
obj
是一个普通对象,它的隐式原型__proto__
指向Object.prototype
。- 通过给
Object.prototype
扩展了一个printProperties
方法,使得所有通过Object
构造函数创建的对象(包括obj
)都可以访问和调用这个方法。
五. 所有对象都有原型吗?
- 特例:
- 大多数对象在 JavaScript 中都有原型,它们通过
__proto__
指向其构造函数的prototype
。然而,使用Object.create(null)
创建的对象是例外,它们的__proto__
是 null,因此不继承任何属性或方法。
- 大多数对象在 JavaScript 中都有原型,它们通过
- 简单示例:
let obj = Object.create(null);
console.log(obj.__proto__); // 输出: null
在这个例子中,obj
是通过 Object.create(null)
创建的,它没有原型链,因此无法继承任何方法或属性,包括 JavaScript 中所有对象默认继承的方法如 toString()
等。