js 对象结构(javascript九大对象)

JavaScript 中构造大型对象系统有多种方法,包括原型继承、类继承和直接创建对象等方法,当你面对需要管理大量数据和逻辑的应用程序时,一个良好的对象系统实现方案可以使你的代码更加易于维护和扩展。
类复制
在早期构建 JavaScript 系统的时候,用的是类复制的方法。类复制是指在生成子类实例时,子类实例的构造逻辑会传入 this 引用,从而用以复制父类中的方法。
在子类内部通过再次引用父类中的方法,后者覆盖子类中的同名成员,整个构造过程都是在不断地从类构造器向this引用复制成员,因此被称为类复制。
通过类复制的方式,我们不仅可以修改这些复制的成员,也可以通过复制具有所有父类的方法来构建复杂的对象系统。
// 父类function Animal(name) {  this.name = name;}Animal.prototype.sleep = function() {  console.log(\\\"动物正在睡觉\\\");};// 子类function Cat(name, color) {  Animal.call(this, name); // 调用父类构造函数复制属性  this.color = color;}
// 使用 Object.assign() 方法复制父类的方法Object.assign(Cat.prototype, Animal.prototype);
// 子类中可以覆盖父类的方法Cat.prototype.sleep = function() { console.log(\\\"猫正在打盹\\\");};
如果我们现在多个子类中增加一个功能,则应该修改父类方法,以使得子类具有该功能的行为。这样一来,根据类的定义,由于父类有了该功能,所有的子类实例都具有了这种行为。
但当我们在给父类构造器增加「方法」时,也就意味着每次创建这个构造器的实例,都需要为实例初始化这个新方法,在这种实现方式下,会构造多个实例,并且它们的方法并不是同一个函数。
子类复制会构造多个实例,访问任何成员都不必回溯原型链,这样访问效率较高,但也带来新的问题,每次生成实例的时候都会重复创建子类并不需要的方法。
原型继承
原型继承是构建对象系统中最常用也是最经典的方法,它是 JavaScript 中最传统的继承方式之一,通过使用原型链来创建对象之间的继承关系。通过在原型链中使用委托来确保继承链的构造过程是唯一的途径,这是一种基于原型继承的基本方法。
// 父类function Animal(name) {  this.name = name;}Animal.prototype.sleep = function() {  console.log(\\\"动物正在睡觉\\\");};// 子类function Cat(name, color) {  this.name = name;  this.color = color;}
// 子类的原型对象指向父类的实例Cat.prototype = new Animal();
Cat.prototype.catchMouse = function() { console.log(\\\"猫正在抓老鼠\\\");};
// 子类中可以覆盖父类的方法Cat.prototype.sleep = function() { console.log(\\\"猫正在打盹\\\");};
然而,原型继承也存在一些缺陷,例如在维护构造函数应用和外部原型链之间平衡的难度,以及缺乏直接调用父类方法的机制。这种方法可能会导致时间和空间的权衡,例如在访问邻近成员时速度较快,而访问不存在的成员时较慢。
在大型对象系统中,实现功能时总是倾向于向「基类」靠拢,需求也促使基类的逻辑变得更重。这正是原型继承在处理复杂需求时可能不擅长的地方。
类继承
类继承既是对原型继承的增强,也是一种再实现。
在 JavaScript 种的类,本质上是用于描述对象,其 extends 声明,则是在描述继承关系。
// 父类class Animal {  constructor(name) {    this.name = name;  }
eat() { console.log(\\\"动物正在吃食物\\\"); }
sleep() { console.log(\\\"动物正在睡觉\\\"); }}
// 子类class Cat extends Animal { constructor(name, color) { super(name); // 调用父类构造函数抄写属性 this.color = color; }
catchMouse() { console.log(\\\"猫正在抓老鼠\\\"); }
// 子类中可以覆盖父类的方法 sleep() { console.log(\\\"猫正在打盹\\\"); }}
类继承更加强调类的设计过程,类似于对象的描述者,并且这种描述是强加在对象之上的。这种强制性导致在基于类继承来设计系统时,必须预先考虑某个类是否具有某种属性、方法或特性。如果某个类的成员设计不正确,那么在其子类的接口和实例使用过程中可能会遇到问题,而重构这样的系统的代价是高昂的。
直接创建对象
无论是经典的原型继承还是通过 class 来声明的类继承,都会通过应用运算调用构造函数来创建新对象,包括语言层面的维护和对新实例的修饰。
原型继承本质上是通过复制原型对象来创建新对象,并将原型作为模板进行复制。构造函数和运算等过程对于复制原型对象来说是唯一的附加效果。
对于原型继承来说,构造新对象的过程中可以使用一种简单的方法,例如 Object.create(),它将构造函数从对象创建过程中剥离出去,使得新对象变成了简单的继承自原型对象的对象,不再需要构造函数这一层语义。
    // 创建对象const animal = {  name: \\\"动物\\\",  eat() {    console.log(\\\"动物正在吃食物\\\");  },  sleep() {    console.log(\\\"动物正在睡觉\\\");  }};
    const cat = Object.create(animal); // 创建 cat 对象,并将 animal 对象设置为其原型cat.name = \\\"猫\\\";cat.color = \\\"黄色\\\";cat.catchMouse = function() { console.log(\\\"猫正在抓老鼠\\\");};// 子类中可以覆盖原型对象的方法cat.sleep = function() { console.log(\\\"猫正在打盹\\\");};
    在选择继承方式时,需要考虑成员访问效率和内存占用两个方面。经典的原型继承方式将原型复制为新对象,构造函数和运算过程对复制原型的影响较大。
    原型继承的机制决定了我们不能单纯依赖原型继承,因为引用类型和基本类型在数据上的表现有差异。因此,对于更基础、稳定、通用的对象体系,倾向于采用类继承,以减轻在业务逻辑和最终实现上的逻辑负担。这通常需要更深层次、更细粒度的继承关系,同时也需要一种标准化的扩展与原生系统的方法,以满足系统设计的需求。
    在选择继承方式时,对于大型系统,应采用基类的思路,利用继承关系的确定性和支持静态语法检测等特性,从而简化开发和业务逻辑的实现,提供足够的系统稳定性。
    而在小型结构和局部使用的情况下,考虑采用原型继承的思路,有优美的实现和高效的性质,同时更深入地理解 JavaScript 中混合不同语言特性的精髓。

    题图生成:Midjourney
    内容优化:ChatGPT
    内容来源:《JavaScript 语言精髓与编程实战》

    原创文章,作者:小道研究,如若转载,请注明出处:https://www.sudun.com/ask/34510.html

    (0)
    小道研究's avatar小道研究
    上一篇 2024年4月12日 下午7:44
    下一篇 2024年4月12日 下午7:46

    相关推荐

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注