JavaScript创建对象的六种模式详解

作者:代码拾荒

时间:2026-10-30

浏览:634

本文系统梳理JavaScript中创建对象的六种主要模式,包括对象字面量、工厂模式、构造函数模式、原型模式、组合使用构造函数与原型模式以及动态原型模式。文章详细解析了各模式的实现原理、优缺点及适用场景,重点探讨了原型链查找机制、属性共享与隔离问题,以及内存优化策略,帮助开发者根据实际需求选择最优的对象创建方案。

文章目录

JavaScript对象创建模式概览

JavaScript中创建对象的模式汇总

JavaScript创建对象模式:

对象字面量 工厂模式 构造函数模式 原型模式 结合构造函数和原型模式 原型动态模式

面向对象的语言通常具备类的概念,允许通过类创建具有相同属性和方法的多个对象实例。尽管从技术角度看,JavaScript属于面向对象语言,但它并不支持传统的类概念,所有事物均为对象。任何对象都是某种引用类型的实例,由已有的引用类型创建;引用类型既可以是语言内置的原生类型,也可以是开发者自定义的类型。

对象字面量模式

1、对象字面量


var person = {
    name : 'Nicholas';
    age : '22';
    job :"software Engineer"
    sayName: function() {
      alter(this.name);
  }
}

示例中创建了一个名为person的对象,并为其添加了三个属性(name、age、job)以及一个方法(sayName())。其中,sayName()方法用于显示this.name的值,该值在解析时对应person.name。

对象字面量适用于创建单个对象,但其明显缺点在于:若使用同一接口创建大量对象,会导致代码高度重复。

工厂模式

2、工厂模式

工厂模式是软件工程中广为人知的设计模式,它抽象了具体对象的创建过程,通过函数封装以特定接口创建对象的细节。


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=creatPerson("Nicholas",22,"software Engineer");
var person2=creatPerson("Greg",24,"student");

函数creatPerson{}能够根据接收到的参数构建一个包含所有必要信息的Person对象。该函数可被无限次调用,每次调用均返回一个包含三个属性和一个方法的对象。

工厂模型虽然解决了创建多个相似对象的问题,但未能解决对象识别问题(即无法判断一个对象的具体类型)。

构造函数模式

3、构造函数模式


function Person(name,age,job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function() {
    alert(this.name);
  }
}
//通过new操作符创建Person的实例
var person1 = new Person("Nicholas",22,"software Engineer");
var person2 = new Person("Greg",24,"student");
person1.sayName(); //Nicholas
person2.sayName(); //Greg

与工厂模式不同,

不显式创建对象

直接将属性和方法赋值给this对象

不使用return语句

创建Person的新实例必须使用new操作符。调用构造函数的四个步骤如下:

创建一个新对象

将构造函数的作用域赋给新对象(此时this指向该新对象)

执行构造函数中的代码

返回新对象

此示例中创建的所有对象既是Object的实例,也是Person的实例。可以通过instanceof操作符进行验证。


alert(person1 instanceof Object);//true

构造函数模式也存在自身问题:实际上,sayName方法会在每个实例上被重新创建一次。需要注意的是,通过实例化创建的方法并不相等,以下代码可以证明这一点。


alert(person1.sayName == person2.sayName);//false

可以将方法移至构造器外部作为全局函数来解决此问题。


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

然而,在全局下创建的全局函数实际上只能被经由Person创建的实例调用,这显得名不副实;若对象需要定义大量方法,则需定义许多全局函数,缺乏封装性。

原型模式

4、原型模式

JavaScript中创建的每个函数都有一个prototype(原型)属性,它是一个指针,指向一个对象,该对象包含了可以由特定类型的所有实例共享的属性和方法(即让所有对象实例共享其属性和方法)。


function Person() {}
  Person.prototype.name ="Nicholas";
  Person.prototype.age = 22;
  Person.prototype.job = "software Engineer";  
  Person.prototype.sayName(){
    alert(this.name);
  };
 var person1 = new Person();
 person1.sayName(); //Nicholas
alert(person1.sayName == person2.sayName);//true

上述代码执行了以下操作:

定义了一个构造函数Person。Person函数自动获得一个prototype属性,该属性默认仅包含一个指向Person的constructor属性。 通过Person.prototype添加三个属性和一个方法。

创建一个Person的实例,随后在该实例上调用sayName()方法。

以Person构造函数和Person.prototype创建实例的代码为例,展示各对象之间的关系。

以Person构造函数和Person.prototype创建实例的代码为例,展示各对象之间的关系。

JavaScript对象创建模式概览 

图中展示了Person构造函数、Person的原型属性以及Person的两个实例之间的关系。Person.prototype指向原型对象,Person.prototype.constructor指回Person。原型对象中除了包含constructor属性外,还包含后续添加的其他属性和方法。Person的两个实例person1和person2均包含一个内部属性,该属性仅指向Person.prototype。

sayName()方法的调用过程如下:

在person1实例上查找logName()方法,发现该方法不存在,于是追溯至person1的原型。

在person1的原型上查找sayame()方法,找到该方法,于是调用该方法。

基于这一查找过程,我们可以通过在实例上定义原型中的同名属性,来阻止该实例访问原型上的同名属性。需要注意的是,这样做并不会删除原型上的同名属性,仅仅是阻止实例访问。


function Person() {}
  Person.prototype.name ="Nicholas";
  Person.prototype.age = 22;
  Person.prototype.job = "software Engineer";  
  Person.prototype.sayName(){
    alert(this.name);
  };
 var person1 = new Person();
 var person2 = new Person();
 person1.name="Greg"
alert(person1.name) //Greg 来自实例
alert(person2.name) //Nicholas 来自原型

使用delete操作符可以完全删除实例属性。


delete person1.name;
alert(person1.name) //Nicholas 来自原型

使用hasOwnProperty()方法可以检测一个属性是存在于实例还是原型中。


function Person() {}
  Person.prototype.name ="Nicholas";
  Person.prototype.age = 22;
  Person.prototype.job = "software Engineer";  
  Person.prototype.sayName(){
    alert(this.name);
  };
 var person1 = new Person();
 var person2 = new Person();
 alert(person1,hasOwnProperty("name"));//false
 person1.name="Greg"
alert(person1.name) //Greg 来自实例
 alert(person1,hasOwnProperty("name"));//true
alert(person2.name) //Nicholas 来自原型
 alert(person2,hasOwnProperty("name"));//false
 delete person1.name;
alert(person1.name) //Nicholas 来自原型
 alert(person1,hasOwnProperty("name"));//false

下图展示了在不同情况下实例与原型之间的关系。

JavaScript对象创建模式概览

简单的原型语法


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

在上述代码中,constructor属性不再指向Person,通过constructor无法确定对象的类型。可以像下面这样特意将其设置回适当的值。


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

重设constructor属性会导致其[[Enumerable]]特性被设置为true。默认情况下,原生的constructor属性是不可枚举的,可以使用Object.defineProperty()方法来改变这一特性。


Object.defineProperty(Person.prototype,"constructor",{
  enumerable:false,
  value:Person
});

原型中查找值的过程是一次搜索,原型对象所做的任何修改都能从实例上立即反映出来。


var friend=new Person();
Person.prototype.sayHi=function(){
  alert("hi);
}
friend,sayHi();//"hi"(没有问题)

person实例是在添加新方法之前创建的,但仍可访问新添加的方法,原因是实例与原型之间存在松散连接关系。 重写原型对象后的情况


function Person() {}
var friend=new Person();
 Person.prototype={
 name :"Nicholas",
 age : 22,
 job : "software Engineer", 
 sayName:function(){
    alert(this.name);
    }
  };
  friend.sayName();//error

调用friend.sayName()时发生错误的原因是,friend指向的原型中不包含以该字段命名的属性,如下图所示。

JavaScript对象创建模式概览 

原型对象的问题

原型对象省略了为构造函数传递初始化参数这一环节,所有实例在默认情况下都取得相同的属性值。原型模型最大的问题是由其共享本性所导致的。当原型模型包含引用类型的属性时,问题尤为严重。请看下面的示例。


function Person() {}
 Person.prototype={
 constructor:Person,
 name :"Nicholas",
 age : 22,
 job : "software Engineer",  
 friends:["Shelby","Court"],
 sayName:function(){
    alert(this.name);
    }
  };
  var person1=new Person();
  var person2=new Person();
  person1.friend.push("Van");
  alert(person1.friends);//"Shelby,Court,Van"
  alert(person2.friends);//"Shelby,Court,Van"
 alert(person1.friends==person2.friends);//true

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

5、组合使用构造函数模式和原型模式

在组合使用构造函数模式和原型模式中,构造函数用于定义实例属性,原型模型用于定义方法和共享属性。这样每个实例都会有自己的一份实例属性副本,同时也可以共享对方法的引用,从而最大限度地节省内存。


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",22,"software Engineer");
var person2 = new Person("Greg",24,"student");
person1.friend.push("Van");
  alert(person1.friends);//"Shelby,Court,Van"
  alert(person2.friends);//"Shelby,Court"
 alert(person1.friends==person2.friends);//false
 alert(person1.sayName==person2.sayName);//true

动态原型模式

6、动态原型模式

原型动态模式将需要的所有信息都封装到构造函数中,通过if语句判断原型中的某个属性是否存在。若不存在(在第一次调用这个构造函数的时候),执行if语句内部的原型初始化代码。


function Person(name,age) {
  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','22','Software Engineer');//初次调用构造函数,此时修改了原型
var person2 = new Person('amy','21');//此时sayName()方法已经存在,不会再修改原型

推荐阅读:

js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)

以上所述是小编给大家介绍的JavaScript中创建对象的模式,希望对大家有所帮助!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章