面向对象设计——创建对象

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("Lucy",30,"Software Engineer");
var person2 = createPerson("Greg",30,"Doctor");

优势:可无数次调用,每次都返回一个包含三个属性一个方法的对象,避免出现多个相似对象的问题。

缺点:无法判断对象的类型。   

2.构造函数模式

与其他函数的区别在于调用方式,通过new操作符来调用,如果不用new,跟其他函数没有什么区别。

function Person(name,age,job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    }
}
var person1 = new Person("Lucy",30,"Software Engineer");
var person2 = new Person("Greg",30,"Doctor");

作为构造函数调用:
var person = new Person("Greg",30,"Doctor");
person.sayName(); //Greg
作为普通函数调用:
Person("Greg",30,"Doctor");  //添加到window
window.sayName();或者sayName();  //Greg
在另一个对象作用域中调用
var o = new Object();
Person.call(o,"Kristen",30,"Nurse");
o.sayName();  //Kristen

优势:

(1)创建自定义的构造函数,意味着可以将实例标识为一种特定的类型,是胜过工厂模式的地方。

alert(person1 instanceof Person);  //true

alert(person1 instanceof Object);  //true

(2)每个方法都要每个实例上重新创建一遍,例子中person1和person2都有一个sayName()的方法,但是两个方法不是同一个Function实例。注:ECMAScript中的函数是对象,因此每定义一个函数,也就实例化一个对象。

等价于:this.sayName = new Function("alert(this.name)");

避免方案:

function Person(name,age,job) {
	this.name = name;
	this.age = age;
	this.job = job;
	this.sayName = sayName;
}
function sayName(){
	alert(this.name);
}  //弊端:全局作用域的函数实际只能被某个对象调用,让全局作用域名不副实;若对象需要定义很多方法,就对应很多全局方法。

3.原型模式

function Person() {}
Person.prototype.name = "Lucy";
Person.prototype.age = 30;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
	alert(this.name)
}
var person1 = new Person();
person1.sayName();  //Lucy

//简单语法

function Person() {}
Person.prototype = {
constructor: Person, //确保constructor属性指向Person函数 name: "Lucy", age: 30, sayName: function(){ alert(this.name) } }; var person1 = new Person(); person1.sayName(); //Lucy
注:若将var person1 = new Person();提升到第二行,则报错,因为重写原型对象切断了现有实例和新原型之间的联系,它们的引用任然是最初的原型。

优势:可以让所有实例共享它所包含的属性和方法。

原理:只要创建了一个函数,即为该函数创建一个prototype属性,这个属性指向函数的原型对象。默认情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性指向函数。

方法:

确定原型与实例关系的两个方法:

(1)instanceof操作符

person1 instanceof Person  //true

(2)isPropotypeOf()方法

Person.prototype.isPrototypeOf(person1)  //true

其他:

Object.getPrototypeOf()方法:Object.getPrototypeOf(person1)返回[[prototype]]的值

hasOwnProperty():检测一个属性是否存在于实例中

in操作符:只要通过对象能访问到属性,就返回true,同上结合即可确定该属性是存在于对象中还是原型中。

弊端:原型模式的所有的属性被很多实例共享,即由于其共享本质所致;尤其是引用类型值的属性(即["red","green","yellow"])。

4.组合使用构造函数模式和原型模式(认可度高,推荐)

即构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性

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,Court,Van'
alert(person1.friends);  //'Shelby,Court'

缺点:有其他OO语言开发经验的人员在看到独立构造函数和原型时,有些困惑。

5.动态原型模式

把所有信息都封装在了构造函数中,通过在构造函数中初始化原型(仅在有必要的情况下)。

function Person(name, age, job){
    this.name = name; 
    this.age = age; 
    this.job = job; 
    //方法
    if(typeof this.name != function){
         Person.prototype.sayName = function(){
              alert(this.name);  
         }  
    }
}       

优势:对于采用这种模式创建的对象,还可以使用instanceof操作符来确定它的类型。即 person1 instanceof Person; //true

6.寄生构造函数模式

除了使用new操作符外,其他跟工厂模式一模一样。

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|blur|green'

缺点:返回的函数与构造函数或者说与构造函数的原型属性一点关系没有,跟工厂模式一样的,不能依赖instanceof操作符来确定类型。

7.稳妥构造函数模式

function Person(name, age, job){
    var o = new Object();
    //可以在这里定义私有变量和函数
    o.sayName = function(){
        alert(name);
    }
    return o;
}
var person1 = Person('Nicholas', 29, 'Software Engineer');
person1.sayName();  //'Nicholas'

优势:除了调用sayName方法外没有别的办法可以访问到其数据成员,适合安全环境中,防止数据被其他程序改动时使用。

与寄生构造函数的区别

(1)新创建对象的实例方法不使用this

(2)不使用new操作符调用构造函数  

缺点:与寄生构造函数类似,操作符instanceof对此对象不起作用。

  

 

posted @ 2016-07-22 15:03  简惬  阅读(157)  评论(0编辑  收藏  举报