JavaScript创建对象(摘录)

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 person1 = createPerson("Nick" ,21 ,"DH");
var person2 = createPerson("Tom" ,23 ,"MK");

函数createPerson( )能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以解决创建多个相似对象的问题,但是没有解决对象识别的问题(即怎样知道一个对象的类型)。

构造函数模式:

//构造函数模式1
function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;

    this.sayName = function() {
        alert(this.name);
    };
}

var person1 = new Person("Nick", 21, "DH");
var person2 = new Person("Tom", 23, "MK");

person1和person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person。


使用构造函数创建新实例,必须要使用new操作符。以这种方式调用构造函数实际上会经历以下4个步骤:
创建一个新对象;
将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
执行构造函数中的代码(为这个新对象添加属性);
返回新对象。

构造函数与其他函数的唯一区别就是在于调用它们的方式不同。任何韩式,只要通过new操作符来调用,都可以作为构造函数;而任何函数,如果不通过new操作符来调,那它跟普通函数也没区别。
构造函数的主要问题,就是每个方法都要在每个实例上重新插 u 你更加爱你一遍。在上述person1和person2都有一个名为sayName( )的方法。所以用这种方式创建函数会导致不同的作用域链和标识符解析,但是创建Function新实例的机制仍然相同。因此,不同实例上的同名函数是不相等的。所以,可以通过把函数定义转移到构造函数外部来解决这个问题:

//构造函数模式2
function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
}
function sayName() {
    alert(this.name);
}

var person1 = new Person("Nick", 21, "DH");
var person2 = new Person("Tom", 23, "MK");

这样做也有缺点:

    在全局作用域中定义的函数实际上只能被某个对象调用,而且如果对象需要定义很多方法,那么久要定义很多全局变量,那这个自定义的引用类型久没有了封装行可言。(这个问题可以通过使用原型模式解决)

原型模式:

    我们创建的每个函数都会又一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个用途是包含可以有特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享他所包含的属性和方法。即,不必在构造方法中定义对象实例的信息,而是将这些信息直接添加到原型对象中。

/原型模式
function Person() {

}

Person.prototype.name = "Nick";
Person.prototype.age = 21;
Person.prototype.job = "DH";
Person.prototype.sayName = function() {
    alert(this.name);
};
/*
Person.prototype = {
       name : “Nick”,
       age : 21,
       job : “DH”,
       sayName : function( ) { alert(this.name);}
}
*/
var person1 = new Person();
person1.sayName();//"Nick"

var person2 = new Person();
person2.sayName();//"Nick"

alert(person1.sayName == person2.sayName);//true

如代码所示,将sayName( )方法和所有属性直接添加到了Person的prototype属性中,构造函数就变成了一个空函数。即使如此,也仍然可以通过调用构造函数来创建新对象,而且新对象还会具有相同的属性和方法,与构造函数模式不同的是,新对象的这些属性和方法是有所有实例共享的,即person1和person2访问的都是同一组属性和同一个sayName( )函数。


原型对象的问题

    首先,它省略了为构造函数传递初始化参数这一个环节,结果所有的实例在默认情况下豆浆去的相同的属性值。原型中所有属性是被很多实例共享的,这种共享对于函数非常合适,对于那些包含基本址的属性也说得过去,(通过在实例上添加一个同名属性,可以隐藏原型中对应的属性)然而对于包含了引用类型值的属性来说问题比较突出:

function Person() {

}

Person.prototype = {
    constructor : Person,
    name : "Nick",
    age : 21,
    job : "DH",
    friends : ["Shelby","Count"],
    sayName : function() {
        alert(this.name);
    }
};

var person1 = new Person();
var person2 = new Person();

person11.friends.push("Van");

alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count,Van"
alert(person1.friends === person2.friends);//true

在此,Person.prototype对象又一个名为friends的属性,该属性包含一个字符串数组。然后创建了两个Person实例。接着修改了Person.prototype引用的数组,由于friends数组存在于Preson.prototype而非person1中,所以这时候person2中的数组也被修改了。


组合使用构造函数模式和原型模式:
    创建自定义类型最常见的方式,就是组合使用构造函数模式和原型模式。构造函数模式泳衣定义实例属性,原型模式用于定义方法和共享的属性。结果每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的饮用,最大限度的节省了内存。另外这种混合模式还支持香构造函数传递参数。

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("Nick", 21, "DH");
var person2 = new Person("Tom", 27, "MK");

person1.friends.push("Van");
alert(person1.friends);//"Shelby,Count,Van"
alert(person2.friends);//"Shelby,Count"
alert(person1.friends === person2.friends);//false
alert(person.sayName === person2.sayName); //true

动态原型模式:

    这种模式把所有的信息都封装在了构造函数中,而通过在构造函数中初始化原型(尽在必要的情况下),有保持了同事使用构造函数和原型的优点。即,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型:

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);
        };
    }
}

var friend = new Person("Nick", 21, "DH");
friend.sayName();

加粗部分,只有在sayName( )方法不存子啊的情况下,才会将它添加到原型中,这一段代码只有在初次调用构造函数的时候才会执行。此后原型已经完成初始化,不需要做什么修改。需要注意的是,这里对原型所做的修改,能够立即在所有实例中得到反映。


寄生构造函数模式:

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;
}

var friend = . new Person("Nick", 21, "DH");
friend.sayName();//"Nick"

通常在前述的几种模式都不适合的情况下,可以使用这种模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。


这个模式在特殊的情况下用来为对象创建构造函数。例如:

function SpecialArray() {
    //创建数组
    var values = new Array();

    //添加值
    values.push.apply(values,arguments);

    //添加方法
    values.toPipedString = function() {
        return this.job("|");
    };
    //返回数组
    return values;
}

var colors = . new SpecialArray("red", "blue", "green");
alert(colors.toPipedString());//"red|blue|green"


关于寄生构造函数模式,有一点需要说明:首先返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖instanceof操作符来确定对象类型


稳妥构造函数模式:
与寄生构造函数类似,但是有两点不同:意识新创建对象的实例方法不引用this;二是不使用new操作符调用构造方法。

//稳妥构造函数模式
function Person(name, age, job) {
    //创建要返回的对象
    var o = new Object();

    //可以在这里定义私有变量和函数

    //添加方法
    o.sayName = function() {
        alert(name);
    }

    //返回对象
    return o;
}

这种模式创建的对象,除了使用sayName( )方法外,没有其他办法访问到name的值。

这样,变量person中保存的是一个稳妥对象,而除了调用sayName( )方法外,没有别的方式可以访问其数据成员。即使有其他代码会给这个对象添加方法货数据成员,但也不可能有别的方法访问传入到构造函数中的原始数据。这种安全性,使它非常适合在某些安全执行环境,例如ADsafe和Caja提供的环境下使用。

与寄生构造函数模式类似,使用稳妥构造环视模式创建的对象与构造函数之间也没有什么关系。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值