一、工厂模式
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
return o;
}
var person1 = createPerson('Jerry', 12, 'job1');
var person2 = createPerson('Tom', 13, 'job2');
这样创建的对象无法识别对象的类型(只是Object类型)
二、构造函数模式,
通过构造函数创建的对象,都可以标识为一种特定类型。
function Person(name, age, job){
this.name = name;
this.age = age;
this.jbo = jbo;
}
var person1 = new Person('Jerry', 12, 'job1');
var person2 = new Person('Tom', 13, 'job2');
//构造函数创建的对象能识别对象类型,即:person1和person2都是Person类型的对象
console.log(person1 instanceof Person); //true
console.log(person1 instanceof Object); //true
console.log(person2 instanceof Person); //true
console.log(person2 instanceof Object); //true
//对象创建后,都有一个constructor属性指向Person
console.log(person1.constructor == Person); // true
//constructor 属性就是为了标识对象的类型,但是用constructor判断对象的类型并不一定正确,在原型链技术里会存在问题,相比来说,instanceof更可靠一些。
构造函数创建对象经过下面4个步骤:
1. 创建一个新对象
2.将构造函数的作用域赋给新对象
3.执行构造函数,给新对象添加属性
4.返回新对象
构造函数与普通函数的唯一一点区别就是调用方式的不同:构造函数用new来调用,普通函数直接调用。理论上来说,任何函数都可以是构造函数,构造函数也可以像普通函数那样直接调用
Person('Jerry', 12, 'job1');//调用后,属性会添加到window对象上,因为没有指定作用域,则作用域赋给了全局对象。
//如果想确定作用域,可以这样调用构造函数
var o = new Object();
Person.call(o, 'Jerry', 12, 'job1');
问题:使用构造函数创建对象,对象中的函数会存在多个副本,例如:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
}
}
var person1 = new Person('Tom', 12, 'job1');
var person2 = new Person('Jerry', 13, 'job2');
console.log(person1.sayName === person2.sayName); // false
//person1 和 person2 分别拥有sayName 的副本
变种:
a、寄生构造函数
function Person(name){
this.name = name;
}
function SpecialPerson(name){
var sp = new Person(name);
sp.age = age;
return sp;
}
var person = new SpecialPerson('Jerry');
//该方式可以拓展原类型
//这种方式最想Delphi中的继承
b,稳妥构造函数
function Person(name){
var o = new Object();
o.sayName = function(){
console.log(name);
};
return o;
}
//该方式最大的优点就是安全,除了sayName函数外,没有任何办法访问到原始数据。
三、原型模式
每个函数都有一个prototype属性,指向一个原型对象,构造函数创建的对象,共享原型对象。
function Person(){
Person.prototype.name = 'Jerry';
Person.prototype.sayName = function(){
console.log(this.name);
}
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName === person2.sayName); // true
对象创建后,会有一个[[prototype]]属性指向原型对象;原型对象会自动获得一个constructor属性,指向Person,其它属性则直接来自Object。当调用对象的属性或者函数的时候,首先搜索对象本身,如果没有,再搜索[[prototype]]指向的原型对象(搜索算法应该是一个递归)。原型方法也可以用更方便的方式
function Person(){
}
Person.prototype = {
name : 'Jerry',
age : 12,
sayName : function(){
console.log(this.name);
}
}
//使用字面量的方式更方便,但是有一个问题:原型对象的constructor不再指向Person,而是Object,需要手动赋值。
在原型对象中添加的属性和函数,会立即反映到实例对象上。但要注意:原型对象是在创建构造函数后就已经生成了,创建的实例对象也会指向该原型对象,如果再重新创建构造函数的原型对象,实例对象会访问不到新的原型对象。
四、组合使用构造函数和原型对象模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job
}
Person.prototype = {
constructor : Person,
sayName : function(){
console.log(this.name);
}
}
组合使用构造函数和原型对象是当下使用最广泛、认同度最高的创建自定义类型的方式。