原型模式

来源:互联网 发布:thomson one 数据库 编辑:程序博客网 时间:2024/06/07 20:21
javascript没有提供传统面向对象语言中的类式继承,而是通过原型委托的方式来实现对象与对象之间的继承。javascript也没有在语言层面提供对抽象类和接口的支持。因此我们要了解一些javascript在面向对象方面的知识。

1.1动态类型语言和鸭子类型
编程语言按照数据类型大致分为两类,一类是静态类型语言,另一类是动态类型语言。
静态类型语言在编译时便已经确定变量的类型,而动态类型语言要等到程序运行的时候,待程序运行时 ,待某个变量被赋值后,才具有某种类型。
静态类型语言的优点:编写可靠程序,缺点:增加代码
动态类型语言的优点:代码数量少,专注逻辑表达,缺点:无法保证变量的类型,从而可能在运行期间发生类型错误。
而javascript当中我们对一个变量赋值时,不需要考虑它的类型,因此它是一门典型的动态类型语言。无需进行类型检测,我们可以尝试调用任何对象的任意方法。
鸭子类型的含义:我们只需关注对象的行为,而不关注对象本身,也就是关注has-a,而不是is-a.

    var duck={        duckSinging:function(){            console.log("gagaga");        }    }//duck对象里拥有duckSinging方法    var chicken={        duckSinging:function(){            console.log("gagaga");        }    }//chicken对象里拥有duckSinging方法    var choir=[];//合唱团    //joinChoir传入参数对象    var joinChoir=function(animal){        if(animal && typeof animal.duckSinging=="function"){            choir.push(animal);            console.log("恭喜加入合唱团");            console.log("合唱团已有成员数量:"+choir.length);        }    }    joinChoir(duck);    joinChoir(chicken);
在动态类型语言的面向对象设计中,鸭子概念:对于加入数组的对象,我们无需检查它们的类型,而是保证这些对象拥有duckSinging方法就可以,对象不需要要求类型是什么。因此我们不需要超类,就可以实现面向接口编程。

1.2多态

//不变的地方隔离:所有的动物都会发出叫声    var makeSound=function(animal){        animal.sound();           }    //把可变的部分各自封装起来(多态的实际上是对象的多态性)    var Duck=function(){};    Duck.prototype.sound=function(){        console.log("嘎嘎嘎");    }    var Chicken=function(){};    Chicken.prototype.sound=function(){        console.log("咯咯咯");    }    makeSound(new Duck());    makeSound(new Chicken());    //这样当添加dog对象时候    var Dog=function(){        Dog.prototype.sound=function(){            console.log("汪汪汪");        }    }    makeSound(new Dog()); 

1.2.5 javascript的多态
javascript是一门不必进行类型检查的动态类型语言,多态的思想实际上是把“做什么”和“谁去做”分离开来,要实现这一点,归根到底先要消除类型之间的耦合关系。如果类型之间的耦合关系没有被消除,那么我们在makesound方法指定了发出叫声的对象是某个类型,它就不可能再被替换成另外一个类型。
1.2.7设计模式与多态
在javascript这种将函数作为一等对象的语言中,函数本身也是对象,函数用来封装行为并且能够被四处传递。当我们对一些函数发出“调用”的消息,这些函数会返回不同的执行结果。这是多态的一种体现。
1.3封装
封装的目的是将信息隐藏,一般而言,我们讨论的封装是封装数据和封装实现,封装类型和封装变化。
1.3.1封装数据
在许多语言中,封装数据是由语法解析来实现的,这些语言也许提供了private,public,protected等关键词来提供不同的访问权限。
但javascript并没有提供对这些关键词的支持,我们只能通过依赖变量的作用域来实现封装特性,而且只能模拟出public和private这两种封装性。
call与apply方法都是将函数绑定到另外一个对象上去运行
http://blog.csdn.net/ithomer/article/details/6592082

