再来说Javascript中的原型
2016-12-22
toString是从哪来的
先来看一段代码
1 | var obj = {}; |
这段代码很简单,一个对象调用了toString方法,但是如果我问你为什么可以调toString方法?toString是从哪来的?
对于大多学过几天js的人知道,因为obj是继承了Object对象,而toString是Object对象的方法,所以obj可以调toString
那么很明显,这个回答是正确的错误答案,因为关键字没有说到:原型
原型是什么?
首先,原型是一个普通的对象。
为什么说是普通,说明一定有不普通的对象,比如function类型的对象,它就属于不太普通的对象,这个后面说
在js的世界里,一个普通对象是怎么样
- 对象上可以定义方法或属性
- 对象一定有一个原型
- 对象的原型上的方法和属性,对象本身可以通过
.
语法获取,就像调用自己的方法和属性一样 - 对象的原型是指向其构造函数的prototye属性的指针(引用)
- 对象本身的方法或属性会覆盖掉原型上的方法或属性
一个对象一定有一个原型,而原型又是一个对象,那么原型上就会有原型,这个原型依旧是对象,而这个对象依旧有原型,这样不停往上追溯,直到js的根对象Object.prototype,这个就叫原型链
实际上每个对象都有一个存放原型的内部属性 [[prototype]]
(实际上看不到,ecma的标准里是这么描述的), 而在浏览器环境中,提供了一个叫__proto__
的属性去访问它,下面就通过__proto__
去看看原型链是是怎么链起来的
先来看一个空对象的原型是怎样的
1 | var obj = {}; // 一个空对象,等同于 new Object() |
obj.__proto__
是obj对象原型,Object是obj对象的构造函数,
对象的原型是指向其构造函数的prototye属性的指针,所以结果是true
1 | obj.__proto__.__proto__ === null; // true |
Object.prototype
是最基本的原型,但是它仍然是个对象,所以,它还是有原型的,它的原型一个空对象null,js就是这么定义的,这里不必深究
上面这个例子因为构造函数就是Object,所以直接就到Object.prototype,没有表现出原型链的样子,下面进入正题
原型链
通常我们使用的对象并不是使用Object这个构造函数生成的,而是我们自己定义的构造函数,构造函数其实就是函数,而函数其实也是对象,就是前面说的那个不太普通的对象。
函数对象上拥有一个特殊的属性prototype,其实前面已经有写到(Object.prototype),它就是用来定义存放原型的地方
js中,因为没有类的概念,所以是构造函数承担类的功能,去定义对象的属性和方法,去实现继承,下面的代码是三个构造函数继承的关系
Tips继承了Dialog,
Dialog继承了Modal,
Modal并没有说明继承了什么,实际上就是继承了Object,通过这种具有继承关系的构造函数,我们可以清晰的看出原型链的形成。
1 | /** |
tips对象的原型就是其构造函数Tips的属性prototype定义了一个对象,这个对象定义了constructor指向Tips,定义了showTitle方法;
同时通过create方法继承了构造函数Dialog的原型(Dialog.prototype),得到了showButton方法;
Dialog又继承了Modal的原型(Modal.prototype),所以得到open方法;
Modal继承Object的原型(Object.prototype),所以得到下面列一个Object原型上的属性和方法
__defineGetter__
__defineSetter__
hasOwnProperty
__lookupGetter__
__lookupSetter__
propertyIsEnumerable
constructor
toString
toLocaleString
valueOf
isPrototypeOf
__proto__
由于对象的原型上的方法和属性,对象本身可以通过.
语法获取,所以
1 | tips.open(); // width:200 ,height:100 ,content:233333 |
优先级问题
如果在Tips上定义的属性和原型链上的重复?
1 | Tips.prototype.toString = function() { console.log('new toString')} |
原型链底下的会覆盖掉原型链顶上的的属性或方法。这也是很合理的
优先级最高的则是对象本身的的属性,即对象本身的方法或属性会覆盖掉原型上的方法或属性
当一个对象调用一个属性或方法时,js解释器会先在当前对象上扫描是否有该属性,如果没有,才会到这个对象的原型上找,如果,还是没有,就到原型的原型上找,最后,直至Object.prototype,还是没有的话,就返回undefined