1. 工厂模式 function createPerson(name,age,job){var o = new Object();o.name = name;o.age = age;o.job = j
1. 工厂模式
function createPerson(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); } return o;}var person1 = createPerson("Nicholas", 29, "software engineer");var person2 = createPerson("Greg",27,"doctor");
存在问题:无法识别对象所属类
2. 构造函数模式
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }}var person1 = new Person("Nicholas", 29, "software engineer");var person2 = new Person("Greg", 27, "doctor");
person1和person2都有一个constructor属性,该属性指向Person
alert(person1.constructor == Person); //truealert(person2.constructor == Person); //true
如果需要检测对象类型,instanceof操作符会更可靠一些。通过instanceof操作符可以验证person1和person2既是Person的实例,同时也是Object的实例,因为所有的对象都继承自Object。
alert(person1 instanceof Object); //truealert(person1 instanceof Person); //truealert(person2 instanceof Object); //truealert(person2 instanceof Person); //true
构造函数与其他函数唯一的区别在于调用方式不同。任何函数,只要通过new操作符来调用就是构造函数,只要不是通过new操作符调用的就是普通函数
// 当作构造函数使用var person = new Person("Nicholas", 29, "software engineer");person.sayName(); //"Nicholas"// 作为普通函数调用Person("Greg", 27, "doctor"); //添加到windowwindow.sayName(); //"Greg"// 在另一个对象的作用域中调用var o = new Object();Person.call(o, "Kristen", 25, "Nurse");o.sayName(); //"Kristen"
存在问题:person1和person2虽然都是Person的实例,但它们都独自拥有一个sayName()方法。在内存中将会存在完成相同任务的多个相同函数,这是浪费内存空间且不必要的。
3. 原型模式
每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型的好处是可以让所有对象实例共享它说包含的属性和方法。
function Person(name,age,job){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "software engineer";Person.prototype.sayName = function(){ alert(this.name);};var person1 = new Person();person1.sayName(); //"Nicholas"var person2 = new Person;person2.sayName(); //"Nicholas"alert(person1.sayName == person2.sayName); //true
将sayName()方法和所有属性都添加到Person的prototype属性中,构造函数成了空函数。这样依然能通过构造函数创建对象,且所有的对象都具有相同的属性和sayName()方法。
上图展示了构造函数、原型和实例之间的关系。它们可以调用sayName()方法是通过查找对象属性的过程来实现的。person1和person2中的[[Prototype]]属性是无法访问到的,可以通过isPrototypeOf()方法来检测对象之间的关系
alert(Person.prototype.isPrototypeOf(person1)); //truealert(Person.prototype.isPrototypeOf(person2)); //true
ECMAScript 5 增加了一个新的方法 Object.getPrototypeOf(),支持这个方法的浏览器有:IE 9+、 Firefox 3.5+、Safari 5+、Opera 12+和Chrome
alert(Object.getPrototypeOf(person1) == Person.prototype); //truealert(Object.getPrototypeOf(person1).name); //"Nicholas"
如果构造函数中和原型中存在同名的属性,实例调用该属性时会调用构造函数中的,忽略原型中的。
可以使用hasOwnPrototype()方法检测一个属性是否存在与构造函数中还是原型中
var person1 = new Person();var person2 = new Person();alert(person1.hasOwnProperty("name")); //falseperson1.name = "Greg";alert(person1.name); //"Greg"--来自实例alert(person1.hasOwnProperty("name")); //truealert(person2.name); //"Nicholas"--来自原型alert(person2.hasOwnProperty("name")); //falsedelete person1.name;alert(person1.name); //"Nicholas"--来自原型alert(person1.hasOwnProperty("name")); //false
3.1 原型与in操作符
in操作符可以单独使用也可以在for-in循环中使用。单独使用in操作符在对象能够访问给定属性时返回true。
alert(person1.hasOwnProperty("name")); //falseperson1.name = "Greg";alert(person1.name); //"Greg"--来自实例alert(person1.hasOwnProperty("name")); //truealert("name" in person1); //truealert(person2.name); //"Nicholas"--来自原型alert(person2.hasOwnProperty("name")); //falsealert("name" in person2); //true
和hasOwnProperty()方法同时使用可以确定属性定义在对象中还是原型中
function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object);}var person1 = new Person();alert(hasPrototypeProperty(person1,"name")); //trueperson1.name = "Greg";alert(hasPrototypeProperty(person1,"name")); //false
for-in可以枚举对象所有可访问、可枚举的属性和方法
var o = { toString: function(){ return "My Object"; }};for(var prop in o){ if(prop == "toString"){ alert("Found toString"); //在 IE 中不会显示 }}
要取得对象中所有可枚举的实例属性,可以使用ECMAScript 5 的Object.keys()。该方法接收一个对象作为参数,返回字符串数组
function Person(name,age,job){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "software engineer";Person.prototype.sayName = function(){ alert(this.name);};var keys = Object.keys(Person.prototype);alert(keys); //"name,age,job,sayName"var person1 = new Person();person1.name = "Rob";person1.age = 31;var p1keys = Object.keys(person1);alert(p1keys); //"name,age"
3.2 更简单的原型语法
可以直接重新定义prototype。
function Person(name,age,job){}Person.prototype = { construct : Person, name : "Nicholas", age : 29, job : "software engineer", sayName : function(){ alert(this.name); }};
3.3 原型的动态性
对原型对象所做的任何修改都能够立即在实例上反映出来
var person1 = new Person();Person.prototype.sayHi = function(){ alert("hi"); }; person1.sayHi(); //"hi"(没有问题)
需要特别注意,实例化和定义原型的顺序,实例化对象必须在定义原型之后才会有影响
function Person(name,age,job){}var person1 = new Person();Person.prototype = { construct : Person, name : "Nicholas", age : 29, job : "software engineer", sayName : function(){ alert(this.name); }};person1.sayName(); //error
3.4 存在问题
存在原型中的引用类型值的属性会被所有实例共享,实例对属性的修改会互相影响。所以,原型模式不适合单独使用
function Person(){}Person.prototype = { construct : Person, name : "Nicholas", age : 29, job : "software engineer", friends : ["Shelby", "Court"], sayName : function(){ alert(this.name); }};var person1 = new Person();var person2 = new Person();person1.friends.push("Van");alert(person1.friends); //"Shelby,Court,Van"alert(person2.friends); //"Shelby,Court,Van"alert(person1.friends === person2.friends); //true
4. 组合模式
结合原型模式和构造函数模式,构造函数用于定义实例属性,而原型模式用于定义共享属性和方法。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"];}Person.prototype = { construct : Person, sayName : function(){ alert(this.name); }};var person1 = new Person("Nicholas", 29, "software engineer");var person2 = new Person("Greg", 27, "doctor");person1.friends.push("Van");alert(person1.friends); //"Shelby,Court,Van"alert(person2.friends); //"Shelby,Court"alert(person1.friends === person2.friends); //falsealert(person1.sayName === person2.sayName); //true
5. 动态原型模式
动态原型模式也是原型模式和构造函数模式的结合,区别在于将所有信息都封装在构造函数中。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; if(typeof this.sayName != "function"){ Person.prototype.sayName = function(){ alert(this.name); } }}var person1 = new Person("Nicholas", 29, "software engineer");person1.sayName();
6. 寄生构造函数模式
这个模式可以在藤属的情况下用来为对象创建构造函数,假设创建一个具有额外方法的特殊数组
function SpecialArray(){ //创建数组 var values = new Array(); //添加值 values.push.apply(values, arguments); //添加方法 values.toPipedString = function(){ return this.join("|"); }; //返回数组 return values;}var colors = new SpecialArray("red","blue","green");alert(colors.toPipedString()); //"red|blue|green"
参考文献: JavaScript高级程序设计(第3版)