原型与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. 数据结构:HDU 2993 MAX Average Problem

    MAX Average Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...

  2. QDomDocument Access violation writing location

    今天犯了一个非常2的错误! 为了将面板参数保存起来,选择用QDomDocument构造Dom树,然后用doc.toString()方法返回符合xml格式的QString.如: QString CutF ...

  3. pgAdminIII使用图解

    原文地址:http://www.2cto.com/database/201312/267218.html pgAdmin III简介 要打开一个到服务的连接,在树中选择所需的服务,并双击它,或使用“工 ...

  4. Eucalyptus安装包的功能列表

    aoetools    是一个用来在以太网上运行 ATA 存储协议的软件,相当于一个网络存储功能.euca2ools  eucalpytus客户端杜昂管理工具axis2c       SOAP引擎,同 ...

  5. HDU 4638 Group 【树状数组,分块乱搞(莫队算法?)】

    根据题目意思,很容易得出,一个区间里面连续的段数即为最少的group数. 题解上面给的是用树状数组维护的. 询问一个区间的时候,可以一个一个的向里面添加,只需要判断a[i]-1 和 a[i]+1是否已 ...

  6. sql服务器内部参数使用详情(存储过程)

    exec sp_help;返回当前数据库中的所有存储过程.exec sp_help datebase.dbo.table名称 返回当前表中的所有对象.如字段名称等.这个吊exec sp_helpfil ...

  7. IntelliJ IDEA安装 一些配置

    idea 配置修改 本篇 参考https://blog.liyang.io/234.html. 1.修改IDEA菜单的字体大小: 单击File | Project Structure菜单项,打开Pro ...

  8. Python中http请求方法库汇总

    最近在使用python做接口测试,发现python中http请求方法有许多种,今天抽点时间把相关内容整理,分享给大家,具体内容如下所示: 一.python自带库----urllib2 python自带 ...

  9. Android的Touch系统简介(一)

    一.Android touch事件的相关概念 用户的Touch事件被包装成MotionEvent 用户当前的touch事件主要类型有: ACTION_DOWN: 表示用户开始触摸. ACTION_MO ...

  10. Note | javascript权威指南[第六版] 第2章:词法结构

      语法结构规定了诸如变量名是什么样的.怎么写注释,以及程序语句之间如何分隔等规则.本章用很短的篇幅来介绍JavaScript的词法结构.   2.1.字符集   JavaScript程序是用Unic ...