本文系统梳理JavaScript中创建对象的九种核心方式,包括Object构造函数、对象字面量、工厂模式、构造函数模式、原型模式及其变体、组合模式、动态原型模式、寄生构造函数模式和稳妥构造函数模式。深入解析每种模式的实现原理、优缺点及适用场景,帮助开发者根据实际需求选择最佳对象创建策略,避免内存浪费与原型共享陷阱。
本文汇总了JavaScript中创建对象的九种主要方法,供读者参考学习。
基础对象创建方式:Object与字面量
【1】使用Object构造函数 [缺点]通过同一接口创建多个对象时,会产生大量重复代码。
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function(){
alert(this.name);
}
【2】使用对象字面量 [缺点]通过同一接口创建多个对象时,会产生大量重复代码。
var person = {
name: "Nicholas",
age : 29,
job: "Software Engineer",
sayName: function(){
alert(this.name);
}
};
工厂模式与构造函数模式
【3】工厂模式:该模式抽象了创建具体对象的过程。鉴于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('Nicholas',29,'software Engineer');
var person2 = createPerson('greg',27,'doctor');
【4】构造函数模式:该模式没有显式地创建对象,而是直接将属性和方法赋给this对象,且没有return语句。 [缺点]每个方法都要在每个实例上重新创建一遍。
function Person(name,age,job){
this.name = name;
this.age = age;
this.jog = job;
this.sayName = function(){
alert(this.name);
};
//与声明函数在逻辑上是等价的
//this.sayName = new Function('alert(this.name)');
}
var person1 = new Person("Nicholas",29,"software Engineer");
var person2 = new Person("Greg",27,"doctor");
【4.1】构造函数拓展模式:将函数定义转移到构造函数外部。 [缺点1]在全局作用域中定义的函数实际上只能被某个对象调用,这使得全局作用域名不副实。 [缺点2]若对象需要定义很多方法,就要定义很多全局函数,导致自定义引用类型失去封装性。
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
var person = new Person('小火柴','20','student')
person.sayName();
console.log(Person);
原型模式及其优化方案
【5】原型模式:我们创建的每个函数都有一个prototype(原型)属性,该属性是一个指针,指向一个对象。这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,prototype就是通过调用构造函数而创建的对象实例的原型对象。
function Person(){
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
}
}
var person1 = new Person();
person1.sayName();//"Nicholas"
var person2 = new Person();
person2.sayName();//"Nicholas"
alert(person1.sayName == person2.sayName);//true
【5.1】更简单的原型模式:为了减少不必要的输入,也为了从视觉上更好地封装原型的功能,使用一个包含所有属性和方法的对象字面量来重写整个原型对象。 [缺点]以这种方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true,默认情况下原生的constructor属性是不可枚举的。
function Person(){};
Person.prototype = {
constructor : Person,
name: "Nicholas",
age: 29,
job: "software Engineer",
sayName : function(){
alert(this.name);
}
};
【5.2】解决enumerable问题的原型模式
function Person(){};
Person.prototype = {
name: "Nicholas",
age: 29,
job: "software Engineer",
sayName : function(){
alert(this.name);
}
};
Object.defineProperty(Person.prototype,"constructor",{
enumerable : false,
value : Person
});
[原型模式缺点1]重写原型对象切断了现有原型与已存在对象实例之间的联系,它们引用的仍是最初的原型。
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
[原型模式缺点2]引用类型属性的共享性问题突出。
function Person(){}
Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "Software Engineer",
friend : ["shelby","Court"],
sayName: function(){
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends);//["shelby","Court","Van"];
alert(person2.friends);//["shelby","Court","Van"];
alert(person1.friends === person2.friends);//true
高级创建模式:组合、动态原型与寄生模式
【6】组合模式:组合使用构造函数模式和原型模式是创建自定义类型的最常见方式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。这种混成模式还支持向构造函数传递参数,是定义引用类型的一种默认模式。
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("Nicholas",29,"Software Engineer");
var person2 = new Person("Greg",27,"Doctor");
person1.friends.push("Van");
alert(person1.friends);// ["shelby","Court","Van"];
alert(person1.friends);// ["shelby","Court"];
alert(person1.friends === person2.friends);//false
alert(person1.sayName === person2.sayName);//true
【7】动态原型模式:把所有信息都封装在构造函数中,通过在构造函数中初始化原型(仅在必要情况下),既保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个存在的方法是否有效,来决定是否要初始化原型。 [注意]使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新实例之间的联系。
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("Nicholas",29,"Software Engineer");
friend.sayName();
【8】寄生构造函数模式:创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。
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("Nicholas",29,"Software Engineer");
friend.sayName();//"Nicholas"
【寄生构造函数模式应用】创建一个具有额外方法的特殊数组。由于不能直接修改Array构造函数,因此可以使用这个模式。
function SpecialArray(){
//创建数组
var values = new Array();
//添加值
values.push.apply(values,arguments);
//添加方法
values.toPipedString = function(){
return this.join('|');
};
//返回数组
return values;
}
var colors = new SpecialArray("red","blue","green");
alert(colors.toPipedString());//"red|blue|green"
稳妥构造函数模式
【9】稳妥构造函数模式:所谓稳妥对象指没有公共属性,而且其方法也不引用this的对象。稳妥对象最适合在一些安全环境中(这些环境会禁止使用this和new)或者在防止数据被其他应用程序改动时使用。
function Person(name,age,job){
//创建要返回的对象
var o = new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
};
//返回对象
return o;
}
//在稳妥模式创建的对象中,除了使用sayName()方法之外,没有其他方法访问name的值
var friend = Person("Nicholas",29,"Software Engineer");
friend.sayName();//"Nicholas"
以上就是javascript创建对象的九种方式,希望对大家的学习有所帮助。