var value="global var";    function mfunc(){      this.value="member var";    }    function gfunc(){      alert(this.value);    }    window.gfunc();//global var    gfunc.call(window);//global var    gfunc.call(new mfunc());//member var    gfunc.call(document.getElementById('idText'))//show element, input text 
var func=new function(){      this.a="func";    }    var func2=function(x){      var a="func2";      alert(this.a);      alert(x);    }    func2.call(func,"func2");    //call内第一个参数是函数func,调用func2,输出alert(this.a);即func.    //call内第二个参数是参数func2,调用func2,alert(this.a);并没有输出,输出alert(x);即func2.

函数func2调用call方法,this默认指向第一个参数func函数对象,因此this.value为this.a,即func
函数func2调用call方法,第二个参数属于函数对象func2的参数,因此alert(x)为第二个参数func2
http://blog.csdn.net/u014345282/article/details/51519397

1.4原型模式和基于原型继承的javascript对象系统
就javascript的真正实现来说,其实只能说对象的构造器有原型。对于“对象把请求委托给它自己的原型”这句话,更好的说法是对象把请求委托给它的构造器的原型。那么对象如何把请求顺利地转交给它的构造器的原型呢?
javascript给对象提供了一个名为proto的隐藏属性,某个对象的proto属性默认会指向它的构造器的原型对象,即{Constructor}.prototype

var a= new Object();console.log(a._proto_===Object.prototype);//输出true
function Person(name){  this.name=name;}Person.prototype.getName=function(){  return this.name;}var objectFactory=function(){  var obj=new Object();//从Object.prototype上克隆一个空的对象  Construtor=[].shift.call(arguments);//取得外部传入的构造器  obj._proto_=Construtor.prototype;//指向正确的原型  var ret=Construtor.apply(obj,arguments);//借用外部传入的构造器给obj设置属性  return typeof ret==='object'?ret:obj;//确保构造器总会返回一个对象  var a=objectFactory(Person,'seven');  console.log(a.name);  console.log(a.getName());  console.log(Object.getPrototypeof(a)===Person,prototype);//输出true}   

我们用objectFactory函数用来模拟new创建对象时,需要手动给obj对象设置正确的proto指向

obj._proto_=Constructor.prototype;

通过这段代码,我们让obj.proto指向Person.prototype,而不是原来的object.prototype

2.如果对象无法响应某个请求,它会把这个请求委托给它的构造器原型
对于一个对象无法响应某个请求的时候,它会顺着原型链把请求传递下去,直到遇到一个可以处理该请求的对象为止。

实际上,虽然javascript的对象最初都是由Object.prototype对象克隆而来的,而对象构造器的原型并不仅限于Object.prototype上,而是可以动态指向其他对象。这样一来,当对象a需要借用对象b的能力时,可以有选择性地把对象a的构造器的原型指向对象b,从而达到继承的效果。
下面代码是我们最常用的原型继承的方式:

var obj={name:"seven"};var A=function(){};A.prototype=obj;var a=new A();console.log(a.name);//输出seven

这段代码中:
首先,尝试遍历对象a中的所有属性,但是没有找到name属性。
查找name属性的这个请求被委托给对象a的构造器的原型,它被a.proto记录着并且指向A.prototype,而A.prototype被设置为对象obj.
在对象obj中找到了name属性,并返回了它的值。

当我们期待一个类继承另一个类时,往往会用以下代码:

var A=function(){};A.prototype={name:"seven"};var B=function(){};B.prototype=new A();var b= new B();console.log(b.name);

首先,尝试遍历对象b中的所有属性,但是没有找到name属性。查找name属性的请求被委托给对象b的构造器原型,它被b.proto记录着并且指向着B.prototype,而B.prototype被设置为一个通过new A()创建出来的对象。
在这个对象中依然没有找到name属性,于是请求被继续委托给这个对象构造器的原型A.prototype
在A.prototype中找到name属性,并返回它的值。

如果查找address属性,但是原型链上没有别的节点了。于是请求就被打住,a.address返回undefind.

原创粉丝点击