弊端:没有解决对象的识别问题,即怎么知道一个对象的类型。
与工厂模式相比:
1、没有显式的创建对象
2、直接将属性和方法赋给了 this 对象
3、没有 return 语句
要创建 person 的实例,必须使用 new 操作符,以这种方式调用构造函数实际上会经历 4 个步骤:
1、创建一个新对象
2、将构造函数的作用域赋给新对象
3、执行构造函数中的代码
4、返回新对象
创建自定义的构造函数可以将它的实例标识为一种特定的类型。
构造函数的缺点:
每个方法都有在每个实例上重新创建一遍。person1 和 person2 都有一个 sayName() 的方法,但两个方法不是同一个 Function 实例。不同实例上的同名函数是不相等的。
创建两个完成同样任务的 Function 实例没有必要,而且还有 this 对象在,不需要在执行代码前就把函数绑定在特定对象上,可以像下面这样。
把 sayName 属性设置成全局的 sayName 函数,这样,由于 sayName 包含的是一个指向函数的指针,因此 person1 和 person2 对象就共享了同一个函数。
但是,如果对象需要定义很多方法,那么就要定义很多全局函数,自定义的引用类型也没有封装可言了。为了解决上述问题,引入原型模式。
理解原型对象
我们创建的每个函数都有一个 prototype 属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。prototype 是通过调用构造函数而创建的那个对象实例的对象原型,使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
首先,解析器会问实例 person1 是否有 name 属性,如果有,就返回。
如果没有,就继续去 person1 的原型中搜索 name 属性,如果有就返回。
如果没有,再继续向 person1 的原型的原型中搜索。
isPrototypeOf() 确定实例和原型对象之间的关联
- console.log(Person.prototype.isPrototypeOf(person1)); //true
Object.getPrototypeOf()返回的是 [[prototype]] 的值
- console.log(Object.getPrototypeOf(person1));
- //Person {name: "Yvette", age: 26, job: "engineer"} 返回的是Person的原型对象。
- console.log(Object.getPrototypeOf(person1) === Person.prototype) //true
- console.log(Object.getPrototypeOf(person1).name); //"Yvette"
hasOwnProperty() 方法可以检测一个属性是存在于实例中,还是存在于原型中,只有给定属性存在于实例中,才会返回 true。
- console.log(person1.hasOwnProperty("name")); //false
原型与 in 操作符
有两种方式使用 in 操作符:单独使用和在 for-in 循环中使用。单独使用时,in 操作符会在通过对象能够访问给定属性时返回 true,无论该属性在于实例中还是原型中。
使用 for in 循环,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括实例中的属性,也包括存在于原型中的属性。如果实例中的属性屏蔽了原型中不可枚举的属性,那么也会返回。IE9 之前的版本实现上有一个 Bug, 屏蔽不可枚举属性的实例属性不会在 for-in 中返回。
在 IE9 之前的吧按本中没有 log 信息。尽管 person 实例中的 toString() 方法屏蔽了原型中的不可 枚举的 toString();
原型简写
这导致了 person1.constructor 不再指向 Person,而是指向了 Object。如果 constructor 很重要,则需要特意将其设为适当的值,如:
但是这种方式会导致 constructor 属性变成可枚举。
如果想设置为不可枚举的(默认不可枚举),可以使用
- Object.defineProperty(Person.prototype, "constructor", {
- enumerable: false,
- value: Person
- });
原型的动态性
由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来。
如果重写整个原型对象,情况就不一样了。调用构造函数时会为实例添加一个指向最初原型的 [[prototype]] 指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。实例中的指针仅指向原型,而不指向构造函数。
person.prototype 指向的是原本的原型对象,而不会指向新的原型对象。
原型对象的问题
原型模式最大问题是由其共享的本性所导致的。
对于包含引用类型值的属性来说,问题较为突出
本意只想修改 person1 的 friends,但是却导致 person2 的 friends 属性值也改变了。因此我们很少单独使用原型模式。
创建自定义类型的最常用的方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性,这样每个实例都有自己的一份实例属性的副本,又同时共享着对方法的引用,最大限度的节省了内存。
除了以上几种方式以外,另外还有动态原型模式,寄生构造模式和稳妥构造模式,但是鉴于使用频率较低,不再赘述。
来源: