原型模式

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值