关于原型,原型链和继承的讨论

写博客也有不短的时间了,经常做一些自己的总结和技术的分享。但是之前的由于之前的备用号码丢失,也正好准备换个备用卡,所以直接注销了,没有找回。所以就开了个新的博客。以后,我会经常在这里分享一下心得,和和大家做一些技术探讨。今天就原型链做一个总结。

在刚接触到js原型和原型链的时候,可能很多人都会有很多的困惑和疑问。这是很正常,因为当你有了这种状态的时候,说明你已经到了王国维先生说的学习的三重境界中的第一重了,昨夜西风凋碧树,独上高楼,望断天涯路。就是说你已经有了想要学的更透彻,追寻运行原理的想法了。是所谓独上高楼,望断天涯路。一定是有着穷其理的好奇心与干劲。

一,基本释义
所有类型(包括基本类型与函数,数组,对象)都拥有__proto__属性(隐式原型)

            所有函数拥有prototype属性(显式原型)最底层的Object也有prototype属性。这也验证了万物皆对象,所有的类型都是从它中来。
            
		函数的prototype是个指针,它指向自己的原型对象,
 function Ball(words){	
        this.words = words;
    }
    Ball.prototype = {
    	a:10,
       play:function(){
       console.log(  this.a);
		}
    }
	var ball=new Ball();
    这个 Ball.prototype就是构造函数function Ball的原型对象;
	通过这个构造函数new 出来的新的对象ball中就有__proto__这个属性,并且__proto__指向这个构造函数function Ball的原型对象 Ball.prototype

二,构造函数实例解析

    //创建实例
  	 function Ball(words){	
        this.words = words;//当前this 指向window,new出新的ball对象后,指向ball(在面向对象中就用这里为原型对象传参数)
    }
    Ball.prototype = {
    	a:10,
       play:function(){
   console.log(  this.a);//这里this指向原型对象 Ball.prototype,在new 出新的实例后, this指向实例即	ball;
		}
    }
	var ball=new Ball();
	ball.play();//   a=10;
           play()方法是ball实例本身具有的方法,所以ball.play()打印a=10;ball.play()不属于ball实例的方法,属于构造函数的方法,因为实例继承构造函数的方法。

            实例w的隐式原型指向它构造函数的显式原型,指向的意思是恒等于
     ball.__proto__ === Ball.prototype
            当调用某种方法或查找某种属性时,首先会在自身调用和查找,如果自身并没有该属性或方法,则会去它的__proto__属性中调用查找,也就是它构造函数的prototype中调用查找。所以很好理解实例继承构造函数的方法和属性:

            ball本身没有play()方法,所以会去Ball()的显式原型中调用play(),即实例继承构造函数的方法。         

三,原型和原型链
Object.prototype.play= “play”;
function Ball(){}
console.log(Ball); //function Ball()
let ball= new Ball();
console.log(ball); //Ball{} 对象
console.log(ball.play); //play

            解析:

            ball是Ball()的实例,是一个Ball对象,它拥有一个属性值__proto__,并且__proto__是一个对象,包含两个属性值constructor和__proto__

    console.log(ball.__proto__.constructor);   //function Ball(){}
    console.log(ball.__proto__.__proto__);     //对象{},拥有很多属性值
            我们会发现ball.__proto__.constructor返回的结果为构造函数本身,ball.__proto__.__proto__有很多参数
            原型链总结:
                      1.查找属性,如果本身没有,则会去__proto__中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有__proto__,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined

            2.ball.__proto__.constructor  == function Ball(){}

            3.ball.___proto__.__proto__== Object.prototype

            4.ball.___proto__.__proto__.__proto__== Object.prototype.__proto__ == null          

            5.通过__proto__形成原型链而非protrotype
		console.log(ball.__proto__.__proto__.__proto__);   //null
    	console.log(Object.prototype.__proto__);        //null
          通过上述我们发现prototype下面是有constructor和__proto__的,也就是说构造函数也是有__proto__指向的,它就指向Object.prototype,而且Object.prototype下也有__proto__指向null;

所以利用这个原理,我们就能完成继承。在继承的类中,例如下列;

//原来的的类
	 function Ball(words){	
            this.words = words;//当前this 指向window,new出新的ball对象后,指向ball(在面向对象中就用这里为原型对象传参数)
        }
        Ball.prototype = {
        	a:10,
           play:function(){
     		 this.a===10;//这里this指向原型对象 Ball.prototype,在new 出新的实例后, this指向实例即	ball;
			}
        };
    
//写一个继承函数
//subClass想要继承的新类,supClass是原来的类
	        function extend(subClass,supClass) {
//         创建一个临时类
         function F() {}
//         设置这个临时的类的原型是父类的原型
         F.prototype=supClass.prototype;
//         子类的原型是实例化的这个临时类
         subClass.prototype=new F();
//         设置子类原型中该子类的指针指向自己这个构造函数
         subClass.prototype.constructor=subClass;
//        设置子类的属性superClass是父类的原型
         subClass.superClass=supClass.prototype;
//         如果父类原型中指针没有指向父类的构造函数,仍然指向Object的指针时,
//         重设父类原型中指针指向父类的构造函数
         if(supClass.prototype.constructor===Object.prototype.constructor){
             supClass.prototype.constructor=supClass;
         }
     }
     
 //继承得到的新类

     function Box() {
//         使用冒充将父类的对象属性加入到子类的对象属性中
         Ball.call(this);
     }
     extend(Box,Ball);
//     console.log(Box);
        Box.prototype.play=function () {
//            Box.superClass.play.call(this);//类似es6中super作用的语句
            console.log(this.a);
        };
         var box=new Box();
        box.play();	//打印的结果为1;(如果上面的super语句不注解的话那么结果就为10;)
     这就是es5当中我们常用的继承方法,是基于原型和原型链基础之上来的。可以看到Box.prototype=new new F();   而  F.prototype=supClass.prototype;其中是通过new改变了Box中的__proto__的指向,让其指向supClass.prototype;所以我们在新类中可以用到原来类的属性和方法,也可以对Box.prototype.play进行原来类下某个方法和属性进行覆盖。(原理是对象属性和方法的调用顺序先找自身,再去原型链上找)但是注意不能对Box.prototype.__proto__进行修改,因为Box.prototype.__proto__指向Ball.prototype。

猜你喜欢

转载自blog.csdn.net/weixin_43665032/article/details/83958624