基础篇|原型链”继承”和class的前世今生

深入探讨ES6中的原型链继承和类在上一篇文章中,我们简单介绍了JavaScript中__proto__的基本概念。
它涉及从一个对象到另一个对象的内部链接,形成所谓的原型链。
本文将延续这个主题,深入探讨JavaScript的原型链继承机制以及ES6中引入的class关键字。
让我们简单回顾一下前面的内容,以确保理解的连贯性。
虽然这里不再重复,但建议不熟悉此内容的读者先阅读上一篇文章,以便更好地理解后面的内容。
__proto__是一个对象内引用其他对象的关联。
它形成了链式结构,这样在查找属性或方法时,可以从一个对象到父对象类逐类查找,直到找到为止。
这种机制本质上定义了一个在查找过程中遍历的“原型链”。
为了更好地理解原型链继承,我们需要打破对类和继承的误解,特别是JavaScript中类和继承的概念。
如果我们仍然停留在关于类和继承的神话中,我们将很难掌握JavaScript中“继承”的本质。
JavaScript中没有直接的类概念。
所谓类就是通过函数原型的特殊属性来模仿的。
在JavaScript中,没有类实例化,只是通过__proto__对象创建指向同一个原型对象的所有对象,从而形成关联。
在JavaScript上下文中讨论继承时,这可能是最令人困惑的部分。
继承通常意味着复制,但在JavaScript中,继承实际上是通过对象之间的关联来实现的,而不是复制。
该函数的原型属性充当了这种关联的桥梁。
类和版本的概念在JavaScript中比较混乱,但更准确的描述是“授权访问”或“关联”。
当您使用new运算符创建对象时,您实际上是在创建绑定到特定原型对象的新对象。
这仍然依赖于对象对齐。
现在,我们来讨论一下原型链的“继承”。
通过前面的讲解,我们已经了解到JavaScript中的继承本质上是一种基于对象关联的机制,而不是传统的类的继承模型。
ES6中提出的类本质上是对原型语法的封装。
下图展示了YDKJS中Foo.prototype和Bar.prototype之间的委托关系,以及对象版本a1和Foo.prototype之间的委托关系。
可以看到,两者的关系类似于类继承,但不同的是Bar.prototype指向Foo.prototype,体现的是委托关系而不是复制。
为了实现上述关联,我们可以使用Object.create()方法。
通过Object.create()创建一个新对象并将其__proto__绑定到指定对象。
核心代码是Bar.prototype=Object.create(Foo.prototype)。
这种方法唯一的缺点是它会创建一个新对象并覆盖原来的Bar.prototype,导致原来的对象被垃圾收集(GC)。
为了解决这个问题,ES6引入了Object.setPrototypeOf()方法,该方法可以可靠地修改对象的__proto__绑定,并为操作提供标准语法。
尽管ES6提供了后端功能,但现代JavaScript引擎在优化属性访问方面存在性能问题。
使用Object.setPrototypeOf修改对象的__proto__会对性能产生严重影响。
了解JavaScript原型机制的局限性有助于优化代码性能。
简而言之,__proto__之间的关联本质上是一种在对象内引用另一个对象的机制。
JavaScript的核心机制是基于对象的实时委托关系。
ES6中的类语法进一步简化了对象创建和方法共享。
类本质上是一个通过new运算符构造和调用的函数,并且可以将共享方法添加到其原型中。
类简化了编码,但您仍然需要了解它们本质上是基于原型的语法糖。
通过这两篇文章的探索,我们可以清楚地解释上图中参考链的复杂性。
希望此内容对您有用。

终于,JavaScript也有了类(class)的概念

类的概念在编程领域已经不再陌生。
它广泛应用于多种编程语言中,为面向对象编程提供了简洁易懂的语法。
在JavaScript中,类的概念之前还没有被实现。
开发人员通常使用“函数”和“原型”来模拟类的行为,实现面向对象编程。
直到ES6(ECMAScript2015)版本的推出,JavaScript才终于有了类的概念。
虽然JavaScript类是基于原型的进一步封装实现,但它们使代码看起来更接近传统面向对象语言的风格,使代码更易于阅读和维护。
类的基本用法包括声明类、使用构造函数、添加属性和方法、创建实例对象等。
声明类的方式比较简单。
使用关键字“class”声明类名,例如“classAnimal{}”。
类体通常包含一个构造函数,用于初始化类的实例。
当类被实例化时,构造函数被调用并默认返回实例对象。
如果没有定义构造函数,系统会自动添加一个空构造函数。
向类添加属性和方法的逻辑与常规对象类似,但更直观。
属性可以在类体中定义,方法用于执行特定的操作。
可以使用new关键字创建实例对象,这样的实例可以访问类中的属性和方法。
类中定义的属性和方法将被所有实例共享,体现了面向对象编程中封装和继承的概念。
静态方法是类中的另一种方法,可以通过类名直接调用,而无需实例化类。
静态方法通常用于提供类级操作,例如类元数据管理或实用函数。
类继承是面向对象编程的关键特性之一,允许子类继承父类的属性和方法。
在JavaScript中,类继承是通过“extends”关键字实现的。
子类可以重用父类的功能,也可以扩展或重写父类的方法以满足特定的需要。
在继承过程中,子类可以通过super关键字访问父类的构造函数、属性和方法。
super类似于对父类的引用,允许子类在构造函数中初始化时调用父类的构造函数,从而保证继承关系的正确性。
类的概念和实现为JavaScript带来了更强大的面向对象编程能力,使开发人员能够以更高效、更清晰的代码结构构建复杂的系统。
通过学习和应用类的概念,开发人员可以提高代码的可维护性和可扩展性,为项目带来更大的价值。