Js 原型对象与原型链
Posted 小小鱼爱吃鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Js 原型对象与原型链相关的知识,希望对你有一定的参考价值。
原型对象
每个javascript对象都有一个原型对象,这个对象在不同的解释器下的实现不同。比如在firefox下,
每个对象都有一个隐藏的__proto__属性,这个属性就是“原型对象”的引用。
原型链
由于原型对象本身也是对象,根据上边的定义,它也有自己的原型,而它自己的原型对象又可以有自
己的原型,这样就组成了一条链,这个就是原型链,JavaScritp引擎在访问对象的属性时,如果在对象本
身中没有找到,则会去原型链中查找,如果找到,直接返回值,如果整个链都遍历且没有找到属性,则返
回undefined.原型链一般实现为一个链表,这样就可以按照一定的顺序来查找。
我们先用一个构造器来实现一个构造函数:
function A(){ this.mark = "A"; this.changeMark = function(){ this.mark += "_changed"; } } A.prototype.mark2 = "A2"; A.prototype.changeMark2 = function(){ this.mark2 += "_changed"; } var a = new A(); var a2 = new A(); //下面则说明构造函数实例化后,分配着不同的实例对象,互不相关 console.log(a.mark); //"A" console.log(a2.mark); //"A" a.changeMark(); //使用实例对象中的方法 console.log(a.mark); //"A_changed" console.log(a2.mark); //"A" //下面则说明了new操作符的一项作用,即将原型中的this指向当前对象, //在a.changeMark2执行时,changMark2中的方法先找 this.mark2 的值, //但是实例对象this中没有mark2值,则在原型链向上寻找,得到A原型对象中的mark2值, //在赋值时,将修改后的值添加在了a实例中。 //总:虽然调用的是prototype方法,但是不会对prototype属性做修改,只会说是在实例中新增属性,但是在使用时,会最使用最近得到的属性(在后面原型链中可以加以理解) console.log(a.mark2); //"A2" console.log(a2.mark2); //"A2" a.changeMark2(); //使用原型链中的方法 console.log(a.mark2); //"A2_changed" console.log(a2.mark2); //"A2"
为什么a可以使原型中的changeMark2方法?这就和js巧妙的原型链相关,在Firefox中我们可以打印出对象并可查看到对象下面的__proto__。
我们把上面的过程用流程图来表示:
只有构造函数才会有prototype属性,而实例化出来的对象会拥有__proto__,而不会有prototype。
像上图画的那样,两个实例化的对象都通过__proto__属性指向了A.prototype(即构造函数的原型对象)
而原型对象的__proto__指向Object对象,就像a.toString()的toString方法就是存在于Object原型对象(Object.prototype)中。
so:当使用对象的方法或属性时,对象会在一步一步通过__proto__向上寻找,找到最近的则是最终的获取到的方法或属性。
————这就是js中的原型链。
就像图上看到的一样,所有对象的原型链最终都指向了Object对象,而Object的原型对象(Object.prototype)是为数不多的不继承自任何属性的对象,即Object.prototype没有__proto__,是原型链的顶峰。
通过上面我们可以了解到,当我们对A.prototype或Object.prototype添加属性或方法时,在a和a2实例中都会查看到该属性或方法,因为这两个实例都通过原型链与A和Object的原型对象相连。
再来看看原型对象和原型链在继承方面的实现:
再构造一个函数A和一个函数B,并让B继承A,如下:
function A(mark){ this.mark = mark; } A.prototype.getMark = function(){ return this.mark; } function B(mark){ this.mark = mark } //var temp = new A("A"); //B.prototype = temp; //上面语句和下语句作用相同 B.prototype = new A("A"); //实例化一个A,其赋值于B.prototype var b = new B("B"); console.log(b.mark); //B, 结果如上面原型链分析的那样,向上找到最近的属性,则为b实例中的mark:"B"
其中的结构示意大概如下图:
这时我们可以看到,在B.prototype中是没有constructor的,因为B.prototype只是简单的new A("A")对象赋值的结果。
在js中的constructor有什么作用呢?如:
var arr = new Array(); arr instanceof Array; //true arr.constructor === Array; //true function TEMP(){ } var temp = new TEMP(); temp instanceof TEMP; //true temp.constructor === TEMP; //true
用《JavaScript权威指南》中的对于constructor的解释为:对象通常继承的constructor均指代它们的构造函数,而构造函数是类的“公共标识”。即constructor可用来判断对象所属的类。
在上面的小例子中,用instanceof也可判断对象的类,但是有自身的缺陷,instanceof的实现方法为:
instanceof不会去检查temp是不是由TEMP()构造函数初始化的,面是判断temp是否继承自TEMP.prototype,这样,范围就宽了很多。
如在上面的大例中,使用
b instaceof B //true 因为在b的原型链中可以找到B.prototype对象 b instaceof A //true 在b的原型链中也可以找到A.prototype对象
可以说instanceof是用来检测继承关系的。而当
console.log(b.constructor) //function A() //因为在b的原型链中,最近的constructor就是A.prototype中有constructor指向了构造函数A();
但我们知道的b是属于B类的,那最后所以要做的就是:
B.prototype.constructor = B; //将constructor指向自身的构造函数 var new_b = new B("B"); console.log(new_b.constructor) //function B()
以上是关于Js 原型对象与原型链的主要内容,如果未能解决你的问题,请参考以下文章