js中的原型对象/prototype

js中的原型对象/prototype

前置任务

在说原型对象是什么之前,我们先讨论一下对象是什么东西
在说对象是什么之前,我们又得讨论一下引用类型

引用类型


首先,js 中变量的值分两种类型

  • 引用类型
  • 值类型

关于这两种类型,我们需要从内存的角度来看

var num = 9527            //值类型
var str = "一段字符串"     // 值类型
var obj = {               // 引用类型
  attr_1:"qwer",
  attr_2:"df"
  }

上面这些数据,在内存中可能是这样的

可以看到

  • 值类型的numstr两个变量,变量名直接对应具体值
  • 引用类型的obj这个变量对应的是一段地址,而这个地址的位置存的才是真正的obj的具体值(对象)

至于为什么要这么存,这跟内存的管理有关就不展开说,简单的

你妈妈给你生了五个可爱的妹妹

  for(var i=0;i<5;i++){
    var 妹妹i号=new 妹妹()
  }

每个妹妹都是new出来的一个对象,她们都有一些属性,比如
js 妹妹1号:{ age:3 name:妹妹1号, parent:{ 妈妈:你的妈妈, 爸爸:你的爸爸 } }
每个妹妹的agename属性都是不同的,而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.constructora指向同一块内存

上面说,创建了自定义的构造函数之后,其原型对象默认会取得 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