• JavaScript中的多态


    一、JS的“类”

     
    之所以对js的“类”加上引号是因为他更多是一种思想,就像“面向对象”同样是一种思想一样。没有人很肯定的认为js是面向对象的,但也没人能反驳,因为面向对象的编程思想在js处完全可以实现,但是具体做法又似乎与C#,java等面向对象语言略有不同。这些都不重要,我们只有明白一点即可-----js可以使用面向对象的思想编程。
     
    js的”类“是一个抽象的含义,与C#,Java不同,他不会有类似class的关键词专门强调“类”。js的“类”的声明方式与他自己的函数声明方式相同-----function。
     
    如:function a(){}
    a便是一个js“类”。
     
    类的内部应该有函数才对(C#中叫“方法”): 
    function a(){
    	var v=1;
            this.getValue=function(){
             return v;
        }
    }

     
    getValue是类型a中的一个公共函数。
    在外部可以这样实例化并使用它:
    var b=new a();
    alert(b.getValue());
    弹窗结果为1.

    那既然有个公共函数,那私有函数呢,改造a类:
     function a(){
    	var v=1;
    	setValue();
    	function setValue(){
    		v=9;
    	}
    	this.getValue=function(){
    		return v;
    	}
    }
    var b=new a();
    alert(b.getValue());
    弹窗结果为9.
     
    a类中的setValue便是私有函数,他在外部无法像公共函数getValue那样调用,他只供a类内部调用。
    但是私有函数依然是有用的,像a中的setValue()他改变了局部变量v的值,影响了公共函数getValue()的返回值。
     
    刚刚提到局部变量,示例中的v是一个a内部使用的局部变量,他也是私有的,外界无法直接调用。但是依然可以通过设置公共函数调用(如示例的getValue())。
    这种做法类似于C#中的属性(get{return v;})。
    我们继续改造示例:
     function a(){
    	var v=1; 
    	this.setValue=function (value){
    		v=value;
    	}
    	this.getValue=function(){
    		return v;
    	}
    }
    var b=new a();
    b.setValue(99);
    alert(b.getValue());

    这时弹框为99.
    我们通过公共函数setValue改变了v的值,又通过公共函数getValue获取了v的值。两者结合便是一个完整对私有变量v的属性。
     

    二、JS的“继承”

    面向对象缺少不了继承。js继承的实现方式也是与C#等不同。他有自已的一套实现(或者说“模拟”)方法。

    1.prototype实现继承(原型继承)

    prototype是js中一个特殊之处,每个类型都有一个prototype变量(即便是自定义的类型),他有一个专门的名字叫做“原型”。
     
    之所以说原型特殊,是因为js的一个机制:
    当你实例化一个类型,并试图调用该实例调用一个函数/变量的时候,js会先在该类型本身“查找”指明的函数/变量,若没有,则会在该类型的原型(prototype)上继续查找
    直到prototype的引用出现死循环或prototype为空为止。
     
    借用这个特殊性,我们可以实现js特色的继承,示例:
     
    function a(){ 
    	function getData(){
    		return 100;
    	}
    	this.getValue=function(){
    		return 123;
    	}
            this.value=9999;
    }	
    
    aa.prototype=new a();//类a的实例是aa的原型
    function aa(){
    	
    }
    var b=new aa(); 
    
    alert(b.getValue());//弹框为123
    alert(b.value);//弹框为9999
    //alert(b.getData());//无法继承非公共函数
     
    如上, 类a的实例便是aa的原型。
    类aa中并没有getValue函数,但是aa的实例依然可以调用该函数,这便是js的原型继承。
    但是同时我们发现,对于私有的getData函数,aa类时无法继承的。(这与C#等面向对象编程语言是一致的)
    同样的,这种方式公共变量也可以同时继承(如示例中的value变量)。
     
    原型继承的缺陷:
    1.对一个被继承的原型,若是修改其中的引用类型属性的值,其他所有继承于该原型的类的实例都会受到影响。(有时这是优点,但更多时候是缺陷)
    2.只能继承构造函数不含参的父类。
     
    针对第一个缺陷,示例:
     
    function a(){ 
    	this.value=[100,200];
    }	
    
    aa.prototype=new a();
    function aa(){
    	
    } 
    var b=new aa(); 
    var e=new aa();
    b.value.push(300,400);
    alert(b.value);//弹框100,200,300,400
    alert(e.value);//弹框100,200,300,400 
    	
     
    如上,a类的公共变量value是一个数组(数组是一种引用类型)
    我们只给b对象的value值做了修改,却发现连e对象的value一起改变了。e对象作为aa类的一个新实例,本不该受到影响,却意外出现这种情况,这便是原型继承最重要的缺陷所致。
     
    即便有缺陷,却不影响原型继承作为js的一种最常见继承方式的存在。
     
     

    2.对象冒充式继承(使用call或apply函数实现)

    使用js提供的call()及apply()函数实现继承。这两者的第一个参数都是要实现继承的子类对象(一般传“this”),剩下的参数略有不同:

    call(this, parameter1, parameter2, parameter3...  )

    apply(this, array)
     
    除了this外,剩下的参数将会传递给父类构造函数,供父类使用。示例:
     
    function a(data1,data2){ 
    	this.value=data1+data2;
    }	
    
    function aa(data1,data2){
    	 a.call(this,data1,data2); 
    }   
    function bb(data1,data2){
    	a.apply(this,new Array(data1,data2)); 
    }
    var b=new aa(10,20); 
    var e=new bb(100,200); 
    alert(b.value);//弹框30
    alert(e.value);//弹框300
     
    优点:
    这种继承方式,可以弥补原型继承的一个缺陷:不能继承有参的父类。
    同时,他可以实现多重继承(继承多个父类)。
     
    缺陷:
    若是多重继承,后继承的父类会覆盖继承自其它父类的同名函数/变量。示例:
    function parentA(data1,data2){ 
    	this.value=data1+data2;
    }	
    function parentB(data1,data2){ 
    	this.value=data1*data2;
    }	
    function aa(data1,data2){
    	 parentA.call(this,data1,data2); 
    	 parentB.call(this,data1,data2); 
    }  
    var b=new aa(10,20);  
    alert(b.value);//弹框200 

     
     

    三、JS的封装与多态

    除了继承,封装与多态也是面向对象思想的组成部分。JS的封装、多态也是通过属性的灵活应用“模拟”实现的。
    通过在类中设置公共属性,并在子类中实现,就可以模拟封装。
    而多态的体现,则更为简单,在子类中直接实现同名函数即可覆盖(override)父类函数。JS中没有类似C#中的virtualde 关键字,所有父类函数都可以直接覆盖。
    示例:
    function calc(value1,value2){
    	this.data1=value1;
    	this.data2=value2; 
    	this.GetResult;
    	this.toString=function(){
    		if(this.GetResult)
    			return 	this.GetResult()+"";
    		return "0";
    	}
    }
     
    function sumCalc(value1,value2){
    	calc.call(this,value1,value2)
    	this.GetResult=function(){ 
    		return this.data1+this.data2;
    	}
    }
    function productCalc(value1,value2){
    	calc.call(this,value1,value2)
    	this.GetResult=function(){ 
    		return this.data1*this.data2;
    	}
    }
    var s=new sumCalc(2,3);
    alert(s.toString());  //弹框5
    
    var p=new productCalc(2,3);
    alert(p.toString());  //弹框6
    如上,sumCalc类与productCalc类都继承并实现了calc类,并实现了“抽象函数”GetResult()。这就是JS封装的实现方式。
    另外,JS中的所有类都继承于Object,而Object有自己的toString()函数。所以,上面calc类的toString()函数实际上覆盖了原有的函数----多态的体现。
     

    四、JS中的“委托”

    委托是C#中的术语,类似C++中的函数指针。即将函数赋予一个函数对象,JS中也可以实现类似的功能。
    示例:
    function calc(value1,value2){
    	this.data1=value1;
    	this.data2=value2; 
    	this.GetResult;
    	this.ToString=function(){
    		if(this.GetResult)
    			return 	this.GetResult(this.data1,this.data2)+"";
    		return "0";
    	}
    }
    function GetSum(value1,value2){
    	return value1+value2;
    }
    var c=new calc(2,3);
    c.GetResult=GetSum; 
    alert(c.ToString());  //弹框为5

    如上,GetResult函数被赋予了一个新函数GetSum(),当calc类执行GetResult()函数时,便会调用该委托的函数-----GetSum()。
     
     
     
    
    

    作者:Mr.Jimmy
    出处:https://www.cnblogs.com/JHelius
    联系:yanyangzhihuo@foxmail.com
    如有疑问欢迎讨论,转载请注明出处

  • 相关阅读:
    Java 浮点数精度丢失
    旧梦。
    luogu6584 重拳出击
    luogu1758 [NOI2009]管道取珠
    luogu4298 [CTSC2008]祭祀
    bzoj3569 DZY Loves Chinese II
    AGC006C Rabbit Exercise
    bzoj1115 [POI2009]石子游戏Kam
    luogu5675 [GZOI2017]取石子游戏
    bzoj3143 [HNOI2013]游走
  • 原文地址:https://www.cnblogs.com/JHelius/p/14318912.html
Copyright © 2020-2023  润新知