javascript中创建对象的几种设计模式
javascript中利用Object构造函数和对象字面量的方法都可以创建单个对象,但它们有个明显的缺点就是:使用同一个接口创建很多对象,会产生大量的重复代码。为了解决这个问题,故而产生了诸多的设计模式:
一.工厂模式
其主要思想是用函数封装以特定接口创建对象的细节,下面是《JavaScript高级程序设计》的例子:
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 alert(this.name); 8 }; 9 return o; 10 } 11 var person1 = createPerson("Nicholas",29,"Software Engineer"); 12 var person2 = reatePerson("Greg",27,"Doctor");
优点:解决了创建多个相似对象的问题,避免了大量重复的代码;
缺点:不能进行对象识别,即如何才能知道实例的对象的类型;
二.构造函数模式
其主要思想是:创建自定义的构造函数,从而定义自定义对象类型的属性和方法,下面是例子:
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");
它和工厂模式的不同:没有显式地创建对象;直接将方法和属性传给this对象;没有return语句。
创建person的实例会经过四个步骤:1.创建一个新对象;
2.将构造函数的作用域赋给新对象;
3.执行构造函数中的代码;
4.返回新对象。
优点:解决了对象识别的问题,可以用alert(person1 instanceof Person)来进行对象识别;
缺点:构造函数中的每个方法都要在每个实例上重新创建一遍,即以这种方式创建函数会导致不同的作用域链和标识符解析。如上面的例子,我们完全可以将sayNname方法放在 外面。
三.原型模式
主要思想:我们创建的每个函数都有一个prototype属性,它是一个指针,指向一个对象,该对象中可以包含由特定类型的所有实例共享的属性和方法。下面是例子:
1 function Person(){ 2 } 3 Person.prototype.name = "Nicholas"; 4 Person.prototype.age = 29; 5 Person.prototype.job = "Software Engineer"; 6 Person.prototype.sayName = function(){ 7 alert(this.name); 8 } 9 var person1 = new Person(); 10 person1.sayName();//"Nicholas" 11 var person2 = new Person(); 12 person2.sayName();//"Nicholas" 13 alert(person1.Sayname == person2.sayName);//true
涉及到的方法:isPrototypeOf():alert(Person.prototype.isPrototypeOf(person1))//true
Objesct.getPrototypeOf():alert(Object.getPrototypeOf(person1)==Person.prototye)//true
hasOwnProperty():可以用来查找一个属性是否是在实例中,它不会再实例的原型中查找。如alert(person1.hasOwnProperty(""name"))
in操作符,会访问原型,同时使用in操作符和hasOwnProperty()可以知道属性到底是存在于实例对象中还是存在于原型中
注意事项:搜索某个对象的属性时,首先从对象实例本身开始,如果在实例中找到了该属性,则返回该属性的值,如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找该属性。故实例中若存在和原型对象中同名的属性或者方法,会屏蔽原型中相对应的属性和方法。如若不想屏蔽,可以用delete删除实例属性,则可以重新访问原型中的属性。
优点:使用原型对象可以让所有对象实例共享它所包含的属性和方法,不必再对象实例中定义对象实例的信息。
缺点:所有实例在默认情况下回取得相同的值,特别是当原型中有引用类型的值时,任何一个对象实例修改的信息都会在其他实例中表现出来,显然这不是想要的结果。
四.组合使用构造函数模式和原型模式
这是最常见的方式,其主要思想是构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性,集两种模式之长。下面是例子:
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby","Court"]; } Person.prototype = { constructor: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 Count, Van" alert(person2.friends); //"shelby,Cpout" alert(person1.sayName == person2.sayName);true
优点:构造函数模式和原型模式的优点它都有;
缺点:独立的构造构造函数和原型可读性不好。
五.动态原型模式
1 function Person(name,age,job){ 2 //属性 3 this.name = name; 4 this.age = age; 5 this.job = job; 6 //方法 7 if(typeof this.sayName != "function"){ 8 Person.prototype.sayName = function(){ 9 alert(this.name); 10 }; 11 } 12 }
优点:保持了构造函数和原型的优点,解决了可读性的问题,很完美。(这段代码只会在初次调用构造函数时才会执行,此后原型已经完成初始化,不需要再做什么修改了)
六.寄生构造函数模式
基本思想:创建一个函数,该函数的作用仅仅是封装对象的代码,然后再返回新创建的对象
使用场景:在特殊情况下用来为对象创建构造函数,比如想创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数,因此可以使用这个模式。
七.稳妥构造函数模式
道格拉斯发明了稳妥对象这个概念,稳妥对象指的是没有公共属性,而且其方法也不引用this的对象。
稳妥对象最适合在一些安全的环境中,或者在防止数据被其他应用程序改动时使用。
特点:新创建对象的实例方法不引用this;不使用new操作符调用构造函数。