1、 工厂模式
2、 构造函数模式 [解决对象识别问题]
3、 原型模式 [解决方法和属性共享问题]
4、 组合使用构造函数模式和原型模式
5、 动态原型模式
6、 寄生构造函数模式
7、 稳妥构造函数模式
- 最简单的方式:创建一个Object的实例,然后为它添加属性和方法。
缺点:使用同一个接口创建很多对象,会产生大量的重复代码
1 var person = new Object(); 2 person.name = 'mackxu'; 3 person.age = 23; 4 person.job = 'Software Engineer'; 5 6 person.sayName = function() { 7 debug(this.name); 8 };
- 工厂模式:这种模式抽象了创建具体对象的过程。
具体做法:用函数封装以特定接口创建对象的细节
缺点:对比上一个方法解决了创建多个相似对象的问题,但却没有解决对象识别的问题
1 function createPerson(name, age, job) { 2 var o = new Object(); 3 o.name = name; 4 o.age = age; 5 o.job = job; 6 o.sayName = function() { 7 debug(this.name); 8 }; 9 return o; 10 }
- 构造函数模式:解决了对象识别问题
使用new操作符调用构造函数经历4个步骤:
1、 创建一个新对象
2、 将构造函数的作用域赋给新对象(this指向这个新对象)
3、 执行构造函数中的代码(为这个新对象添加属性)
4、 返回新对象
1 function Emp(name, age, job) { 2 this.name = name; 3 this.age = age; 4 this.job = job; 5 this.sayName = function() { 6 debug(this.name); 7 }; 9 }
var p = new Emp();
模拟过程如下:
var p = {};
Emp.apply(p);
p.__proto__ = Emp.prototype;
将构造函数当作函数
1 var person = new Person('mackxu', 23, 'Software Engineer'); 2 person.sayName(); 3 debug(person.constructor == Person); 4 debug(person instanceof Person); 5 //var person2 = Person('mack', 22, 'Doctor'); 6 //作为普通的函数 7 Person('mack', 22, 'Doctor'); 8 sayName(); // == window.sagName(); 9 //在另外一个对象的作用域中调用 10 var o = new Object(); 11 //this 指向o对象 12 Person.call(o, 'zhangsan', 23, 'Nurse'); 13 o.sayName(); //zhangsan 14 debug(o instanceof Person); //false
问题:方法不能被实例共享
解决方法有二:1、把方法定义到构造函数外面,但破坏了封装性
2、用原型模式共享方法
- 原型模式(解决属性方法共享问题)
每一个函数都有一个prototype属性,这个属性是一个对象,它的用途是保存可以由特定类型的所有实例共享的属性和方法。
1 function Person() { 2 //... 3 } 4 Person.prototype.name = 'mackxu'; 5 Person.prototype.age = 22; 6 Person.prototype.job = 'Software Engineer'; 7 Person.prototype.sayName = function() { 8 debug(this.name); 9 }; 10 debug(person.sayName == person2.sayName) //true
理解原型:
1 debug(Person.prototype.isPrototypeOf(person)); //true 2 使用delete操作符删除实例的属性 3 使用hasOwnProperty()方法可以检测一个属性在实例中,还是在于原型中。 4 (准确的说:只能确定在实例中的属性,返回true) 5 debug(person2.hasOwnProperty('name')); //false 6 debug('name' in person2); //true 7 //自定义函数判断属性在原型中定义 8 function hasPrototypeProperty(object, name) { 9 return !object.hasOwnProperty(name) && (name in object); 10 } 11 debug(hasPrototypeProperty(person2, 'name')); //true 12 //会屏蔽原型中与实例中相同的属性 13 for (var property in person) { 14 debug(property); 15 }
更简单的原型语法[字面量对象]
1 function Person() { 2 //... 3 } 4 Person.prototype = { 5 constructor: Person, 6 name : 'mackxu', 7 age : 22, 8 job : 'Software Engineer', 9 sayName: function() { 10 debug(this.name); 11 } 12 }; 13 var person2 = new Person(); 14 debug(person2.name); 15 debug(person2 instanceof Person); //true 16 debug(person2.constructor); //Person{} 17 debug(Person.prototype.constructor); //Person{}
原型的动态性
由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来
——即使是先创建了实例后修改原型也照样如此。
1 function Person() { 2 //..... 3 } 4 var person = new Person(); 5 Person.prototype.sayHi = function() { 6 debug('Hi'); 7 }; 8 person.sayHi(); //Hi 9 10 Person.prototype = { 11 name: 'mackxu', 12 sayHi: function() { 13 debug('Hi'); 14 } 15 }; 16 person.sayHi(); //error
原理:
原生对象的原型
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且可以定义新方法
//为基本包装类型String添加自定义方法
String.prototype.startWith = function(text) { return this.indexOf(text) == 0; }; var msg = 'Hello World'; debug(msg.startWith('Hello')); //true
原型对象的问题:
1、 省略了为构造函数传递初始化参数的环节,
2、 其共享本性导致的(对于引用类型属性)
function Person() { //... } Person.prototype = { constructor: Person, students: ['aaa', 'bbb'] }; //students被实例所共享,违背了我们的初衷 var person2 = new Person(); debug(person2.students); var person3 = new Person(); person2.students.push('ccc'); debug(person3.students); //"aaa", "bbb", "ccc"
3、 实例:属性是自己的,方法是被共享的
- 组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
结果,每个实例都有都会有自己的一份实例属性的副本,同时共享着对方的方法引用
1 function Person(name, age, job) { 2 this.name = name; 3 this.age = age; 4 this.job = job; 5 this.friends = ['zhangsan', 'lisi']; 6 } 7 Person.prototype = { 8 constructor: Person; 9 sayName: function() { 10 debug(this.name); 11 }; 12 }; 13 var person2 = new Person('mackxu', 22, 'Software Engineer'); 14 var person3 = new Person('mackxu2', 23, 'Software Engineer'); 15 person2.sayName(); //mackxu 16 person2.friends.push('wangwu'); 17 debug(person2.friends); //['zhangsan', 'lisi', 'wangwu'] 18 debug(person3.friends); //['zhangsan', 'lisi']
- 动态原型模式
把所有信息都封装在构造函数中,通过在构造函数中初始化原型,保持了同时使用构造函数和原型的有点。可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型
1 function Person(name, age, job) { 2 this.name = name; 3 this.age = age; 4 this.job = job; 5 this.friends = ['zhangsan', 'lisi']; 6 7 if (typeof this.sayName != 'function') { 8 Person.prototype.sayName = function() { 9 debug(this.name); 10 }; 11 Person.prototype.sayHi = function() { 12 debug("Hi"); 13 }; 14 } 15 } 16 var person2 = new Person('mackxu', 22, 'Software Engineer'); 17 person2.sayName(); //mackxu 18 person2.sayHi();
使用动态原型模式时,不能使用对象字面量重写原型。
- 寄生构造函数模式
构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加return,可以重写调用构造函数时返回的值。
注意:返回的对象与构造函数或者与构造函数的原型属性之间没关系
1 function Person(name, age, job) { 2 var o = new Object(); 3 o.name = name; 4 o.age = age; 5 o.job = job; 6 o.sayName = function() { 7 debug(this.name); 8 }; 9 return o; 10 } 11 12 var person2 = new Person('mackxu', 22, 'Software Engineer'); 13 person2.sayName(); //mackxu 14 15 function SpecialArray() { 16 //创建数组 17 var values = new Array(); 18 //添加值 19 values.push.apply(values, arguments); 20 //添加方法 21 values.toPipedString = function() { 22 return this.join('|'); 23 }; 24 return values; 25 } 26 //此处new的作用是什么?? 27 var colors = new SpecialArray('red', 'blue', 'green'); 28 debug(colors.toPipedString());
- 稳妥构造函数模式
禁止使用this和new,所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象