JavaScript Prototype(原型)详解
在 JavaScript 中,原型(Prototype) 是理解对象、继承、构造函数的核心机制之一。原型让我们能够实现对象之间的继承,是 JavaScript 中面向对象编程的重要基础。
一、什么是 Prototype?
每个 JavaScript 函数在创建时,都会自动获得一个名为 prototype
的属性,它是一个对象。这个 prototype
对象包含了该函数构造出的所有实例共享的属性和方法。
在 JavaScript 中,对象可以通过原型(prototype)从其他对象继承特性。每个对象都有一个名为原型的属性。
由于原型本身也是一个对象,它也有自己的原型。这就形成了所谓的“原型链”(prototype chain)。当某个原型的原型为 null
时,原型链就到此结束。
假设你有一个名为 person
的对象,并且它有一个名为 name
的属性:
let person = {'name' : 'John'}
当你在控制台中检查 person
对象时,你会发现它有一个名为 [[Prototype]]
的属性,这个属性表示该对象的原型。
当你访问一个对象的属性时,如果该对象本身具有该属性,它会返回该属性的值。下面的例子访问了 person
对象的 name
属性:
它会按预期返回 name
属性的值。
但是,如果你访问一个对象中不存在的属性,JavaScript 引擎会在该对象的原型中查找该属性。
如果在对象的原型中仍然找不到该属性,JavaScript 引擎会继续在原型的原型中查找,直到找到该属性或到达原型链的末端(即原型为 null
)。
简单理解:
1 2 3 4 5 6 7 8 9 10 11 |
function Person(name) { this.name = name; } Person.prototype.sayHello = function () { console.log("Hello, I am " + this.name); }; let p1 = new Person("Alice"); p1.sayHello(); // Hello, I am Alice |
sayHello
定义在Person.prototype
上;- 所有通过
new Person()
创建的对象都可以访问这个方法。
二、构造函数与原型的关系图示
1 2 3 4 5 6 |
Person → prototype → { sayHello: function() { ... } } ↑ [[Prototype]] ↑ p1 = new Person() |
也可以通过代码验证:
1 2 |
console.log(p1.__proto__ === Person.prototype); // true |
三、prototype 和 proto 的区别
名称 | 说明 |
---|---|
prototype |
函数对象的属性,指向将被用作原型的对象 |
__proto__ |
实例对象的内部属性,指向其构造函数的 prototype |
1 2 3 4 5 6 |
function Animal() {} let a = new Animal(); console.log(Animal.prototype); // 原型对象 console.log(a.__proto__); // 指向 Animal.prototype |
四、原型链(Prototype Chain)
JavaScript 中对象的属性访问是通过原型链进行的:
- 首先查找对象自身;
- 如果找不到,就查找其
__proto__
(也就是构造函数的prototype
); - 一直向上查找,直到找到
Object.prototype
; - 如果仍未找到,返回
undefined
。
1 2 3 |
let obj = {}; console.log(obj.toString); // 来自 Object.prototype |
五、给所有对象添加方法
由于所有对象最终都会继承自 Object.prototype
,我们可以向其添加方法(不推荐,可能污染全局):
1 2 3 4 5 6 7 |
Object.prototype.sayHi = function () { console.log("Hi from Object"); }; let o = {}; o.sayHi(); // Hi from Object |
六、构造函数与原型继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function Animal(name) { this.name = name; } Animal.prototype.speak = function () { console.log(this.name + " makes a noise"); }; function Dog(name) { Animal.call(this, name); // 继承属性 } Dog.prototype = Object.create(Animal.prototype); // 继承方法 Dog.prototype.constructor = Dog; let d = new Dog("Lucky"); d.speak(); // Lucky makes a noise |
七、ES6 中的 class 与原型的关系
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Person { constructor(name) { this.name = name; } sayHello() { console.log("Hello, " + this.name); } } let p = new Person("Tom"); p.sayHello(); |
实际上,sayHello
方法仍然是挂载在 Person.prototype
上的。
八、小结
原型知识点 | 描述 |
---|---|
每个函数都有 prototype 属性 |
用于定义实例共享的方法 |
每个对象都有 __proto__ 属性 |
指向构造函数的 prototype |
原型链 | 实现属性查找和继承 |
class 是语法糖 | 底层仍然基于 prototype |