原型与in操作符

有两种方式使用in操作符:单独使用和在for-in循环中使用。

在单独使用时,in操作符会遍历实例公开(可枚举)的属性,如果找到该指定属性则返回true,无论该指定属性是存在与实例中还是原型中。直接上代码:

```javascript
function Animal(){ }
Animal.prototype.name = "animal"
var tom = new Animal();
tom.age = 22;
console.log("name" in tom); //true
console.log("age" in tom); //true
```

以上代码虽然能够确定实例是否能够访问指定属性,但是我们不能确定指定属性到底是存在于实例上海市原型上,同时使用hasOwnPrototype()和in就可以确定指定属性到底是在实例上海市原型中。上代码:

```javascript
function Animal(){ }
Animal.prototype.name = "animal"
var tom = new Animal();
tom.age = 22;
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name) && (name in object);
}
console.log(hasPrototypeProperty(tom,"name")); //true
console.log(hasPrototypeProperty(tom,"age")); //false
```

hasPrototypeProperty()方法只有当该属性只存在于原型上时才会返回true。

在使用for-in循环时,返回的是所有对象能够访问的,可枚举的属性。既包括实例本身的属性也包括实例原型上的属性。要取得对象上所有可枚举的实例属性,可以使用ECMAScript5的Object.keys()方法。这个方法接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。上代码:

```javascript
function Animal(){
}
Animal.prototype.name = "animal"
Animal.prototype.canEat = true;
Animal.prototype.canRun = true;
var WangCai = new Animal();
console.log(Object.keys(WangCai)); // []
console.log(Object.keys(Animal.prototype)); //["name", "canEat", "canRun"]
```

如果你想得到所有的实例属性,无论它是否可枚举,可以使用Object.getOwnPropertyNames() 方法,代码如下:

```javascript
function Animal(){
}
Animal.prototype.name = "animal"
Animal.prototype.canEat = true;
Animal.prototype.canRun = true;
var WangCai = new Animal();
console.log(Object.keys(WangCai)); // []
console.log(Object.getOwnPropertyNames(Animal.prototype)); //["constructor", "name", "canEat", "canRun"]
```

Object.keys()和Object.getOwnPropertyNames() 方法都可以用来取代for-in循环,但是注意只有IE9+,FF 4+,Safari 5+,Opera 12+ ,Chrome支持。

更简单的原型语法

在前面的例子中,每给原型添加一个属性或方法都要敲一遍Animal.prototype,对于有代码洁癖的我,那是最受不了的,更好的做法是用一个对象字面量来重写prototype,代码如下:

```javascript
function Animal(name){
this.name = name;
}
Animal.prototype = {
canEat : true,
canRun : true,
say : function(){
console.log("oh ~~~~我的名字叫"+this.name);
}
};
var tom = new Animal("tom");
tom.say(); //oh ~~~~我的名字叫tom
console.dir(Animal.prototype.constructor); //function Object() { [native code] }
```

在上面的代码中,我们把Animal.prototype设置为一个以字面量形式创建的新对象。结果相同,但是我们发现constructor已经不再指向Animal了,而是指向Object的构造函数,上一篇中提到,每创建一个新函数,就会同时创建他的prototype对象,这个对象也会获得constructor属性。而我们在这里重写了prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object的构造函数),不再指向Animal,这个时候constructor也就无法确定对象的类型了。(注意constructor就不再是存在于prototype上了)

我们也可以手动指定constructor,代码如下:

```javascript
function Animal(name){
this.name = name;
}
Animal.prototype = {
constructor: Animal,
canEat : true,
canRun : true,
say : function(){
console.log("oh ~~~~我的名字叫"+this.name);
}
};
```

以上代码,我们设置了一个constructor,从而确保了通过该属性能够访问到适当的值,但是这种方式重设constructor属性会导致它的[[enumerable]] 特性被设置为true。默认情况下,原生的constructor属性是不可枚举的。在支持ECMAScript 5的浏览器上你可以用 Object.defineProperty()。示例代码如下:

```javascript
function Animal(name){
this.name = name;
}
Animal.prototype = {
constructor: Animal,
canEat : true,
canRun : true,
say : function(){
console.log("oh ~~~~我的名字叫"+this.name);
}
};
//重新设置构造函数
Object.defineProperty(Animal.prototype,"constructor",{
enumerable : false,
value : Animal
});
```

原型的动态性

