js 原型链
Last updated
Was this helpful?
Last updated
Was this helpful?
你有没有曾疑惑过,为什么当我们声明了一个object
,就能用.hasOwnProperty
来判断里面有没有这个key
?
这个.hasOwnProperty
方法哪里来的呢?是在新建object
的时候,每个实例下面都新建这个方法吗?
其实
lint
的规范是不让用obj.hasOwnProperty
的。如果要实例化一个object
,一般是用Object.creat(null)
,这样object
就不会继承任何的方法。我们在prototype
写方法的时候也不会污染原有的Object
原型。毕竟object
是所有人的爹,这样做会更加安全吧。不过本文用
object
为例来说明继承的问题,可以把它想象成string
的.length
一样,差不多一个意思
显然不是。 一个obj
就写一次方法、占一次内存,那存一个新华字典,岂不是要几千块硬盘了?
既然大家都是obj
,就不如在obj
的原型上定义各类方法,每次声明的obj
都能继承原型上面的函数。
换言之,就算新建了个obj
的object
实例,obj
底下什么方法都还没定义。但是它能顺着原型链一层层原型往上找,找到.hasOwnProperty
方法,就可以拿来用了。当然找不到就会返回undefined
。
也就是说,原型链的出现是为了继承,把公用的方法抽象到原型上。
那么这些实例,是怎么找到自己的原型的呢?
每个实例创建的时候,都会附带一个.__proto__
的方法,指向自己的原型。
比如我们实例化的obj
,它的.__proto__
指向的是Object
对象
可以看到,object
的原型除了hasOwnProperty
还有toString
、valueOf
等等。任何实例化的obj
都能通过.__proto__
找到并使用这些方法。
你可能会问,object
的原型也是个对象(object)啊,它有没有原型呢?
答案是没有了。或者说,它的原型就是null
。
所以这一条原型链就很明显了:
声明obj
的过程,其实是个new
的过程
其中的Object()
是一个构造函数。它能构造出实例,就被称为“构造函数”。其实本质上就是个函数。
当Object()
在new
了一个实例之后,除了让实例能通过.__proto__
找到原型对象之外,还与它进行了绑定。
原型对象增加一个.constructor
属性,指向构造函数
构造函数增加一个.prototype
属性,指向原型对象
这下原型对象和构造函数,也能互相找到对方了
那么除了Object()
能构造出实例之外,还有其他构造函数吗?
多了去了。
最基本的是js的基本数据类型,比如值类型中有:字符串(String)、数字(Number)、布尔(Boolean);引用数据类型中有:对象(Object)、数组(Array)、函数(Function)。
我们平常声明一个数组、函数,都相当于new
了一个Array
或Funcion
。
不过值类型new
出来的是都是其对象。比如
其中a.__proto__
指向的原型,就是String
对象,类似于未定义版的String{}
说到底,构造函数就是个函数,换言之任何函数,都能成为构造函数。 【箭头函数似乎不行,具体原因还不太懂,似乎因为没有constructor?】
举例而言,我们可以新建一个Person()
函数,来实例化person
对象,让实例化后的每个对象都能sayHi()
我们还能给两个小人命名
也可以修改一下person
的原型对象,都增加两个方法:
所以现在就长这样:
明显person
的原型也是个对象啊,可以通过.__proto__
打印发现,它的原型就是obj
的原型:
因此,Person()
在实例化对象的时候,一方面通过Object()
实例化了一个object
,作为person
的原型。另一方面又把实例化后的person1
的.__proto__
指向这个原型。
函数都是Function
实例化出来的
构造函数,其实就是函数
换言之,构造函数也是Function
实例化出来的。
既然是实例化,那除了会生成实例化对象外,也会对应生成一个原型对象,和构造函数互指。
之前我们实例化后,new
出来的实例化对象,都会把.__proto__
指向其对应生成的原型对象。构造函数也是对象,因此也不例外。
【为了避免太乱,只突出了构造函数的继承指向】
其中Function()
的继承对象是很奇怪的【棕色的继承箭头】,就是它的原型对象。
可以这么理解:Function()
能实例化它自己,所以.__proto__
也就指向了自己的原型对象。
现在这个图已经很乱了,但有几点可以理清:
构造函数的prototype
和其构造出来的原型的constructor
,相互指向对方
构造函数 实例化(new
)出来的对象,会继承对应的原型。(.__proto__
指向原型)
在一个对象上找不到的方法,就会顺着.__proto__
,一层层往原型的下面找。如果找到了null
都没找到,那就是没这个方法了。
整个顺着.__proto__
向上找的链条,就是原型链。