JavaScript中原型(prototype)、原型链、原型继承的理解
一、原型 prototype 和 _proto_
每个对象都有他的_proto_属性,并且指向他的prototype原型对象
每个构造函数都有一个prototype原型对象
每个prototype原型对象里面的constructor属性都指向构造函数本身
实例对象的_prototype_指向构造函数的prototype属性,从而可以实现继承,而此时prototype原型对象相当于特定
类型的所有实例对象可以访问的容器。
代码如下:
function Person(name,age,sex){//定义Person原型对象
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.sayName = function(){//在Person原型上定义一个方法
console.log(this.name);
};
var p_1 = new Person('jack',20,'man');
var p_2 = new Person('jian',21,'woman');
console.log(p_1.name);//jack
console.log(p_1.age);//20
console.log(p_1.sex);//man
p_1.sayName();//jack
console.log(p_2.name);//jian
console.log(p_2.age);//21
console.log(p_2.sex);//woman
p_2.sayName();//jian
console.log(p_1.__proto__ === Person.prototype);//true
console.log(p_2.__proto__ === Person.prototype);//true
console.log(p_1.__proto__ === p_2.__proto__);//true
console.log(Person.prototype.constructor === Person);//true
function Person(name,age,sex){//定义Person原型对象 this.name = name; this.age = age; this.sex = sex; } Person.prototype.sayName = function(){//在Person原型上定义一个方法 console.log(this.name); }; var p_1 = new Person('jack',20,'man'); var p_2 = new Person('jian',21,'woman'); console.log(p_1.name);//jack console.log(p_1.age);//20 console.log(p_1.sex);//man p_1.sayName();//jack console.log(p_2.name);//jian console.log(p_2.age);//21 console.log(p_2.sex);//woman p_2.sayName();//jian console.log(p_1.__proto__ === Person.prototype);//true console.log(p_2.__proto__ === Person.prototype);//true console.log(p_1.__proto__ === p_2.__proto__);//true console.log(Person.prototype.constructor === Person);//true以上代码中的实例对象p_1和p_2的__proto__属性都指向Person原型对象的prototype属性,并且Person的原型对象的constructor属性指向构造函数本身,即Person。
二、原型链
每个对象都有一个__proto__属性,指向它的prototype原型对象,而该prototype原型对象又有它自己的prototype原型对象,依次层层往上查询直到prototype的原型对象为null,则该条查询路径为原型链。
如以下代码:
[javascript] view plain copy print?
var arr = [1,2,3];
console.log(arr.valueOf());//Array(3) [ 1, 2, 3]
var arr = [1,2,3]; console.log(arr.valueOf());//Array(3) [ 1, 2, 3]当使用console.log()方法时,控制台输出 Array(4)[1,2,3]。
然而arr中并没有定义任何方法来显示arr中的值。此时原型链起到了作用。也就是说arr数组中没有显示值的方法,那么它会依着原型链寻找它的prototype原型对象里面的属性和方法,在这里它的上一级原型对象为Array,它的类型为Object类。然而在Array.prototype里面,即arr.__proto__上并没有valueOf()方法,参考下图:
于是继续往上查询,即在Array.prototype.__proto__上查找到valuOf()方法。如下图:
所以查找valveOf()方法的过程如下:
当试图访问一个属性或者方法时,它不仅在该对象上查找,也会查找该对象的原型,以及在该对象的原型的原型上查找,
依次层层向上查找,知道找到一个值匹配的属性或方法,否则直到原型链末端(null)。
1.当前实例对象obj,查找obj的属性和方法,找到后返回,否则进行下一步;
2.通过obj__proto__,查找obj构造函数的prototype对象上的属性和方法,找到后返回,否则进行下一步;
3.把Array.prototype当做obj,重复以上步骤;
3. 当查找到Object.prototype时,Object.prototype.__proto__ === null,查找结束。
上述代码的原型对象关系有如下:
var arr = [1,2,3]; console.log(arr.valueOf());//Array[1,2,3] console.log(arr.__proto__ === Array.prototype);//ture console.log(Array.prototype.__proto__ === Object.prototype);//true console.log(arr.__proto__.__proto__ === Object.prototype);//true console.log(Object.prototype.__proto__ === null);//true 原型链的终点
原型链:
arr --->Array.prototype ---> Object.prototype --->null
三、原型继承
3.1什么是继承?
继承是指一个对象使用另一个对象的属性和方法。
得到一个对象的属性
得到一个对象的方法
3.2 属性继承
创建一个Person类,
function Person(name,age){ this.name = name ; this.age = age; } Person.prototype.youAge = function(){// 在Person原型上定义youAge()方法 console.log(this.age); };
创建一个Manager类,它可以继承Person所有的属性,并且额外添加属于自己特定的属性;
一个新的属性,bonus——这个属性包含了经理的奖金。
function Manager(bonus){//创建子类Manager this.bonus = bonus;//添加自己的属性 }
当我想在Manager类中可以使用Person类的name和age属性时,通过call或apply方法指定this为当前的执行环境,
这样就可以得到Person类的所以属性。
代码如下:
function Manager(name,age,bonus){//创建子类Manager Person.call(this,name,age);//使用call方法实现子类继承父类的属性 //Person.apply(this,['name','age']);//使用apply方法,第二个参数为一个数组 this.bonus = bonus;//添加自己的属性 }
实例化一个对象,测试子类是否继承父类的属性。
代码如下:
var m = new Manager('jack',20,20000); console.log(m.name);//jack console.log(m.age);//20 console.log(m.bonus);//20000
3.3方法继承
当Manager类想要继承Person类的方法时,我们可以先创建一个对象,然后将该对象赋值给Manager.prototype,
Maneger.prototype = Object.create(Person.prototype);
为什么不直接将Person.prototype直接赋值给Manager.prototype呢?因为如果执行这个操作,那么当Manager.prototype改变时,Person.prototype也会随之改变,这是我们所不愿意看到的。
另外,在给Manager
类添加方法时,应该在修改prototype
以后,否则会被覆盖掉,原因是赋值前后的属性值是不同的对象。
最后还有一个问题,我们都知道prototype
里有个属性constructor
指向构造函数本身,但是因为我们是复制其他类的prototype
,所以这个指向是不对的,需要更正一下,将constructor指向它本身。如果不修改,会导致我们类型判断出错。
代码如下:
function Person(name,age){ this.name = name ; this.age = age; } Person.prototype.youAge = function(){//在Person原型上定义youAge()方法 console.log('My age is '+this.age); }; function Manager(bonus){ this.bonus = bonus; }; Manager.prototype =Object.create(Person.prototype); //继承Person类的方法 Manager.prototype.constructor = {constructor:Manager};//将构造器的constructor指向它本身 function Manager(name,age,bonus){//创建子类Manager Person.call(this,name,age);//使用call方法实现子类继承父类的属性 //Person.apply(this,['name','age']);//使用apply方法,第二个参数为一个数组 this.bonus = bonus;//添加自己的属性 } var m = new Manager('jack',20,20000); console.log(m.name);//jack console.log(m.age);//20 console.log(m.bonus);//20000 m.youAge(); //My age is 20
以上代码显示,子类Maneger继承了youAge()方法。
3.4 hasOwnProperty
在原型链上查询属性比较耗时,对性能有影响,试图访问不存在的属性时会遍历整个原型链。遍历对象属性时,每个可枚举的属性都会被枚举出来。 要检查是否具有自己定义的属性,而不是原型链上的属性,必须使用hasOwnProperty方法。hasOwnProperty 是 JavaScript 中唯一处理属性并且不会遍历原型链的方法。
共同学习,写下你的评论
评论加载中...
作者其他优质文章