今天看了《javascript高级程序设计》的面向对象篇,有点感想,记录下。
关于模式,是我之前都没有接触过的,亏我还是计算机系的学生,结果在上次实习面试的时候。。。怎一个惨淡了得。
- 工厂模式:用函数来封装以待定接口创建对象的细节,
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 person = createPerson("greg", 29, "doctor");//没有使用new来构造实例
这里的注意区别之后提到的寄生构造模式(使用new构造实例)和稳妥构造函数模式(不使用this,不适用new调用构造函数)! 工厂模式解决了创建多个相似对象的问题,但是却没有解决对象的识别问题。因此引入构造函数模式。
2. 构造函数模式:a.没有显示的创建对象 b.直接将属性和方法赋给了this对象 c.没有return语句。
function Person(name, age, job){//构造函数始终都应该以一个大写字母开头Object,Number this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } person1 = new Person("vv", 29, "doctor"); //使用new操作符
关于this的解析,使用上述方式创建实例时,会经过下面所说的四个步骤。
a.创建一个新对象,
b.将构造函数的作用域赋给新对象(this此时就指向了新对象)《当在全局 作用域中调用一个函数的时候,this对象指向global对象。》
c. 执行构造函数的代码,
d. 返回新对象。
构造函数的主要问题:每个方法都要在每个实例上重新创建一遍。可以将function转移到构造函数的外部来解决。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = sayName; } function sayName(){ alert(this.name); } person1 = new Person("vv", 29, "doctor");
但是全局作用域的函数只能被某个对象调用,这让全局作用域有点名不副实。如果对象需要很多的方法,则需要定义很多的全局函数。 引入原型模式
3. 原型模式: 每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象是可以由特定类型的实例共享的属性和方法。
function Person(){ } Person.prototype.name = "shelly"; Person.prototype.age = 27; Person.prototype.sayName = function(){ alert(this.name); }; person1 = new Person();
原型模式所定义的属性和方法都由实例所共享。因此我们可以通过实例来访问保存在原型中的值,但是我们不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性并且该属性的名字与原型中的属性同名,那么就会在实例中创建该属性,并且屏蔽掉原型中的属性(读取属性是通过搜索的方式进行的,现在实例中查找,若没有找到该属性,再查找原型中的属性)使用delete可以删除实例属性,从而重新访问原型中的属性。检测属性是在实例中还是在原型中可以通过hasOwnProperty()和in来实现
function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object); }
通常可以用字面量的形式来重写整个原型对象
function Person(){ } Person.prototype = { constructor : Person, //此时constructor是可枚举的 name : "shelly", age : 27, sayName : function(){ alert(this.name); } };
若需要将constructor实行设置为不可枚举的可以使用Object.defineProperty(); note:可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但是如果重写整个原型对象(字面量方式),情况就不一样了。
原型对象的主要问题:a. 所有属性在默认情况下都将取得相同的属性值; b. 由共享导致另外一个问题,如下:
function Person(){} Person.prototype = { constructor: Person, name: "Nicholas", age: 29, friends: ["shelly","courts"], sayName: function(){ alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); alert(person1.friends); //person1.friends = ["wei","json"]; //person1.friends.push("van"); alert(person1.friends); alert(person2.friends);
若是使用push方式对数组进行更改,则person1和person2的friends属性值是一样的,也就是说他们是共享的,如果对person1的数组进行重置,就如上面所说,person1会在实例中创建一个friends数组,person2则继续使用原型中的数组。
4. 组合使用构造函数模式和原型模式:使用构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["a","b","c"]; //不共享 } Person.prototype = { constructor : Person, //可枚举 sayName : function(){ alert(this.name); } }
5. 动态原型模式:把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; if(typeof this.sayName != "function"){ Person.prototype.sayName = function(){ alert("this.name"); }; } }
只有在sayName方法不存在的情况下,才会将他添加到原型中。这段代码只会在初次调用构造函数时才会执行。这里对原型所做的修改,能够立即在所有实例中得到反映
6. 寄生构造函数模式(类比工厂模式):创建一个函数,该函数的作用仅仅是封装创建的代码,然后再返回新创建的对象;
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert("this.name");
};
return o;
}
person = new Person("name", 27, "job");
除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一样的。构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个return语句,可以重写调用构造函数时返回的值。返回的对象与构造函数或者构造函数的原型属性之间没有任何关系。
7. 稳妥对象:a. 新创建对象的实例方法不引用this;b. 不使用new操作符调用构造函数。
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(name);//没有使用this
};
return o;
}
person = Person("name", 27, "job");//没有使用new
参考自《javascript高级程序设计》