前面所说,简单的创建对象就是字面量创建和new Object()创建,一个代码的麻烦冗余,一个不像一个整体。所以可以推荐几种方式创建!
1.工厂模式
工厂模式,听起来就是一个工厂加工成自己需要的东西。将创建的对象封装在一个函数中(工厂函数),然后创建一个实例传递参数,就像工厂成批出产一样。
//将创建对象的代码封装在一个函数中
function createPerson(name, age, gender) {
var person = new Object();
person.name = name;
person.age = age;
person.gender = gender;
person.sayName = function () {
console.log(this.name);
}
return person;
}
//利用工厂函数来创建对象
var person1 = createPerson("zhangsan", 18, 'male');
var person2 = createPerson("lisa", 20, 'female');
这个缺点也很明显, 工厂函数中直接用person.构造属性,导致了对象类型的单一化。就是说,实例对象时,只能用preson(人)类型的,如果我想用dog(狗)、apple(苹果)等就不适用了。
2.构造函数模式
为了解决工厂模式的缺点,我们研究了一个构造函数模式,自定义构造函数。
// 自定义构造函数
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function () {
console.log(this.name);
}
}
var person1 = new Person('zhangsan', 29, 'male');
var person2 = new Person('lisa', 19, 'female');
person1.sayName(); // zhangsan
person2.sayName(); // lisa
工厂函数的preson.创建属性变成了this.(指向新对象),也没有了return。这时候要用new操作符来创建实例对象了,形成了一个新的对象,也就是this的指向了,解决了工厂模式对象类型单一化缺点。
温馨提醒:任何函数只要使用 new 操作符调用就是构造函数,而不使用 new 操作符调用的函数就是普通函数 。如:
Person("lisa", 27, "female");
window.sayName(); // lisa
这个构造函数也存在一定的缺点。如果想要访问sayname属性,多个实例创建后:一、如果在构造函数内部,每个实例都要再次创建sayname属性函数,没有必要;二、如果在构造函数外部,有很多个类型的像sayname的属性函数,容易造成全局混乱和全局污染。
3.原型模式
每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。
function Person() { }
Person.prototype.name = "zhangsan";
Person.prototype.age = 29;
Person.prototype.gender = "male";
Person.prototype.sayName = function () {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "lisa";
console.log(person1.name); // lisa,来自实例
console.log(person2.name); // zhangsan,来自原型
person1 的 name 属性遮蔽了原型对象上的同名属性,不会修改属性只是在上面盖了一层,覆盖住了。JavaScript 引擎会问:“person2 实例有 sayName 属性吗?”答案是没有。然后,继续搜索并问:“person2 的原型有 sayName 属性吗?”答案是有。于是就返回了保存在原型上的这个函数。 一层层往上找。
原型函数也有一定的缺点,弱化了向构造函数传递初始化参数的能力,会导致所有实例默认都取得相同的属性值 。最大的缺点就是,原型上的所有属性是在实例间共享的,意味着多个实例情况下,只要有一个属性改变了,那其他实例的属性也跟着改变了。
4.组合模式
这个时候啊,人们就会发现,能不能把构造函数和原型函数组合在一起,让代码冗余减少,不造成全局混乱,结构形成一个整体,而且属性共享的同时修改自身属性不影响其他实例,于是就形成了组合模式。
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
this.firends = ['zhangsan', 'lisa'];
}
Person.prototype = {
constructor: Person,
sayName: function () {
console.log(this.name);
}
};
var p1 = new Person('shen', 22, 'male');
var p2 = new Person('kun', 23, 'male');
p1.firends.push('robin');
console.log(p1.firends); // [ 'zhangsan', 'lisa', 'robin' ]
console.log(p2.firends); // [ 'zhangsan', 'lisa' ]
console.log(p1.firends === p2.firends); // false
console.log(p1.sayName === p2.sayName); // true
构造函数用于定义实例属性,原型模式用于定义方法和共享属性。 就是分工明确,构造函数实例分开,原型创造共享属性,确实不错!!!
总结:虽然组合模式是最好的,但是也不是一定要使用它。在实际情况中,我们可以根据情况使用适合的方法创建对象。