js中的原型对象/prototype
前置任务
在说原型对象是什么之前,我们先讨论一下对象是什么东西
在说对象是什么之前,我们又得讨论一下引用类型
引用类型
首先,js 中变量的值分两种类型
- 引用类型
- 值类型
关于这两种类型,我们需要从内存的角度来看
var num = 9527 //值类型
var str = "一段字符串" // 值类型
var obj = { // 引用类型
attr_1:"qwer",
attr_2:"df"
}
上面这些数据,在内存中可能是这样的
可以看到
- 值类型的
num
和str
两个变量,变量名直接对应具体值 - 引用类型的
obj
这个变量对应的是一段地址,而这个地址的位置存的才是真正的obj的具体值(对象)
至于为什么要这么存,这跟内存的管理有关就不展开说,简单的
你妈妈给你生了五个可爱的妹妹
for(var i=0;i<5;i++){
var 妹妹i号=new 妹妹()
}
每个妹妹都是new出来的一个对象,她们都有一些属性,比如
妹妹1号:{
age:3
name:妹妹1号,
parent:{
妈妈:你的妈妈,
爸爸:你的爸爸
}
}
每个妹妹的
age
和name
属性都是不同的,而parent
属性都是相同的,这时候如果每个妹妹都存一份parent
就太浪费内存了,所以我们可以存个地址.内存中这个地址的位置存真正的parent
信息,这样就可以很好的利用起宝贵的内存空间啦
ps: 我们建立一个概念,一个对象是一个独立的'块',而不是妹妹i号.parent
这样一条属性,妹妹i号.parent
这条属性指向一个对象,也不用纠结,先往下看
对象
前面我们说了,对象是独立的块内存,要想访问或者操作对象,就得通过该对象的的地址,而变量存储的就是这个地址
然后我们来看
var obj = {
attr_1: "qwer",
attr_2: "df"
};
var obj_2=obj
obj_2.attr_1="qwqaqaaaaaawer"
console.log(obj.attr_1) //qwqaqaaaaaawer
这样,为什么改的是obj_2.attr_1
而打印obj.attr_1
的时候是qwqaqaaaaaawer
应该就很清楚了
原型对象
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。
注意两点
- 函数的
prototype
属性指向函数的原型对象,而不是说prototype
就是原型对象,prototype是地址,内存中这个地址的位置上的东西才是原型对象- 函数也是对象,所以函数也可以有属性
拓展阅读: 为什么要创建原型对象
在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数(下图这个例子中的a)的地址。看图理解:
到目前为止,内存中是这样的
思考题:为什么a.prototype.constructor==a
答案:a.prototype.constructor
和a
指向同一块内存
上面说,创建了自定义的构造函数之后,其原型对象默认会取得 constructor 属性
然鹅:
这个a.prototype.toString
函数根本没有定义,上面的内存图中也看不到它,那它是从哪哪冒出来的???
至于其原型对象的其他方法,则都是从 Object 继承而来的。
(这个__proto__
是什么,看下面的详细讲解)
当调用构造函数创建一个新实例后(是一个对象),该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。
es5 中管这个指针叫[[Prototype]]
。虽然在 js 中没有标准的方式访问[[Prototype]]
,
但 Firefox、Safari 和 Chrome 在每个对象(函数的原型对象也是对象,所以也有__proto__
属性)上都支持一个属性__proto__
;
再来看一遍这段代码:
a.prototype
指向原型对象,原型对象是由构造函数Ojbect
生成的Object
也是一个函数,是Object.prototype
指向Object的原型对象- 思考题:
a.prototype.__proto__
是函数a
的原型对象的一条属性,这个属性的属性值是一个地址,那么内存中这个地址存的是什么?
答案: 存的是Object
的原型对象
附内存图一张,方便理解:
这个连接存在于实例的原型对象与构造函数的原型对象之间,而不是存在于实例与构造函数之间。
a
的原型对象是由构造函数Object
生成的,他们两个之间存在链接(通过__proto__
)
接着说,js中,有这样一条规则:访问一条属性(假设是属性attr)时,在当前对象(假设是obj)中找不到的,就往obj.__proto__
找,即obj.__proto__.attr
,再找不到,就往obj.__proto__.__proto__
找,直到找到或者obj.__proto__.......
为null
才停止
所以 前面的a.prototype.toString
实际上是a.prototype.__proto__.toString
也就是Object.tostring