• 作者:老汪软件技巧
  • 发表时间:2024-09-27 17:01
  • 浏览量:

深入理解 JavaScript 中的原型与原型链

在 JavaScript 中,原型机制是理解对象继承和属性查找的关键。本文将详细探讨原型和原型链的概念,并通过具体的代码示例来帮助你更好地理解和应用这些知识。

1. 什么是原型?

在 JavaScript 中,每个函数都有一个prototype属性,这个属性是一个对象,包含了一些可以在该函数的所有实例之间共享的属性和方法。当你使用构造函数创建一个新对象时,这个新对象会继承构造函数原型上的所有属性和方法。

function Person(name) {
    this.name = name;
}
Person.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name}`);
};
const person1 = new Person('Alice');
person1.greet(); // 输出: Hello, my name is Alice

在这个例子中:

2. 实例对象与原型的关系(1)实例对象可以修改显示继承到的属性

实例对象可以直接修改自己拥有的属性,这不会影响其他实例。

person1.name = 'Bob';
person1.greet(); // 输出: Hello, my name is Bob

(2)实例对象无法修改隐式继承到的属性

如果你试图通过实例对象来修改原型上的属性,实际上是在实例对象上创建了一个同名的新属性,这不会影响原型上的原始属性。

person1.greet = function() {
    console.log(`Hi, I'm ${this.name}`);
};
person1.greet(); // 输出: Hi, I'm Bob
const person2 = new Person('Charlie');
person2.greet(); // 输出: Hello, my name is Charlie

(3)实例对象无法给原型新增属性

如果尝试通过实例对象向原型添加新的属性,实际上是将这个属性添加到了实例对象本身。

person1.newProperty = 'New Value';
console.log(person1.newProperty); // 输出: New Value
console.log(person2.newProperty); // 输出: undefined

(4)实例对象无法删除原型上的属性

即使你使用delete关键字,也只是从实例对象上删除该属性,不会影响原型。

delete person1.name;
console.log(person1.name); // 输出: undefined

3. 对象的隐式原型

每个对象都有一个内部链接指向其构造函数的原型对象,这个链接可以通过__proto__属性访问。当尝试访问一个对象的属性时,如果该对象本身没有这个属性,JavaScript 引擎会沿着这条链向上查找,直到找到该属性或到达原型链的顶端(即null)。

_js中原型链的理解_原型链js简书

console.log(person1.__proto__ === Person.prototype); // 输出: true

4. 原型链

原型链是 JavaScript 实现继承的主要方式。每个对象都有一个原型对象,而原型对象本身也可能有原型,这样就形成了一条原型链。当访问一个对象的属性时,如果该对象自身没有这个属性,就会沿着原型链向上查找,直到找到或者到达原型链的末端(原型链的末端是 null,表示没有更多的原型可以查找)。

function Animal() {}
Animal.prototype.eat = function() {
    console.log('I am eating.');
};
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.bark = function() {
    console.log('Woof!');
};
const dog = new Dog();
dog.bark(); // 输出: Woof!
dog.eat();  // 输出: I am eating.

在这个例子中:

5. 特殊情况:没有原型的对象(1)用Object.create(null)创建的对象

通常情况下,所有对象都至少有一个原型,但有一种特殊情况,即用Object.create(null)创建的对象,它没有任何原型,因此也没有__proto__属性。由于没有原型,objWithoutProto无法继承Object.prototype上的任何方法,如toString。

const objWithoutProto = Object.create(null);
console.log(objWithoutProto.__proto__); // 输出: undefined

(2)null本身

null是一个特殊的值,它没有原型。尝试访问null的__proto__属性会导致错误,因为 null不是一个对象。

(3)基本数据类型

基本数据类型(如number、string、boolean、symbol 和 bigint)在作为字面量使用时没有原型。但是,当它们被包装成对象时,会有对应的原型。

const num = 42;
console.log(num.__proto__); // 会抛出错误
const numObj = new Number(42);
console.log(numObj.__proto__ === Number.prototype); // 输出: true

在这个例子中:

所以,在 JavaScript 中,大多数对象都有原型,但以下几种情况是没有原型的:

使用Object.create(null)创建的对象:这些对象的 [[Prototype]] 为 null,没有原型。null本身:null是一个特殊的值,没有原型。基本数据类型:基本数据类型(如 number、string、boolean、symbol 和 bigint)在作为字面量使用时没有原型,但它们的包装对象有原型。4. 函数的原型

函数也是对象,因此它们也有原型。但是,函数的prototype属性是指向其实例的原型对象,而不是其自身的原型。

function MyFunction() {}
console.log(MyFunction.__proto__ === Function.prototype); // 输出: true
console.log(MyFunction.prototype); // 输出: MyFunction 的原型对象

在这个例子中:

总结

通过本文,我们详细探讨了 JavaScript 中的原型和原型链机制。原型机制是 JavaScript 实现继承和共享行为的基础,掌握这一概念对于编写高效且易于维护的 JavaScript 代码至关重要。