创建对象
对象的定义:“无序属性的集合,其属性可以包含基本值、对象或者函数。” 严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。
创建一个 Object 的实例,然后再为它添加属性和方法。
var person = new Object();
person.name = 'xiaoming';
person.age = 25;
person.job = '程序员';
person.sayName = function(){
alert(this.name);
}
person.sayName(); // xiaoming
使用同一个接口创建很多对象,会产生大量的重复代码,为解决这个问题,我们开始使用工厂模式的一种变体。
1、工厂模式
抽象了创建具体对象的过程。考虑到在 ECMAScript 中无法创建类,于是就用一种特定的函数来封装以特定接口创建对象的细节。
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("xiaoming",25,"程序员");
var person2 = createPerson("xiaohong",22,"UI");
person1.sayName() // xiaoming
person2.sayName() // xiaohong
函数 createPerson() 能够根据接受的参数来构建一个包含所有必要信息的 Person 对象。可以无数次的调用这个函数,而每次它都返回一个包含三个属性一个方法的对象。
优点:解决了创建多个相似对象的问题。
缺点:没有解决对象识别的问题(即怎样知道一个对象的类型)。
2、构造函数模式
ECMAScript 中的构造函数可以用来创建特定类型的对象。像 Object 和 Array 这样的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
function Person(name,age,job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name)
}
}
var person3 = new Person("xiaoming",25,"程序员");
var person4 = new Person("xiaohong",22,"UI");
person3.sayName() // xiaoming
person4.sayName() // xiaohong
在上面的例子中,Person() 函数取代了 createPerson() 函数。除了相同的部分外,还存在以下不同之处:
1.没有显示地创建对象;
2.直接将属性和方法赋给了 this 对象;
3.没有 return 语句;
4.此外,函数P是大写。(构造函数始终都以一个大写字母开头,非构造函数以一个小写字母开头。)
优点:将它的实例标识为一种特定的类型。
缺点:每个方法都要在每个实例上重新创建一遍。
3、原型模式
我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的所有属性和方法,都会被构造函数的实例继承,可以把那些不变的属性和方法直接定义在 prototype 对象上。
function Person() {}
Person.prototype.name = "xiaoming";
Person.prototype.age = 25;
Person.prototype.job = "程序员";
Person.prototype.sayName = function() {
alert(this.name);
}
var person5 = new Person();
person5.sayName(); // xiaoming
var person6 = new Person();
person6.sayName(); // xiaoming
alert(person5.sayName() == person6.sayName()); // true
优点:在原型中,所有的属性是被很多实例共享的,这种共享对于函数非常合适。
缺点:它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值,对于包含引用类型值的属性来说,就有很大的问题。
4、组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
构造函数与原型混成的模式,是目前使用最广泛、认同度最高的一种创建定义类型的方法。
function Person(name,age,job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["xiaobai","xiaoli"];
}
Person.prototype = {
sayName: function() {
alert(this.name);
}
}
var person7 = new Person("xiaoming",25,"程序员");
var person8 = new Person("xiaohong",22,"UI");
person7.friends.push("xiaohua");
alert(person7.friends); // ["xiaobai","xiaoli","xiaohua"]
alert(person8.friends); // ["xiaobai","xiaoli"
alert(person7.friends == person8.friends); // false
alert(person7.sayName == person8.sayName); // true
优点:每个实例都会有自己的一份实例属性的副本,但同时又共享着对方的引用,最大限度地节省了内存。
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 person9 = new Person("xiaoming",25,"程序员");
person9.sayName(); // xiaoming
上面的代码,只有在 sayName() 方法不存在的情况下才会将它添加到原型中。这段代码只有在初次调用时才会执行。过后,原型已经初始化完成,不需要再修改。这样对原型所做的修改,能够立即在所有实例中得到反映。
函数中使用 if(typeof this.sayName != “function”) 目的是为了防止创建多个对象时,方法执行多次。
注意:使用动态原型模式时,不能使用对象字面量重写原型,如果在已经创建了实例的情况下重写原型,就会切断现有实例与新原型之间的联系。
6、寄生构造函数模式 ---- 函数有返回值
创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。
从表面上看,这个函数又很像是典型的构造函数。
function Person(name,age,job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(o.name);
}
return o;
}
var person10 = new Person("xiaoming",25,"程序员");
person10.sayName(); // xiaoming
注意:寄生构造函数模式返回的对象与构造函数或者与构造函数的原型属性之间没有关系,也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。建议在可以使用其他模式的情况下,不要使用这种模式。
7、稳妥构造函数模式
所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用 this 和 new ),或者防止数据被其他应用程序改动时使用。
与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数模式之间也没有什么关系,但有两点不同,一是新创建对象的实例方法不引用 this,二是不使用 new 操作符调用构造函数。
function Person(name,age,job) {
var o = new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(o.name);
}
//返回对象
return o;
}
var person11 = new Person("xiaoming",25,"程序员");
person11.sayName(); // xiaoming
注意:在以这种模式创建的对象中,除了使用 sayName() 方法外,没有其他的办法访问 name 的值。