什么是原型模式?

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

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

```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实例(虽然可以解决,但是也让我们的代码丝毫没有封装性,详细请看上一篇),而原型模式正好解决了我们的问题,而且原型模式更符合我们封装的需求。

理解原型对象

无论什么时候,只有我们创建了一个新的函数,JS就会给这个新函数创建一个prototype属性,该属性指向这个新函数的原型对象。在默认情况下,所有的原型对象都会自动获得一个constructor属性,该属性包含一个指向这个函数本身的指针,上代码:

```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);
```

运行结果如下图所示:

而同样的我们可以给prototype添加其他自定义的方法和属性(原型模式的共享),当调用构造函数创建一个新实例后,该实例内部将包含一个指针,指向构造函数的原型对象。ECMA-262第五版管这个指针叫做[[prototype]],需要注意的在JS中是没有标准的方式访问[[prototype]],但Firefox,Safari,Chrome在每个对象上都支持一个属性__proto__,该属性指向创建这个实例的构造方法的原型对象,需要注意的是即便我们在以后改变了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
```

后记

这这是原型模式的第一部分,估计会分三部分。由于这段时间工作需求比较紧,可能没办法保证一天一篇,但是一周我对自己的要求是最少三篇。文中如果发现有错或则我的理解不对,请告诉我,共同进步。

初涉JavaScript模式 (5) : 原型模式 【一】的更多相关文章

  1. 初涉JavaScript模式 (7) : 原型模式 【三】

    组合使用构造函数模式和原型模式 上篇,我们提到了原型模式的缺点,就是每个实例不能拥有自己的属性,因为纯原型模式所有的属性都是公开给每个实例的,故我们可以组合使用构造函数模式和原型模式.构造函数用来定义 ...

  2. 理解javascript中的原型模式

    一.为什么要用原型模式. 早期采用工厂模式或构造函数模式的缺点:  1.工厂模式:函数creatPerson根据接受的参数来构建一个包含所有必要信息的person对象,这个函数可以被无数次的调用,工厂 ...

  3. 浅谈JavaScript中的原型模式

    在JavaScript中创建对象由很多种方式,如工厂模式.构造函数模式.原型模式等: <pre name="code" class="html">/ ...

  4. javascript创建对象之原型模式(三)

    少废话,先上代码: function Human() { } Human.prototype.name = "成吉思汗"; Human.prototype.sex = " ...

  5. javascript 面向对象编程(工厂模式、构造函数模式、原型模式)

      javascript 面向对象编程(工厂模式.构造函数模式.原型模式) CreateTime--2018年3月29日17:09:38 Author:Marydon 一.工厂模式 /** * 工厂模 ...

  6. JavaScript之面向对象学习六原型模式创建对象的问题,组合使用构造函数模式和原型模式创建对象

    一.仔细分析前面的原型模式创建对象的方法,发现原型模式创建对象,也存在一些问题,如下: 1.它省略了为构造函数传递初始化参数这个环节,结果所有实例在默认的情况下都将取得相同的属性值,这还不是最大的问题 ...

  7. javascript创建对象之函数构造模式和原型模式结合使用(四)

    创建自定义类型的常见方式就是组合使用构造函数模式与原型模式一起使用. 构造函数模式用于定义实例对象的特有的部分(属性和方法),原型模式用于定义共享的部分. 这样最大限度的节省了内存的开销. funct ...

  8. [19/04/24-星期三] GOF23_创建型模式(建造者模式、原型模式)

    一.建造者模式 本质:分离了对象子组件的单独构造(由Builder负责)和装配的分离(由Director负责),从而可以构建出复杂的对象,这个模式适用于:某个对象的构建过程十分复杂 好处:由于构建和装 ...

  9. 建造者模式与原型模式/builder模式与prototype模式/创建型模式

    建造者模式 定义 用于简化复杂对象的创建 JDK中的建造者模式 java.lang.StringBuilder中的append()方法,每次调用后返回修改后的对象本身. public StringBu ...

随机推荐

  1. CentOS 6.3上搭建PPTP VPN

    系统版本:CentOS 6.3_x86_64 eth0:172.16.10.72(实验环境当公网IP使用) eth1:192.168.100.50 1.检测是否支持ppp模块 # cat /dev/p ...

  2. 【数论】【扩展欧几里得】Codeforces 710D Two Arithmetic Progressions

    题目链接: http://codeforces.com/problemset/problem/710/D 题目大意: 两个等差数列a1x+b1和a2x+b2,求L到R区间内重叠的点有几个. 0 < ...

  3. Response.End(); 用HttpContext.Current.ApplicationInstance.CompleteRequest 代替

    Response.End(); 会报异常 HttpContext.Current.ApplicationInstance.CompleteRequest 这里有个讨论的帖子很有意思:http://q. ...

  4. VMware连不上网解决

    在VMware里安装了ubuntu,但是某一天打开它,ubuntu忽然不能上网了,于是百度了好几个方法,最后是这样解决的: 额...现在连接的是无线,前两天出问题的时候连接的是有线,在Internet ...

  5. openStack 云平台管理节点管理网口流量非常大 出现丢包严重 终端总是时常中断问题调试及当前测试较有效方案

    tuning for Data Transfer hosts connected at speeds of 1Gbps or higher <一.本次OpenStack系统调试简单过程简单记录& ...

  6. Java GC CMS 日志分析

    https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs 笔者对其中某几条记录又进行了详细说明,以下是一条完整的CMS日志记录的示 ...

  7. Java 流的概述及操作(转)

    一.什么是流? 流就是字节序列的抽象概念,能被连续读取数据的数据源和能被连续写入数据的接收端就是流,流机制是Java及C++中的一个重要机制,通过流我们可以自由地控制文件.内存.IO设备等数据的流向. ...

  8. Linux Epoll介绍和程序实例

    Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...

  9. Android图片框架---Glide

    Glide *** 使用* 一.添加依赖* compile 'com.github.bumptech.glide:glide:3.7.0'* compile 'com.android.support: ...

  10. SPOJ 4053 - Card Sorting 最长不下降子序列

    我们的男主现在手中有n*c张牌,其中有c(<=4)种颜色,每种颜色有n(<=100)张,现在他要排序,首先把相同的颜色的牌放在一起,颜色相同的按照序号从小到大排序.现在他想要让牌的移动次数 ...