继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

JavaScript中原型(prototype)、原型链、原型继承的理解

慕仔4209126
关注TA
已关注
手记 304
粉丝 69
获赞 305

一、原型 prototype 和 _proto_


  • 每个对象都有他的_proto_属性,并且指向他的prototype原型对象

  • 每个构造函数都有一个prototype原型对象

  • 每个prototype原型对象里面的constructor属性都指向构造函数本身

实例对象的_prototype_指向构造函数的prototype属性,从而可以实现继承,而此时prototype原型对象相当于特定

类型的所有实例对象可以访问的容器。

代码如下:

  1. function Person(name,age,sex){//定义Person原型对象  

  2.     this.name = name;  

  3.     this.age = age;  

  4.     this.sex = sex;   

  5. }  

  6. Person.prototype.sayName = function(){//在Person原型上定义一个方法  

  7.     console.log(this.name);  

  8. };  

  9. var p_1 = new Person('jack',20,'man');  

  10. var p_2 = new Person('jian',21,'woman');  

  11.   

  12. console.log(p_1.name);//jack  

  13. console.log(p_1.age);//20  

  14. console.log(p_1.sex);//man  

  15. p_1.sayName();//jack  

  16.   

  17. console.log(p_2.name);//jian  

  18. console.log(p_2.age);//21  

  19. console.log(p_2.sex);//woman  

  20. p_2.sayName();//jian   

  21.   

  22. console.log(p_1.__proto__ === Person.prototype);//true  

  23. console.log(p_2.__proto__ === Person.prototype);//true        

  24. console.log(p_1.__proto__ === p_2.__proto__);//true  

  25. 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?

  1. var arr = [1,2,3];  

  2. 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 中唯一处理属性并且不会遍历原型链的方法。

原文出处

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP