javascript 中对于对象的描述:
ECMA-262 把对象的定义为:“无序属性的集合,其属性可以包含基本值,对象或者函数。”严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。正是因为这样,我们可以把 javascript 中的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数。
javascript 中九种创建对象的方式:
1、原生 Object 构造函数
2、对象字面量表示法
3、工厂模式
4、构造函数模式
5、原型模式
6、组合使用构造函数模式和原型模式
7、动态原型模式
8、寄生构造函数模式
9、稳妥构造函数模式
1、原生 Object 构造函数
具体示例:
// 创建对象
var person = new Object();
// 为对象添加属性
person.name = "luochen";
person.age = 22;
person.job = "student";
// 为对象添加方法
person.sayJob = function(){
alert(this.job);
};
PS:上面的例子创建了一个名为 person 的对象,并为它添加了三个属性和一个方法。
2、对象字面量表示法
具体示例:
// 创建对象
var person = {
name: "luochen",
age: 22,
job: "student",
sayJob: function(){
alert(this.job);
}
};
PS:虽然 Object 构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建多个对象,会产生大量的重复代码。
3、工厂模式
具体示例:
// 自定义创建对象的函数
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayJob = function(){
alert(this.job);
};
return o;
}
var person1 = createPerson("luochen",22,"student");
PS:工厂模式的原理就是自定义一个函数,用函数来封装以特定接口创建对象的细节。这个函数可以接收一些必要参数用来初始化对象,函数内部最后将这个新对象返出,在函数外部将这个调用这个函数的结果赋值给一个新变量。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题。
4、构造函数模式
具体示例:
// 创建自定义构造函数,从而定义自定义对象类型的属性和方法
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayJob = function(){
alert(this.job);
};
}
var person1 = new Person("luochen",22,"student");
PS:构造函数模式和工厂模式的区别:
1、没有显示地创建对象
2、直接将属性和方法赋值给了 this 对象
3、没有 return 语句
以 new 操作符调用构造函数创建对象实际上会经历以下4个步骤:
1、创建一个新对象
2、将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
3、执行构造函数中的代码(为这个新对象添加属性)
4、返回新对象
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。构造函数有个明显的缺点就是每个方法都要在每个实例上重新创建一遍,即不同实例上的同名函数是不相等的。
5、原型模式
具体示例:
// 不在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中
function Person(){
}
Person.prototype.name = "luochen";
Person.prototype.age = 22;
Person.prototype.job = "student";
Person.prototype.sayJob = function(){
alert(this.job);
};
var person1 = new Person();
person1.sayJob();
PS:我们创建的每个函数都有一个 prototype 的原型属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,即 prototype 属性指向的那个对象是所有实例的原型对象。原型模式与构造函数模式最大的不同之处在于:由原型模式创建的对象共享所有的属性和方法(即使用同一组属性和同一个函数)。要格外记住的是:通过原型模式创建的对象实例都不包含属性和方法,但我们可以调用 person1.sayJob() 这是为啥呢???其实这是通过查找对象属性的过程来实现的。每当代码读取某个对象的某个属性或方法时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则会继续搜索指针(实例的内部属性 `Prototype`)指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。
// 原型模式更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象
function Person(){
}
Person.prototype = {
// 用对象字面量来重写整个原型对象,constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),不再指向 Person 函数
constructor: Person,
name: "luochen",
age: 22,
job: "student",
sayJob: function(){
alert(this.job);
}
};
var person1 = new Person();
PS:原型模式省略了给构造函数传递参数这一环节,结果所有的实例在默认情况下都将取得相同的属性值,并且当原型对象上有一个引用类型值的属性时,在一个对象实例上对其值的修改,结果会在所有实例中反映出来。
6、组合使用构造函数模式和原型模式
具体示例:
// 构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["tom","jerry"];
}
Person.prototype = {
constructor: Person,
sayJob: function(){
alert(this.job);
}
};
var person1 = new Person("luochen",22,"student");
var person2 = new Person("shelby",30,"enginner");
person1.friends.push("van");
alert(person1.friends); // "tom,jerry,van"
alert(person2.friends); // "tom,jerry"
alert(person1.friends == person2.friends); // false
alert(person1.sayJob == person2.sayJob); // true
PS:通过组合使用构造函数模式和原型模式创建的对象实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。这种模式是目前使用最广泛,认同度最高的一种创建自定义类型的方法。可以说是用来定义引用类型的一种默认模式。
7、动态原型模式
具体示例:
// 把所有信息都封装在构造函数中,在构造函数中通过检查原型初始化之后应该存在的任何属性和方法是否存在,来决定是否需要初始化原型
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
// 判断是否初始化原型
if(typeof this.sayJob != "function"){
Person.prototype.sayJob = function(){
alert(this.job);
};
}
}
var person = new Person("luochen",22,"student");
person.sayJob();
PS:对于采用这种模式创建的对象,还可以使用 instanceof 操作符确定它的类型。使用动态原型模式时,不能使用对象字面量重写原型,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。
8、寄生构造函数模式
具体示例:
// 这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后在返回新创建的对象
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayJob = function(){
alert(this.job);
};
return o;
}
var person = new Person("luochen",22,"student");
person.sayJob();
PS:这种模式除了使用 new 操作符并把使用的包装函数叫做构造函数之外,跟工厂模式其实是一模一样的。构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个 return 语句,就可以重写调用构造函数时返回的值。这种模式的缺点是不能通过 instanceof 操作符来确定对象的类型,因为返回的对象与构造函数或者与构造函数的原型属性之间没有关系。
9、稳妥构造函数模式
具体示例:
// 稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用 this;二是不使用 new 操作符调用构造函数
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayJob = function(){
alert(name);
};
return o;
}
var person = Person("luochen",22,"student");
person.sayJob();
PS:变量 person 中保存的是一个稳妥对象(指的是没有公共属性,而且其方法也不引用 this 的对象),而除了调用 sayJob() 方法外,没有别的方式可以访问其数据成员。与寄生构造函数模式相似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此 instanceof 操作符对这种对象也没有意义。