您好,欢迎访问一九零五行业门户网

JS中的原型链详解

js虽然不是面向对象类型的语言,但这不并不意味着js就不能够实现oop的特性。 我相信大家在使用js的时候,一定用过object的原型方法,比如call,apply,hasownproperty等等方法,可是这些方法是从哪里来的呢?如果js无法实现继承的话,这些方法的使用就无从谈起了。这里我们就来谈谈在js中实现继承的方法,原型链。
_proto_和prototype首先我们要了解什么是普通对象,什么是函数对象。
普通对象
var a = {}
var a = new object();
var a = new f1();//与上一个创建对象的方式相同
函数对象
var a = function(){};
var a = new function(){};
f1()
_proto_是每个普通对象都拥有的属性,用来指向构造函数的prototype,也就是构造函数的原型对象。而构造函数的原型对象一般来说也是一个普通对象(在构造函数为function的时候,它就变成了一个函数对象),所以它也有_proto_属性。而它的_proto_则指向它的构造函数的原型对象,也就是object.prototype。最后的object.prototype._proto_指向null,到了原型链的顶端。
prototype是函数对象都拥有的属性,它在对象创建的时候被指定给新的对象实例。当然也可以动态修改。
function person(){}; var p = new person();//创建一个普通对象 //创建过程实际为 var p={}; p._proto_=person.prototype; person.apply(p,arguments);//或者是call... //执行构造函数,并返回创建的对象。
对上面代码的补充说明
正常来讲构造函数中是不用写return语句的,因为它会默认返回新创建的对象。但是,如果在构造函数中写了return语句,如果return的是一个对象,那么函数就会覆盖掉新创建的对象,而返回此对象;如果return的是基本类型如字符串、数字、布尔值等,那么函数会忽略掉return语句,还是返回新创建的对象。
而构造函数的原型对象的默认值为:
person.prototype={ constructor://指向构造函数本身 _proto_://指向构造函数person的原型对象的构造函数的原型对象,这里是指object.prototype } //这里有一个特殊情况——当构造函数为function的时候 function.prototype._proto_===object.prototype //我们知道function.prototype是一个函数对象,它的_proto_应该指向它的构造函数的原型,也就是function.prototype。 //可是这样下去就没完没了了,毕竟一条链总是有顶端的。这里约定function.prototype._proto_===object.prototype; //这时,object.prototype._proto_===null;完美结束原型链。
我们可以不断修改构造函数的原型对象的指向,这样最终就可以形成一条链。而上面提到的一条链就是js中的默认原型链。
谈谈代码实现下面我们看看代码:
function parent(name){ this.name=name||"parent"; } function son(name){ this.name=name||"son"; this.property="initial son name"; } function grandson(name){ this.name=name||"grandson"; this.ggs="initial grandson name"; } son.prototype = new parent("原型中的parent"); grandson.prototype = new son("原型中的son"); let grandson = new grandson("孙子"); console.log(grandson instanceof son);//true console.log(grandson instanceof grandson);//true console.log(grandson instanceof parent);//true
很显然,最后都输出true。但是我们改动一点代码:
grandson.prototype = new son("原型中的son"); son.prototype = new parent("原型中的parent");//其实上一步已经实例化了一个son的对象给grandson.prototype //这个时候son的实例的_proto_已经确定指向那个时候的构造函数.prototype了(默认原型对象) let grandson = new grandson("孙子"); console.log(grandson instanceof son);//false console.log(grandson instanceof grandson);//true console.log(grandson instanceof parent);//false
为什么结果会变呢?原因也很简单。我们之前有提到对象的创建的创建过程:对象在实例化的时候就已经给对象的_proto_赋了构造函数的prototype了。也就是说上面代码中第一行已经确定了grandson.prototype._proto_的值了,即使在第二行修改了son.prototype也是无法修改grandson.prototype._proto_的值。
conclusion:js中原型链的关系是由_proto_维持的,而不是prototype。
小测试var animal = function(){}; var dog = function(){}; animal.price = 2000; dog.prototype = animal; var tidy = new dog(); console.log(dog.price) console.log(tidy.price)
答案是输出什么呢?是undefined和2000,我们分析一下:
首先我们清楚animal和dog都是函数对象,在第四行修改了dog的原型对象为animal。那么我们接着往下看,console.log(dog.price) 这一句首先会寻找dog的price,没有。然后去原型链上寻找。怎么找的呢?我们之前提到是通过_proto_去到它构造函数的原型对象上,这里因为dog是函数对象,那么它的构造函数的原型对象就是function.prototype,这是一个empty function。于是返回undefined,没有找到price这个属性。
那么console.log(tidy.price) 呢?
tidy是一个普通对象,首先也是寻找它本身的属性price,也没有。通过_proto_去到它构造函数的原型对象上,也就是dog.prototype。因为tidy实例化在dog.prototype = animal; 之后,所以tidy._proto_的指向已经指向了修改后的dog.prototype。也就是指向了animal,也就是能够找到price这个属性了,所以输出2000。
原型对象上的所有属性和方法都可以看成是java中父类的public(protected)属性和方法,在这些方法内部使用this即可访问构造函数中的属性和方法。至于为什么,这又得提到js中this的绑定问题了….总而言之,谁调用的函数,this就指向谁。箭头函数除外…
  相关推荐:详解js原型和原型链(一)
详解js原型和原型链(二)
详解js原型和原型链(三)
以上就是js中的原型链详解的详细内容。
其它类似信息

推荐信息