批量创建ECMAScript对象的七种模式

创建单个对象的方法:

  1. 直接量

    var blog = {
        author: "MirrorAvatar",
        year: 2015
    };
  2. 关键字new

    var blog = new Object();
    blog.author = "MirrorAvatar";
    blog.year = 2015;

以上方法的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为了可复用性,才有了下面七种用于批量创建对象的七种模式。

1.工厂模式(Factory Pattern)

 
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("MirrorAvatar", "3", "coder");
var person2 = createPerson("Cindy", "4", "master");

console.log(person1);  //Object {name: "MirrorAvatar", age: "3", job: "coder", sayName: function}
console.log(person2); //Object {name: "Cindy", age: "4", job: "master", sayName: function}
 

代码特点:

  • 函数名首字母小写
  • 可以传入参数
  • 函数显式地创建并返回一个对象
  • 解决了创建多个相似对象的问题

缺点:无法识别一个对象的类型

2.构造函数模式(Constructor Pattern)

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert(this.name);
    }
}

var person1 = new Person("MirrorAvatar", "3", "coder");
var person2 = new Person("Cindy", "4", "MLer");

console.log(person1);  //Person {name: "MirrorAvatar", age: "3", job: "coder", sayName: function}
console.log(person2);  //Person {name: "Cindy", age: "4", job: "MLer", sayName: function}
 
代码特点:
  • 函数名首字母大写,为区分普通函数
  • 直接将属性和方法赋值给this对象
  • 没有return语句
  • 创建Person对象实例,必须使用new操作符
创建构造函数实例的过程:
  1. 创建一个新对象;
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
  3. 执行构造函数中代码(为这个新对象添加属性);
  4. 返回新的对象。
创造出不同实例的特点:

person1和person2都有一个constructor属性,都指向了Person。

person1.constructor === Person;  //true
person2.constructor === Person;  //true
person1.constructor === person2.constructor;  //true
 

对象的constructor属性最初是用来标识对象类型的,但是用instanceof操作符更可靠。

person1.instanceof Object;  //true
person1.instanceof Person;  //true
person2.instanceof Object;  //true
person2.instanceof Person;  //true
 
构造函数模式优缺点

优点:创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。

缺点:构造函数里的每个方法都要在实例上重新创造一遍,不可复用。如果像下面的代码那样讲方法抽象出来,那么如果对象需要定义很多方法,那么就要定义很多全局函数,毫无封装性。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}

function sayName() {
    alert(this.name);
}
 
理解构造函数
  1. 构造函数也是函数,不存在函数的特殊用法。
  2. 构造函数和其他函数的区别,在于调用它们的方式不同。

    任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,那它跟普通函数也不会有什么两样。

//当做构造函数用
var person = new Person("Michael282694", 5, "MLer");
person.sayName();  //Michael282694

//当做普通函数用,添加到宿主对象window,this指向Global对象
Person("Michael", 6, "MLer");
window.age;  //6
window.sayName();  //Michael

//在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "Cindy282694", 7, "MLer");
o.sayName();  // Cindy282694
 

3.原型模式

//原型模式一

function Person() {
}

Person.prototype.name = "MirrorAvatar";
Person.prototype.age = 4;
Person.prototype.job = "coder";
Person.prototype.sayName = function() {
    alert(this.name);
}

var person1 = new Person();
person1.sayName();  //MirrorAvatar

var person2 = new Person();
person2.sayName();  //MirrorAvatar
 
代码特点
  1. Person函数为空;
  2. 属性方法都绑定在Person函数的原型(prototype)上。
//原型模式二,减少模式一不必要的输入
function Person() {
}

Person.prototype = {

    name: "MirrorAvatar",
    age: 4,
    job: "coder",
    sayName: function() {
        alert(this.name);
    }
};

Person.prototype.constructor === Person;  //false
Person.prototype.constructor === Object;  //true
person1 instanceof Person;  //true
person2 instanceof Object;  //true
 
特点
  1. Person函数为空;
  2. Person的prototype是以字面量的形式定义的3;
  3. 此时Person函数原型对象的constructor不再指向Person而是Object。
//原型模式三,针对上述3的缺陷
function Person() {
}

Person.prototype = {

    constructor: Person,
    name: "MirrorAvatar",
    age: 3,
    job: "coder",
    sayName: function() {
        alert(this.name);
    }
};

//以上重设了constructor指向,导致constructor的[[Enumerable]]特性设置成true,默认是false的。
//重设构造函数,只适用于ECMAScript 5兼容的浏览器 
Object.defineProperty(Person.prototype, "constructor",{
    enumerable: false,
    value: Person
});
 
重写整个原型对象

重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;它们引用的仍然是最初的原型。

function Person() {
}

var friend = new Person();

Person.prototype = {
    constructor: Person,
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function() {
        alert(this.name);
    }
};

friend.sayName();  //Error
 

重写原型对象前:

重写原型对象前

重写原型对象后:

重写原型对象后

原型模式缺点:
  1. 它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值;
  2. 对于引用类型值的属性,体现不出实例的个性。
function Person() {
}

Person.prototype = {
    constructor: Person,
    name: "MirrorAvatar",
    age: 2,
    job: "coder",
    friends: ["Cindy","Cindy282694"],
    sayName: function() {
        alert(this.name);
    }
}

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

person1.friends.push("Michael");

console.log(person1.friends);  //"Cindy,Cindy282694,Michael"
console.log(person2.friends);  //"Cindy,Cindy282694,Michael"

person1.friends === person2.friends;  //true
 

4.组合使用构造函数模式和原型模式

创建自定义类型的最常见方式。既复用方法,又共享共同属性。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Cindy", "Michael"];
}

Person.prototype = {
    constructor: Person,
    sayName: function() {
        alert(this.name);
    }
}

var person1 = new Person("Cindy282694", 4, "coder");
var person2 = new Person("MirrorAvatar", 3, "coder");

person1.friends.push("Michael282694");
console.log(person1.friends);  //"Cindy,Michael,Michael282694"
console.log(person2.friends);  //"Cindy,Michael"

person1.friends === person2.friends;  //false
person1.sayName === person2.sayName;  //true
 

这种构造函数与原型混成的模式,是目前在ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式。

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

var friend = new Person("MirrorAvatar", 3, "coder");
friend.sayName();  //MirrorAvatar
 
6.寄生构造函数模式

这个模式其实和第一个模式“工厂模式”一样,不信你看。

通常,在前述的几种模式都不适用的情况下,可以使用寄生(parasitic)构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。

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("MirrorAvatar", 2, "coder");
friend.sayName();
 

特点:

首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。

7.稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this和new),或者在防止数据被其他应用程序(如Mashup程序)改动时使用。

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

function Person(name, age, job) {

    //创建要返回的对象
    var o = new Object();

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

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

    //返回对象
    return o;
}

var friend = Person("MirrorAvatar", 3, "coder");
friend.sayName();  //"MirrorAvatar"
 

特点: 1. 没有new操作符 2. 没有this

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值