前面几篇包括上面的重写prototype都涉及到了原型的动态性,所以我在这里总结一下。原型是伴随着一个新函数的创建而创建的,原型本身和一个新的Object实例并无区别,只是他多了一个constructor属性,这个constructor属性是指向那个新函数的。注意,原型是伴随着函数(那个新函数)存在的,和具体的实例并无直接联系,故每一个由这个新函数(构造函数)创建的新实例都共享原型的属性和方法(公开的),这也就是原型继承的基本。原型模式和构造函数的每个方法都是一个新的Function实例有着本质的区别,每个新的实例并无具体的方法实现(原型上定义的方法),只是通过原型链接口来访问原型上的具体实现。但是值得注意的是,通过构造函数创建的新实例指向的原型都是当初创建时构造函数所指的那个原型,就算我们在实例化以后改变该构造函数的原型(如重新声明原型为{}),该实例的原型依然是当初那个原型。而上面我们就是以字面量的方式重写了该原型,所以也就切断了已有实例和现在原型的关系。

原生对象的原型

原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。所有的原生引用类型(Object,Array,String...)都在其构造函数的原型上定义了方法。

通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新的方法,甚至可以重写默认方法(不推荐,如果出现冲突,将很难处理)。

原型对象的问题

原型模式也不是没有缺点,原型模式的最大缺点就是每个实例对原型的修改都会影响到所有继承自该原型的实例(通过该原型伴随的函数创建的实例)。可是实例一般都要有属于自己的全部属性,而这个问题也是有解决方式的(下一篇 呵呵)

后记

如果在文中发现错误,请指正。推荐一个JS学习群(群里都是大神) 239147101

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

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

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

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

    什么是原型模式? 原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象.--引自JavaScript设计模式 我们创建的每一个函数都有一个prototype ...

  3. 理解javascript中的原型模式

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

  4. 浅谈JavaScript中的原型模式

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

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

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

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

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

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

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

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

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

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

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

  10. Java进阶篇设计模式之三 ----- 建造者模式和原型模式

    前言 在上一篇中我们学习了工厂模式,介绍了简单工厂模式.工厂方法和抽象工厂模式.本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式. 建造者模式 简介 建造者模式是属于创建型模式.建造者模式使用 ...

随机推荐

  1. Qt入门(11)——Qt插件

    Qt提供了一个简单地插件接口,可以轻松地生成作为独立组件的定制数据库驱动.图象格式.文本编解码器(text codec).风格(style)和部件.警告:Qt 3.0.5对插件的一些方面做了改变,具体 ...

  2. 【转】爱普生打印机L358

    原文网址:http://www.chiphell.com/thread-838403-1-1.html 儿子幼儿园老是要打印作业,就决定用他的压岁钱买个打印机,主要以文件打印为主,兼顾照片.昨天网上一 ...

  3. Linked List Cycle——LeetCode

    Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ext ...

  4. cf602B Approximating a Constant Range

    B. Approximating a Constant Range time limit per test 2 seconds memory limit per test 256 megabytes ...

  5. [Hibernate] List 映射例子

    List 是 java 集合的一个工具,存储线性的数据,允许重复数据.开发者可以准确控制在 list 那个位置插入数据.本例子演示 Java 的 List 集合和 MySQL 数据库的映射应用. 使用 ...

  6. 使用 HTMLTestRunner.py

    HTMLTestRunner.py python 2版本 下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html 教程:http://www. ...

  7. redis 集群

    http://www.linuxidc.com/Linux/2015-08/121845.htm Redis3.0版本之后支持Cluster,具体介绍redis集群我就不多说,了解请看redis中文简 ...

  8. 性能比较工具runstats

    runstats能对做同一件事的两个不同方法进行比较,得出谁好一点.我们只需要提供两个不同方法,余下的事情都由runstats负责.runstats只是测量3个要素: 1. 耗用的时间. 2. 系统统 ...

  9. 使用Xcode8的Instruments检测解决iOS内存泄露(leak)

    在苹果没有出ARC(自动内存管理机制)时,我们几乎有一半的开发时间都耗费在这么管理内存上.后来苹果很人性的出了ARC,虽然在很大程度上,帮助我们开发者节省了精力和时间.但是我们在开发过程中,由于种种原 ...

  10. 关于C++函数思考1(缺省的六大函数)

    我们知道大神们在设计C++时候就给C++有六个默认的函数,所谓默认就是,无需我们这些程序猿们动手去写,仅仅要你在将类实例化.即创建一个对象,在利用对象进行数据操作时候,就会编译器自己主动调用默认的函数 ...