1.prototype
在讨论prototype之前先看一段代码:
function Npc(hp){
this.hp = hp
this.die = function(){
this.hp = 0
}
}
var npc1 = new Npc(100) //Npc {hp: 100, die: ƒ}
var npc2 = new Npc(90) //Npc {hp: 90, die: ƒ}
我们用构造函数创建了2个实例对象,两者都有相同的方法。但两者抽象出来实际上可以归类为同一类,可以想象当有较多对象时,这种方法无疑会浪费系统资源,因为每生成一个这类对象就会生成同样的方法。应用继承的方法可以解决这类问题。
很多的语言都支持面向对象的思想,而继承则是面向对象的特性之一。c++和java引入了“类”的概念,但JavaScript却没有这一概念,JavaScript通过原型对象来实现继承。
可以改写这段代码:
function Npc(hp){
this.hp = hp
}
Npc.prototype.die = function(){
this.hp = 0
}
var npc1 = new Npc(100)
npc1.hp // 100
npc1.die()
npc1.hp // 0
每个函数中都有prototype属性,该属性指向一个对象,在构造函数中,prototype指向的对象为生成的实例对象的原型,所有实例对象继承了原型中的所有属性和方法。重复的内容都放在这个原型中作为公有属性/方法,所以这样就不必在生成对象的时候各自生成同样的方法了,通过继承实例可以调用原型中的方法。
简单小结一下:构造函数中prototype属性指向了各实例的原型,实例会继承原型中的内容。
2. __ proto __
JavaScript中,每个对象中都有__ proto __属性,该属性直接指向本对象的原型。从上面我们可以知道:函数中prototype指向实例的原型。那么我们可以得出下面的结论:
npc1.__proto__ === Npc.prototype //true
梳理一下它们的关系:
1.首先npc1这个实例是由Npc这个构造函数生成
2.代码中为构造函数的原型(prototype指向)添加了“die”的方法
3.实例对象自动继承了原型对象(prototype指向的对象)的内容
4.npc1的__ proto __属性指向它的原型,构造函数的prototype属性也指向原型,两个原型是同一个对象
实际上,不仅仅是这种对象是通过原型来继承,这种模式同样适用于其他对象。
String对象
var str = new String()
str.__proto__ === String.prototype //true
Number对象
var num = new Number()
num.__proto__ === Number.prototype //true
Object对象
var obj = new Object()
obj.__proto__ === Object.prototype //true
上面3个例子中String()、Number()、Object()作为各自实例的构造函数
到了这里可以得出推论:实例.__ proto __ === 构造函数.prototype,它们代表同一个对象,即实例的原型对象。
同样,已知原型对象,可以通过constructor属性来查询构造函数:
str.__proto__.constructor === String //true
num.__proto__.constructor === Number //true
obj.__proto__.constructor === Object //true
小结:在局部的原型与实例对象以及构造函数之间,有如下关系
局部对象之间的关系
3.原型链
了解了局部对象之间的关系后,那么什么是原型链呢?
原型链,从字面意思来看是“原型组成的链”,或许你知道“食物链”是什么意思,我们可以参考其他术语来给原型链一个模糊的定义:原型链是各种对象之间通过某种关系连在一起的链。这些对象既可能是原型,也可能是实例对象。
从结构上来说,原型链是上面总结的局部关系的集合,任何一个对象都有原型,从底端的实例对象开始,它的原型仍然有原型,既“.__proto __. __proto __.....”的关系。在JavaScript里,所有的原型都可以向上追溯到Object.prototype,Object.prototype是原型链的起点,但Object.prototype也是一个对象,它的__proto __属性是null,即:
Object.prototype.__proto__ //null
原型链
在调用属性或方法时,会先在当前实例对象中寻找是否有该方法,如果没有则沿着原型链上找,到这就是为什么所有对象都有toString和valueOf这种方法的原因。
值得注意的是,Function和Object既可以作为构造函数又是对象
- Function作为对象,它的构造函数是Function
Function.constructor === Function //true
Function.__proto__ === Function.prototype /*true 或者这么理解:
Function作为函数,函数会继承Function的prototype*/
Function instanceof Object //true
- Object作为构造函数,同时Function.prototype作为对象也应该有原型
Function.prototype.__proto__ === Object.prototype //true
- Object作为函数,函数继承自Function的原型
Object.__proto__ === Function.prototype //true
- Object作为函数,函数继承自Function的原型,同时Function的prototype也是一个对象,Function原型对象会继承自Object.prototype,即Object的原型。
Object.__proto__.__proto__ === Object.prototype //true
网友评论