原型与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. Win7下IE8无法打开https类型的网站解决方法笔记

      现象: 一台笔记本(XP系统),一台台式机(Win7,64位系统),都是IE8,之前没任何问题,访问https也没异常,都能正常访问; 前天突然发现登录火车票网站出现无法打开登录页面情况,后来换其 ...

  2. poj3186 Treats for the Cows

    http://poj.org/problem?id=3186 Treats for the Cows Time Limit: 1000MS   Memory Limit: 65536K Total S ...

  3. Sqrt(x)——LeetCode

    Implement int sqrt(int x). Compute and return the square root of x. 题目大意:实现求一个int的根. 解题思路:二分. public ...

  4. 高效算法——B 抄书 copying books,uva714

    Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description ...

  5. [转]C服务端与java客户端的socket通信注意事项

    http://blog.csdn.net/gaoxin1076/article/details/7671752 Socket网络通讯开发总结之:Java 与 C进行Socket通讯 注意以下问题: 1 ...

  6. Java Spring的 JavaConfig 注解

    序 传统spring一般都是基于xml配置的,不过后来新增了许多JavaConfig的注解.特别是springboot,基本都是清一色的java config,不了解一下,还真是不适应.这里备注一下. ...

  7. redis 集群

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

  8. C#中反射接受的字符串需要满足的Backus-Naur Form语法

    MSDN的Specifying Fully Qualified Type Names指明了C#中反射接受的字符串需要满足如下的Backus-Naur Form语法. BNF grammar of fu ...

  9. OpenGL ES2.0基础入门

    1.OpenGL ES 1.x渲染管线(又称为渲染流水线) (1).基本处理: 基本处理主要是设定3D空间中物体的顶点坐标.顶点对应的颜色.顶点的纹理坐标等属性,并且指定绘制方式. 常见的绘制方式有: ...

  10. CF 295E Yaroslav and Points(Splay)

    题目大意: 两个操作 1 id op  把id的位置+op 2 id op  查询在[id.op]之间的全部的数的差 思路: 关键是pushup函数. 自己退一下会发现.跟区间的总和,区间的节点个数有 ...