原型是JavaScript最重要的概念。同时也是初级开发者最忌惮的内容,缘由在于网上不多有关于它的合理描述。javascript
但事实上,原型很简单,你能够很轻松的掌握它的知识要点。html
了解什么是原型以前,咱们先看一个示例:前端
var obj = {}; obj.toString(); // "[object Object]"
上面的例子中,咱们声明了一个空对象,并无为它添加toString
属性方法,但这个方法却能够被成功调用并输出。是否是以为很神奇?java
缘由在于JavaScript的对象都有一个内置的[[Prototype]]
私有属性,这个属性指向另外一个对象,咱们称这个对象为原对象的原型。当JS引擎访问obj
的toString
属性时,首先会去obj
对象查找,发现找不到,就沿着obj
的[prototype]
属性去他的原型上查找。数组
经过Chrome开发者工具,咱们能够看到这个obj
原型的真面目:函数
图中咱们能够看到,obj
的原型对象已经定义了一个toString
属性。因此,空对象也可使用toString()
这个属性方法。工具
原型意义在于实现属性的继承。学习
想象一下:编写一个JS脚本,建立1000个数组实例。若是在每一个数组实例中单独定义数组的操做方法,不只代码冗余,还会内存资源极大的浪费。有了原型,咱们只须要把数组的操做方法定义在数组的原型上便可,实现了属性的共享。this
举个例子:prototype
咱们但愿JS的数字类型能提供一个方法判断当前数字是否为奇数,没有原型的状况下,你只能这么作:
var num = new Number(99); num.isOdd = function(){return this % 100 !== 0}; num.isOdd(); // true
可是这样是没有意义的,由于isOdd()
方法定义在num
变量上面,其余数字类型并不能使用它:
var num2 = 100; num2.isOdd(); // num2.isOdd is not a function
有了原型概念之后,因为全部数字类型都指向了同一个原型,咱们能够把isOdd
方法定义在这个原型上,这样,全部数字类型就都能调用到这个方法了:
var num1 = 99, num2 = 100, num3 = 0; Number.prototype.isOdd = function(){return this % 100 !== 0}; num1.isOdd(); // true num2.isOdd(); // false num3.isOdd(); // false
Number.prototype
指向数字类型的原型原型并非一个特别的存在,它也只是一个普通的对象而已。
换句话说,原型也能够拥有属于它的原型。若是把对象的[[prototype]]
属性想象成链条,就造成了一条原型链。
接下来,咱们经过一个示例来看下JS引擎是如何经过原型链查找属性的:
如今建立三个对象,并经过Object.setPrototypeOf()
方法将它们连结成一条原型链:
var son = {a: 1, b: 2}, parent = {b: 3, c: 4}, ancestor = {d: 5}; Object.setPrototypeOf(son, parent); Object.setPrototypeOf(parent, ancestor); son.a // 1 son.b // 2 son.c // 4 son.d // 5
这三个对象造成将造成一条原型链,JS引擎将从左往右有序地查找目标属性:
JavaScript分别经过 Object.setPrototypeOf() 和Object.getPrototypeOf() 两个方法来设置和获取对象的原型。
var parent = {type: 'parent'}, son = {type: 'son'}; Object.setPrototypeOf(son, parent); Object.getPrototypeOf(son) === parent // true
JavaScript提供了一些内置对象(构造函数),好比Object, String, Array, Boolean等等,它们提供了prototype
属性,指向实例的原型。所以,能够简单地经过instance.constructor.prototype
来获取
var obj = {}, str = '', arr = [], bl = true; Object.getPrototypeOf(obj) === obj.constructor.prototype // true Object.getPrototypeOf(str) === str.constructor.prototype // true Object.getPrototypeOf(arr) === arr.constructor.prototype // true Object.getPrototypeOf(bl) === bl.constructor.prototype // true
constructor
这个属性,能够阅读构造函数一节。经过instance.constructor.prototype
这种方式获取原型的方式并非绝对可靠的。由于实例的constructor
属性是可改变的(mutable)。一旦属性,instance.constructor.prototype
便没法正确指向实例的原型。
var obj = new Object(); obj.constructor = Array; Object.getPrototypeOf(obj) === obj.constructor.prototype // false
上面的例子中,咱们随意修改了obj的constructor
属性,而后obj.constructor.prototype
便再也不指向obj
的原型了。
constructor
也是可变的(内置构造函数的constructor
被配置为不可改变)综合来讲,咱们推荐使用 Object.getPrototypeOf() 方法获取实例原型。