原型链作为js语言中的重点、难点内容,并且也是面试官常问的一个内容,理解原型链并掌握它对我们以后找工作时大有裨益,那么下面让我们来了解原型链这个内容。
原型链的概念
每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,实例包含一个指向原型对象的内部指针,如果我们让原型对象等于另一个类型的实例,则此时的原型对象包含指向另一个类型的指针,如此层层递进构成原型链。
相当于:
构造函数使用new操作符创建实例对象,构造函数里面有个prototype属性指向原型对象,实例对象里面又有一个_ proto _属性指向原型对象,当然原型对象里面有constructor属性指向构造函数,层层递进。
好了,如果有人还不理解这个概念的话,我们用一张图来说明这个问题:
原型链的搜索属性过程
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue =function (){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承SuperType
SubType.prototype =new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
console.log(instance.getSuperValue());
当然在浏览器的控制台结果为true,这究竟为何?
一组图片来解释下其中的缘由:
其中查找的流程一般如下所示:
在当前实例对象obj中,查找obj的属性或方法,找到后返回
没有找到,通过obj. _ proto _,找到obj构造函数的prototype并且查找上面的属性和方法,找到后返回,没有找到,把Array.prototype当做obj,重复以上步骤
原型链是有终点的,最后查找到Object.prototype时,Object.prototype._ proto _ === null,意味着查找结束。
hasOwnProperty:
hasOwnProperty()方法可以检测一个属性存在于实例中还是原型中,这个方法只在给定属性存在于对象实例中才返回true;
在原型链上查询属性是比较耗时,对性能又有影响,试图访问不存在的属性时会遍历整个原型链,造成了不必要的资源浪费。遍历对象属性时,每个可枚举的属性都会被枚举出来。 要检查是否具有自己定义的属性,而不是原型链上的属性,必须使用hasOwnProperty方法。
function Person(){
}
Person.prototype.name = "Tom";
Person.prototype.age = 19;
Person.prototype.job = "Teacher";
var person1 = new Person();
person1.name = "Jack";
console.log(person1.hasOwnProperty("name"));
console.log(person1.hasOwnProperty("age"));
注:hasOwnProperty 是 JavaScript 中唯一处理属性并且不会遍历原型链的方法。
原型链的继承
js主要是通过原型链来实现继承的,继承的方式多种多样,但就其实用性而言,我们用的最多的还是组合使用构造函数和原型模式。
即:构造函数模式用于定义实例化属性,原型模式用于定义方法和共享的属性
function Person(name,age){
this.name = name;
this.age =age;
this.friend = ["213", "456"];
}
Person.prototype = {
constructor : Person,
sayName : function (){
console.log(this.name);
}
}
var person1 = new Person("yf", 20);
var person2 = new Person("lrt", 19);
person1.friend.push("789");
console.log(person1.friend);
console.log(person2.friend);
在这份代码里面,充分的体现了这种方式继承的好处。还有里面的person2.friend之所以没有改变,是因为person1,person2保存的都是friend的副本,所以person2.friend的值是没有改变的。
Tips
1、原型对象修改(通过构造函数方式来修改)的变动都可以从实例上反映出来。
2、重写原型对象,会切断构造函数和最初原型之间的联系,实例指针仅指向原型函数,而不指向构造函数。
3、省略了构造函数传递初始化参数,导致所有实例在默认情况下都取得相同的属性值。
4、实例化的对象无法修改原型的固有属性,引用类型可以。
5、子类型有时需要覆盖超类型中的某个方法,给原型添加方法的代码一定要放在替换原型的语句之后
6、不能在原型链实现继承后,使用对象字面量的方法来添加新方法,否则将会导致 继承失败。
好了,以上就是自己对原型链的一些理解,如果小伙伴们有不同的想法,欢迎来一起讨论,一起进步。