JavaScript 是一种基于原型的语言,理解原型(prototype)和原型链(prototype chain)对于深入掌握 JavaScript 的面向对象编程非常重要。本文将详细解释什么是原型、原型链以及隐式原型,并通过示例代码来展示它们的工作原理。
原型
在 JavaScript 中,每个函数都有一个 prototype
属性,这个属性是一个对象,包含了由该函数创建的实例对象的公共祖先。当通过构造函数创建一个对象时,实例对象会继承构造函数 prototype
对象上的属性和方法。
// 定义一个构造函数 function Person(name) { this.name = name; } // 在 Person 的 prototype 上定义一个方法 Person.prototype.sayHello = function() { console.log(`Hello, my name is ${this.name}`); }; // 通过构造函数创建实例对象 const person1 = new Person('Alice'); const person2 = new Person('Bob'); // 调用实例对象的方法 person1.sayHello(); // 输出:Hello, my name is Alice person2.sayHello(); // 输出:Hello, my name is Bob
在这个示例中,sayHello
方法被定义在 Person
构造函数的 prototype
上,因此所有通过 Person
构造函数创建的实例对象都可以访问这个方法。
原型链
当我们访问对象的属性或方法时,JavaScript 引擎会先在对象自身查找。如果没有找到,它会沿着原型链向上查找,直到找到属性或方法,或者到达原型链的顶端 null
为止。这个链状的查找过程被称为原型链。
// 定义一个构造函数 function Animal(type) { this.type = type; } Animal.prototype.eat = function() { console.log(`${this.type} is eating`); }; // 定义另一个构造函数,并将其原型设置为 Animal 的实例 function Dog(name) { Animal.call(this, 'Dog'); this.name = name; } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.bark = function() { console.log(`${this.name} is barking`); }; // 创建 Dog 的实例 const dog = new Dog('Buddy'); // 调用实例对象的方法 dog.eat(); // 输出:Dog is eating dog.bark(); // 输出:Buddy is barking
在这个示例中,Dog
的原型被设置为 Animal
的实例,因此 Dog
的实例对象可以访问 Animal
原型上的 eat
方法。这个查找过程就是原型链的实际应用。
将代码放到浏览器我们可以通过控制台我们可以查看它的原型查找过程,直到找到object的隐式原型为空原型链的查找就到头了
隐式原型
每个 JavaScript 对象都有一个 __proto__
属性,这个属性指向创建该对象的构造函数的 prototype
。通过 __proto__
属性,可以实现原型链的查找。
// 检查 dog 对象的隐式原型 console.log(dog.__proto__ === Dog.prototype); // 输出:true console.log(Dog.prototype.__proto__ === Animal.prototype); // 输出:true console.log(Animal.prototype.__proto__ === Object.prototype); // 输出:true console.log(Object.prototype.__proto__ === null); // 输出:true
在这个示例中,我们可以看到 dog
对象的 __proto__
属性指向 Dog
构造函数的 prototype
,而 Dog.prototype.__proto__
指向 Animal.prototype
,最终 Animal.prototype.__proto__
指向 Object.prototype
,Object.prototype.__proto__
为 null
,表示原型链的终点。
所有对象都有原型吗?
并不是所有的对象都有原型。通过 Object.create(null)
创建的对象没有原型。
// 创建一个没有原型的对象 const obj = Object.create(null); console.log(obj.__proto__); // 输出:undefined console.log(Object.getPrototypeOf(obj)); // 输出:null
在这个示例中,我们创建了一个没有原型的对象 obj
,因此 obj.__proto__
为 undefined
,Object.getPrototypeOf(obj)
返回 null
。
结论
通过理解原型、原型链和隐式原型,我们可以更好地掌握 JavaScript 的面向对象编程。这些概念帮助我们理解对象之间的继承关系,以及如何通过原型链实现方法和属性的共享。希望这篇文章和示例代码能帮助你更深入地理解 JavaScript 中的原型机制。
最后:
大家可以通过分析一下这张图来加深印象