初涉JavaScript模式 (5) : 原型模式 【一】

什么是原型模式?

原型模式(prototype)是指用原型实例指向建立对象的种类,而且经过拷贝这些原型建立新的对象。--引自JavaScript设计模式javascript

咱们建立的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象包含了全部由指向他的构造函数所生成的实例的共享属性和方法。说的通俗点,就是一个对象包含了一些属性和方法,而全部prototype为这个对象的构造函数所建立的实例均可以共享这个对象的属性和方法。直接上代码:java

```javascript
           function Animal(type){
               this.type = type;
           }
           Animal.prototype.sayType = function(){
               console.log("我是一只"+this.type);
           };
           var tom = new Animal("Cat");
           var jerry = new Animal("Mouse");
           tom.sayType();   // "我是一只Cat"
          jerry.sayType(); // "我是一只Mouse"
          console.log(tom.sayType === jerry.sayType); //true
```

以上代码说明了每一个实例都拥有了原型上的方法,这也就是Javascript基于原型的继承设计模式

我在上一篇中提过,构造函数模式一个缺点是每一个实例的方法都是一个新的Function实例(虽然能够解决,可是也让咱们的代码丝毫没有封装性,详细请看上一篇),而原型模式正好解决了咱们的问题,并且原型模式更符合咱们封装的需求。markdown

理解原型对象

不管何时,只有咱们建立了一个新的函数,JS就会给这个新函数建立一个prototype属性,该属性指向这个新函数的原型对象。在默认状况下,全部的原型对象都会自动得到一个constructor属性,该属性包含一个指向这个函数自己的指针,上代码:ide

```javascript
        function Animal(type){
            this.type = type;
        }
        console.log(Animal.prototype.constructor == Animal); //true
```

咱们要注意的是prototype只会取得constructor属性,至于其余方法和属性则都是从Object继承而来的,示例以下:函数

```javascript
           function Animal(type){
               this.type = type;
           }
           var obj = new Object();
           console.dir(Animal.prototype); 
           console.dir(obj);
```

运行结果以下图所示:this

而一样的咱们能够给prototype添加其余自定义的方法和属性(原型模式的共享),当调用构造函数建立一个新实例后,该实例内部将包含一个指针,指向构造函数的原型对象。ECMA-262第五版管这个指针叫作[[prototype]],须要注意的在JS中是没有标准的方式访问[[prototype]],但Firefox,Safari,Chrome在每一个对象上都支持一个属性__proto__,该属性指向建立这个实例的构造方法的原型对象,须要注意的是即使咱们在之后改变了prototype,也不会改变这个属性。(详细请看上一篇)。在其余的实现中,这个属性对脚本是彻底不可见的。prototype

虽然咱们没有标准的方式去访问到实例的[[prototype]],但能够经过isPrototypeOf()方法来肯定实例直接是否存在这种关系。从本质上讲,若是[[prototype]]指向调用isPrototypeOf()的实例,那么这个方法就会返回true,代码以下:设计

```javascript
           function Animal(type){
               this.type = type;
           }
           var tom = new Animal("Cat");
           Animal.prototype = {};
           var jerry = new Animal("Mouse");
           console.log(Animal.prototype.isPrototypeOf(tom)); //false
           console.log(Animal.prototype.isPrototypeOf(jerry)); //true
```

以上代码说明,isPrototypeOf()可以正确的检测出实例的[[prototype]]指向。指针

ECMAScript5 增长了一个新的方法Object.getPrototypeOf(),在全部支持的实现中,这个方法返回[[prototype]]的值。(只支持IE9+,FF3.5+,Safari5+,Opera12+,Chrome)

JavaScript对象属性查找

每当咱们读取一个对象的属性时,都会执行一次搜索,他会如今对象自己查看有木有符合给定名字的属性,若是没有他会去找该对象的原型对象,一直找下去,直到找到为止,若是到了Object.prototype还没找到则会返回undefined,代码以下:

```javascript
           function Man(name){
               this.name = name;
               this.sayName = function(){
                   console.log(this.name);
               };
           }
           function Person(){
               this.type = "人类"
               this.sayType = function(){
                  console.log("我是人");
              };
          }
          function X(){
              this.say = function(){
                  console.log("XXXXX");
              }
          }
          Object.prototype.sayHello = function(){
              console.log("Hello Moto!");
          }
          Person.prototype = new X();
          Man.prototype = new Person();
          var wr = new Man("WeiRan");
          wr.sayName(); //WeiRan
          wr.sayType(); //我是人
          wr.say();      //XXXXX
          wr.sayHello();//Hello Moto
          wr.sayLast(); //Uncaught TypeError: Object #<X> has no method 'sayLast' 
          console.log(wr.xxx); //undefined
```

而咱们新建另一个实例,也会依次执行这样的步骤,而这正是多个实例共享原型所保存的属性和方法的基本原理。

覆盖原型的方法和属性

废话很少说,直接上代码:

```javascript
           function Animal(type,name){
               this.type = type;
               this.name = name;
           }
           Animal.prototype.say = function(){
                   console.log("我是一只叫作"+this.name+"的"+this.type);
           };
           var tom = new Animal("Cat","tom");
           var jerry = new Animal("Mouse","jerry");
          tom.say = function(){
              console.log("我偏不说~~~~");
          };
          tom.say();   //我偏不说~~~~ 
          jerry.say(); //我是一只叫作jerry的Mouse 
```

在这个例子中,咱们发如今tom实例中,say给咱们override的方法给替代了,而在jerry实例中,咱们发现也能正常的访问prototype。这是由于,当为一个对象添加一个属性,这个属性会屏蔽原型对象中保存的同名属性。须要注意的是屏蔽而非覆盖,咱们override的方法或则属性这是阻止咱们访问原型中的同名属性,而并无修改原型对象中同名属性的值。即便咱们在实例中设置这个属性为null,undefined,也不会恢复其指向原型的连接,从而让咱们从新访问原型中的属性。

若是咱们想从新恢复该属性指向原型的连接,咱们能够用delete删除该属性,就能够再次访问到该原型的同名属性了。

检测属性是否存在与实例中

使用hasOwnProperty()方法能够检测一个属性是否存在与实例中,直接上代码:

```javascript
           function Animal(type,name){
               this.type = type;
               this.name = name;
           }
           Animal.prototype.say = function(){
                   console.log("我是一只叫作"+this.name+"的"+this.type);
           };
           var tom = new Animal("Cat","tom");
           console.log(tom.hasOwnProperty("name")); //true
          console.log(tom.hasOwnProperty("say"));  //false
```

后记

这这是原型模式的第一部分,估计会分三部分。因为这段时间工做需求比较紧,可能没办法保证一天一篇,可是一周我对本身的要求是最少三篇。文中若是发现有错或则个人理解不对,请告诉我,共同进步。

相关文章
相关标签/搜索