JavaScript面向对象编程中的对象封装模式详解

作者:青柠

时间:2026-10-29

浏览:555

本文深入解析JavaScript中实现对象封装的多种模式,从原始对象字面量到构造函数,再到Prototype模式。重点探讨如何解决内存浪费问题,通过prototype属性共享不变属性和方法,并结合isPrototypeOf、hasOwnProperty及in运算符验证实例与原型的关系,帮助开发者掌握高效的JS面向对象编程技巧。

文章目录

JavaScript对象封装基础与原始模式

JavaScript是一门基于对象的语言,开发中接触的大多数实体均可视为对象。然而,由于语法层面缺乏class关键字,它并非严格意义上的面向对象编程(OOP)语言。若需将属性(property)与方法(method)封装为对象,或从原型对象创建实例,应如何操作? 一、生成对象的原始模式 假设将猫视为一个对象,具备“名字”和“颜色”两个属性。


var Cat = {
    name : '',
    color : ''
  }

基于该原型对象的规范,现需生成两个具体的实例对象。


var cat1 = {}; // 创建一个空对象
    cat1.name = "大毛"; // 按照原型对象的属性赋值
    cat1.color = "黄色";
  var cat2 = {};
    cat2.name = "二毛";
    cat2.color = "黑色";

此方法实现了最基础的封装,即将两个属性整合至单一对象中。但存在两点不足:首先,若需生成多个实例,编码过程繁琐;其次,生成的实例与原型对象之间缺乏显式的关联标识。 二、原始模式的改进 可通过编写函数来消除代码重复现象。


function Cat(name,color){
    return {
      name:name,
      color:color
    }
  }

生成实例对象的过程,实质上等同于调用上述函数。


var cat1 = Cat("大毛","黄色");
  var cat2 = Cat("二毛","黑色");

构造函数模式及其局限性

该方式仍未能解决cat1与cat2之间缺乏内在联系的问题,无法体现它们源自同一原型对象的事实。 三、构造函数模式 为解决从原型生成实例的问题,JavaScript引入了构造函数(Constructor)模式。 构造函数本质为普通函数,但其内部使用了this变量。利用new运算符调用构造函数可生成实例,此时this变量将绑定至该实例对象。 例如,猫的原型对象可定义如下:


 function Cat(name,color){
    this.name=name;
    this.color=color;
  }

随后即可生成实例对象。


 var cat1 = new Cat("大毛","黄色");
  var cat2 = new Cat("二毛","黑色");
  alert(cat1.name); // 大毛
  alert(cat1.color); // 黄色

此时,cat1与cat2将自动拥有constructor属性,指向其对应的构造函数。


alert(cat1.constructor == Cat); //true
  alert(cat2.constructor == Cat); //true

JavaScript还提供了instanceof运算符,用于验证原型对象与实例对象之间的关联关系。


 alert(cat1 instanceof Cat); //true
  alert(cat2 instanceof Cat); //true

四、构造函数模式的问题 尽管构造函数模式实用,但存在内存浪费现象。 假设为Cat对象添加固定属性“type”(种类)及方法eat(吃老鼠),原型对象Cat的定义将变为:


 function Cat(name,color){
    this.name = name;
    this.color = color;
    this.type = "猫科动物";
    this.eat = function(){alert("吃老鼠");};
  }

沿用相同方法生成实例:


 var cat1 = new Cat("大毛","黄色");
  var cat2 = new Cat ("二毛","黑色");
  alert(cat1.type); // 猫科动物
  cat1.eat(); // 吃老鼠

表面看似无误,实则存在重大缺陷。每个实例对象的type属性及eat()方法内容完全一致,每次生成新实例均需重复创建这些内容,导致内存占用增加,既低效也不环保。 能否使type属性与eat()方法仅在内存中生成一次,并让所有实例指向同一内存地址?答案是肯定的。 五、Prototype模式 JavaScript规定,每个构造函数均拥有prototype属性,指向另一个对象。该对象的所有属性与方法,将被构造函数的实例所继承。 因此,可将不变的属性与方法直接定义于prototype对象上:


alert(cat1.eat == cat2.eat); //false

Prototype模式与内存优化

能否使type属性与eat()方法在内存中仅生成一次,且所有实例均指向该内存地址?答案是肯定的。 五、Prototype模式 JavaScript规定,每个构造函数都拥有prototype属性,指向另一个对象。该对象的所有属性和方法,将被构造函数的实例所继承。 因此,我们可以将那些不变的属性和方法,直接定义在prototype对象上:


 function Cat(name,color){
    this.name = name;
    this.color = color;
  }
  Cat.prototype.type = "猫科动物";
  Cat.prototype.eat = function(){alert("吃老鼠")};

随后生成实例。


 var cat1 = new Cat("大毛","黄色");
  var cat2 = new Cat("二毛","黑色");
  alert(cat1.type); // 猫科动物
  cat1.eat(); // 吃老鼠

此时,所有实例的type属性与eat()方法均指向同一内存地址,即prototype对象,从而显著提升运行效率。


 alert(cat1.eat == cat2.eat); //true

原型链验证方法

六、Prototype模式的验证方法 为配合prototype属性,JavaScript定义了一系列辅助方法以协助使用。 6.1 isPrototypeOf() 该方法用于判断特定proptotype对象与实例之间的关系。


 alert(Cat.prototype.isPrototypeOf(cat1)); //true
  alert(Cat.prototype.isPrototypeOf(cat2)); //true

6.2 hasOwnProperty() 每个实例对象均具备hasOwnProperty()方法,用于判断某一属性是本地属性还是继承自prototype对象的属性。


 alert(cat1.hasOwnProperty("name")); // true
  alert(cat1.hasOwnProperty("type")); // false

6.3 in运算符 in运算符可用于判断实例是否包含特定属性,无论该属性是否为本地属性。


 alert("name" in cat1); // true
  alert("type" in cat1); // true

此外,in运算符还可用于遍历对象的所有属性。


 for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

以上即为JavaScript封装的全部内容,希望对各位的学习有所助益。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章