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
		

以上代码中的实例对象p_1p_2__proto__属性都指向Person原型对象的prototype属性,并且Person的原型对象的constructor属性指向构造函数本身,即Person

二、原型链

每个对象都有一个__proto__属性,指向它的prototype原型对象,而该prototype原型对象又有它自己的prototype原型对象,依次层层往上查询直到prototype的原型对象为null,则该条查询路径为原型链

如以下代码:

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






















  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值