尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

JavaScript继承全解析:从原型链到ES6 Class的实战指南

JavaScript继承全解析:从原型链到ES6 Class的实战指南 1. 项目概述为什么“继承”是JavaScript高级编程的基石聊到JavaScript很多人觉得它就是个写写页面交互、做点动态效果的语言。但当你真正深入到复杂应用开发比如大型单页应用、Node.js后端服务或者复杂的组件库时你会发现那些看似简单的概念背后藏着决定代码质量、可维护性和团队协作效率的“魔鬼”。今天要聊的“继承”就是这样一个核心中的核心。简单说继承就是让一个对象或类能够获取并使用另一个对象或类的属性和方法。这听起来像是面向对象编程的老生常谈但在JavaScript这个“万物皆对象”、原型链机制独特的语言里实现继承的姿势五花八门而且每一种姿势背后都牵扯着内存、性能、代码组织等深层考量。理解不透彻写出来的代码要么是过度设计的“类”套“类”难以维护要么是到处复制粘贴重复代码满天飞。这篇文章我会从一个有十多年踩坑经验的老码农视角带你彻底拆解JavaScript的继承。我们不只停留在“怎么用”更要深挖“为什么这么用”以及“在什么场景下该选哪种”。你会看到从最原始的原型链继承到ES6的class语法糖再到为了应对复杂场景而衍生的各种组合模式。我的目标是让你读完不仅能应付面试更能写出清晰、健壮、易于扩展的业务代码。2. 继承的核心原型链机制深度解析要理解JavaScript的继承绕不开它的核心机制——原型链。这可以说是JavaScript最独特也最容易让人迷惑的设计之一。很多人学了__proto__和prototype就晕了其实我们可以把它想象成一个“家族族谱”。2.1 原型与原型链的本质在JavaScript中每个对象除了null都有一个内置属性[[Prototype]]在浏览器中可以通过__proto__访问它指向另一个对象我们称后者为前者的“原型”。当你试图访问一个对象的属性时如果这个对象自身没有引擎就会去它的原型对象上找如果原型对象上也没有就去原型的原型上找这样一层层向上直到找到属性或者到达原型链的尽头null。这条查找路径就是原型链。举个例子我们创建一个数组let arr [1, 2, 3]。arr本身有length属性但它没有push方法。当你调用arr.push(4)时引擎发现arr自身没有push于是沿着arr.__proto__找到了Array.prototype这里定义了push方法于是调用成功。Array.prototype也有自己的__proto__指向Object.prototype最终指向null。// 模拟原型链查找 let arr [1, 2, 3]; console.log(arr.hasOwnProperty(push)); // false自身没有 console.log(arr.__proto__.hasOwnProperty(push)); // true在Array.prototype上 console.log(arr.__proto__.__proto__.hasOwnProperty(toString)); // true在Object.prototype上 console.log(arr.__proto__.__proto__.__proto__); // null原型链终点注意在实际编码中建议使用Object.getPrototypeOf(obj)来获取对象的原型而不是直接操作__proto__因为后者并非ECMAScript标准只是浏览器广泛实现的特性。2.2 构造函数、原型对象和实例的关系这是理解所有继承方式的基础。当我们使用new关键字调用一个函数时会发生以下几件事在内存中创建一个新的空对象。将这个新对象的[[Prototype]]即__proto__链接到构造函数的prototype属性指向的对象。将构造函数内部的this绑定到这个新对象。执行构造函数内部的代码通常是为this添加属性。如果构造函数没有显式返回一个对象则自动返回这个新创建的对象。这里的关键是第2步实例的__proto__指向其构造函数的prototype。构造函数的prototype属性本身也是一个对象我们称之为“原型对象”。这个原型对象上通常定义了该类型所有实例共享的方法。function Person(name) { this.name name; } // 在原型对象上添加方法所有实例共享 Person.prototype.sayHello function() { console.log(Hello, Im ${this.name}); }; let alice new Person(Alice); let bob new Person(Bob); alice.sayHello(); // Hello, Im Alice bob.sayHello(); // Hello, Im Bob console.log(alice.__proto__ Person.prototype); // true console.log(alice.sayHello bob.sayHello); // true共享同一个方法这种设计非常巧妙。属性如name定义在构造函数内部成为每个实例独有的“实例属性”。而方法如sayHello定义在原型对象上是所有实例共享的“原型方法”节省了内存。当我们想实现继承时本质上就是要让“子类”的实例能够访问“父类”原型对象上的方法这就是所有继承模式的出发点。3. 六种经典继承模式从原理到实战踩坑理解了原型链我们就可以来逐一剖析JavaScript中实现继承的几种主要方式。每种方式都有其特定的实现逻辑、优缺点和适用场景。我会结合大量代码示例和实际开发中的教训来讲解。3.1 原型链继承最直接也最坑爹的方式这是最直观的继承方式让子类的原型对象等于父类的一个实例。function Parent() { this.names [kevin, daisy]; } Parent.prototype.sayName function() { console.log(this.names); }; function Child() {} // 核心子类原型指向父类实例 Child.prototype new Parent(); let child1 new Child(); child1.names.push(yayu); console.log(child1.names); // [kevin, daisy, yayu] let child2 new Child(); console.log(child2.names); // [kevin, daisy, yayu] 问题出现优点实现极其简单子类能访问父类原型上的方法如sayName。致命缺点引用类型属性被所有实例共享如上例所示Parent构造函数中的names数组成为了Child.prototype的一个属性。所有Child实例访问的都是同一个数组修改一个会影响所有这几乎从来都不是我们想要的行为。无法向父类构造函数传参在创建子类实例时无法向父类的构造函数传递参数因为父类实例化new Parent()发生在定义子类原型的时候远早于创建子类实例。实操心得原型链继承在实际业务开发中几乎不会被单独使用因为它带来的共享问题太严重了。但它作为理解其他复合继承方式的基础其概念必须吃透。3.2 构造函数继承经典继承解决属性共享问题为了解决原型链继承的引用属性共享问题我们可以使用“盗用构造函数”技术。核心是在子类构造函数内部调用父类构造函数但通过call或apply方法将子类实例的this绑定过去。function Parent(name) { this.name name; this.colors [red, blue, green]; } Parent.prototype.sayName function() { console.log(this.name); }; function Child(name, age) { // 核心在子类构造函数中“借用”父类构造函数 Parent.call(this, name); // 第二次调用Parent this.age age; } let child1 new Child(Alice, 10); child1.colors.push(black); console.log(child1.name); // Alice console.log(child1.colors); // [red, blue, green, black] console.log(child1.sayName); // undefined问题出现 let child2 new Child(Bob, 12); console.log(child2.colors); // [red, blue, green] 引用属性独立了优点避免了引用属性共享每个子类实例都有自己独立的父类属性副本。可以在子类中向父类传参如Parent.call(this, name)。致命缺点方法无法继承父类原型对象上定义的方法如sayName对子类实例不可见。因为这种方式根本没有操作原型链子类实例和父类原型之间没有建立联系。所有方法都必须在构造函数中定义无法复用违背了代码复用的初衷。3.3 组合继承扬长避短的经典方案组合继承结合了原型链继承和构造函数继承成为了JavaScript中最常用的继承模式。其思路是使用构造函数继承来继承属性使用原型链继承来继承方法。function Parent(name) { this.name name; this.colors [red, blue, green]; } Parent.prototype.sayName function() { console.log(this.name); }; function Child(name, age) { // 1. 继承属性构造函数继承 Parent.call(this, name); // 第二次调用Parent this.age age; } // 2. 继承方法原型链继承 Child.prototype new Parent(); // 第一次调用Parent // 修正constructor指针指向Child本身 Child.prototype.constructor Child; // 子类自己的原型方法 Child.prototype.sayAge function() { console.log(this.age); }; let child1 new Child(Alice, 10); child1.colors.push(black); console.log(child1.colors); // [red, blue, green, black] child1.sayName(); // Alice child1.sayAge(); // 10 let child2 new Child(Bob, 12); console.log(child2.colors); // [red, blue, green] child2.sayName(); // Bob child2.sayAge(); // 12优点融合了两种模式的优点实例属性独立原型方法共享。子类实例既是Child的实例也是Parent的实例instanceof测试为真。可以向父类构造函数传参。缺点父类构造函数被调用了两次一次在创建子类原型时new Parent()一次在创建子类实例时Parent.call(...)。这导致子类原型对象Child.prototype上有一组父类属性子类实例自身又有一组相同的属性造成了一定的内存浪费实例属性屏蔽了原型上的同名属性。尽管有这个小缺点组合继承在ES5时代仍然是应用最广泛、认可度最高的继承模式因为它足够通用和稳定。3.4 原型式继承Object.create的哲学这种模式不涉及自定义类型构造函数而是直接基于一个现有对象创建新对象。道格拉斯·克罗克福德在2006年提出了一个函数来实现function object(o) { function F() {} F.prototype o; return new F(); }这其实就是ES5中Object.create()方法的原理。它创建了一个临时构造函数F将其原型指向传入的对象o然后返回F的新实例。本质上它实现了一次浅拷贝的原型链委托。let person { name: Nicholas, friends: [Shelby, Court, Van] }; let anotherPerson Object.create(person); anotherPerson.name Greg; anotherPerson.friends.push(Rob); let yetAnotherPerson Object.create(person); yetAnotherPerson.name Linda; yetAnotherPerson.friends.push(Barbie); console.log(person.friends); // [Shelby, Court, Van, Rob, Barbie]适用场景当你只是需要一个对象与另一个对象保持类似的行为而不需要兴师动众地创建构造函数和完整的继承链时。例如创建一个配置对象的多个副本并希望它们共享某些基础配置。缺点和原型链继承一样包含引用类型值的属性会被所有派生对象共享。3.5 寄生式继承增强对象的工厂模式寄生式继承的思路类似于原型式继承但它在创建对象后会以某种方式增强这个对象然后返回。它更像一种模式而非严格的继承。function createAnother(original) { let clone Object.create(original); // 通过调用函数创建一个新对象 clone.sayHi function() { // 以某种方式增强这个对象 console.log(hi); }; return clone; // 返回这个对象 } let person { name: Nicholas, friends: [Shelby] }; let anotherPerson createAnother(person); anotherPerson.sayHi(); // hi优点在主要关注对象而非自定义类型和构造函数的情况下提供了一种简单的复用方式。缺点和构造函数继承类似通过寄生式继承给对象添加函数会导致函数难以复用降低效率。3.6 寄生组合式继承终极的完美方案这是组合继承的优化版旨在解决其“调用两次父类构造函数”的缺点。被道格拉斯认为是引用类型最理想的继承范式。思路是不通过调用父类构造函数来给子类原型赋值而是获得父类原型的一个副本。本质上就是使用寄生式继承来继承父类的原型然后将结果指定给子类的原型。function inheritPrototype(child, parent) { // 1. 创建父类原型的副本原型式继承 let prototype Object.create(parent.prototype); // 2. 修正副本的constructor属性 prototype.constructor child; // 3. 将副本赋值给子类的原型 child.prototype prototype; } // 使用示例 function Parent(name) { this.name name; this.colors [red, blue]; } Parent.prototype.sayName function() { console.log(this.name); }; function Child(name, age) { // 只在这里调用一次父类构造函数继承实例属性 Parent.call(this, name); this.age age; } // 关键使用寄生组合式继承来设置原型继承方法 inheritPrototype(Child, Parent); // 添加子类原型方法 Child.prototype.sayAge function() { console.log(this.age); }; let child new Child(Alice, 10); console.log(child instanceof Child); // true console.log(child instanceof Parent); // true console.log(child.__proto__ Child.prototype); // true console.log(child.__proto__.__proto__ Parent.prototype); // true优点只调用一次父类构造函数避免了在子类原型上创建不必要的、多余的属性。效率更高内存使用更佳。原型链保持不变instanceof和isPrototypeOf()操作符能正常使用。是ES6classextends关键字在ES5下的实现原理。实操心得在ES5时代如果你要写一个需要被深度继承的库或框架寄生组合式继承是首选。虽然代码比组合继承多几行但其性能优势在复杂的继承层次中会体现出来。在ES6普及后我们通常直接使用class语法但了解其底层实现依然是高级程序员的必修课。4. ES6 Class继承语法糖下的本质ES6引入的class和extends关键字让JavaScript的继承写法终于和其他主流面向对象语言看齐了。但必须明白这只是一个语法糖其底层实现仍然是基于原型链的特别是寄生组合式继承。4.1 Class继承的基本语法与原理class Parent { constructor(name) { this.name name; this.colors [red, blue]; } sayName() { console.log(this.name); } static staticMethod() { console.log(I am a static method of Parent); } } class Child extends Parent { constructor(name, age) { // 必须在this之前调用super() super(name); // 相当于 Parent.call(this, name) this.age age; } sayAge() { console.log(this.age); } // 重写父类方法 sayName() { super.sayName(); // 调用父类的sayName方法 console.log(And Im ${this.age} years old.); } } let child new Child(Alice, 10); child.colors.push(green); console.log(child.colors); // [red, blue, green] child.sayName(); // 输出: Alice \n And Im 10 years old. child.sayAge(); // 10 let child2 new Child(Bob, 12); console.log(child2.colors); // [red, blue] 引用属性独立 // 静态方法也被继承 Child.staticMethod(); // I am a static method of Parent console.log(child instanceof Child); // true console.log(child instanceof Parent); // true关键点解析extends关键字建立了Child和Parent之间的原型链关系。Child.prototype.__proto__ Parent.prototype。super关键字在构造函数中super()用于调用父类的构造函数必须在访问this之前调用。在普通方法中super.method()用于调用父类原型上的方法。静态方法的继承class的静态方法也会被继承因为Child.__proto__ Parent。方法的定义类中定义的方法默认都是不可枚举的且被添加到类的prototype属性上。4.2 Class继承与ES5继承的对比特性ES5 组合/寄生组合继承ES6 Class 继承语法函数和原型操作相对繁琐class/extends/super清晰简洁内置继承需要手动设置Child.prototype.constructor自动维护正确的constructor引用静态成员继承需要手动处理Child.__proto__ Parent通过extends自动继承静态方法和属性原型方法枚举性手动添加到原型的方法默认可枚举类中定义的方法默认不可枚举调用父类方法需要显式引用Parent.prototype.method.call(this, ...)使用super.method()更加直观new.target不支持支持可用于判断构造函数如何被调用本质显式的原型链操作语法糖底层仍是原型链寄生组合式核心结论ES6class并没有引入新的继承模型它只是提供了一种更优雅、更易读、更不易出错的语法来编写基于原型的继承代码。Babel等工具在将class语法转译成ES5代码时使用的就是寄生组合式继承。注意事项使用class时一个常见的错误是忘记在子类构造函数中调用super()或者是在使用this之后才调用super()。这会导致引用错误。记住规则在子类的constructor中必须先调用super()才能使用this。5. 继承的进阶应用与设计模式掌握了基础继承方式后我们来看看在实际复杂项目中如何运用这些知识解决更高级的问题。5.1 Mixin混入模式实现多重继承JavaScript本身只支持单根继承一个类只能有一个直接父类。但在某些场景下我们需要一个类具备多个其他类的功能这时就可以使用Mixin模式。Mixin本质上是将一个对象的属性“混合”到另一个对象的原型中。// 一个简单的Mixin函数 function mixin(target, ...sources) { Object.assign(target.prototype, ...sources); } // 定义一些可复用的功能对象 let CanEat { eat(food) { console.log(${this.name} is eating ${food}); this.energy 10; } }; let CanSleep { sleep(hours) { console.log(${this.name} is sleeping for ${hours} hours); this.energy hours * 5; } }; let CanCode { code(language) { console.log(${this.name} is coding in ${language}); this.energy - 5; } }; // 基类 class Person { constructor(name) { this.name name; this.energy 100; } } // 使用Mixin创建“程序员”类具备多种能力 class Programmer extends Person {} mixin(Programmer, CanEat, CanSleep, CanCode); // 或者使用Object.assign在类定义后混入 // Object.assign(Programmer.prototype, CanEat, CanSleep, CanCode); let alice new Programmer(Alice); alice.eat(pizza); // Alice is eating pizza alice.code(JavaScript); // Alice is coding in JavaScript alice.sleep(8); // Alice is sleeping for 8 hours console.log(alice.energy); // 计算后的能量值Mixin的优缺点优点非常灵活可以按需组合功能避免了创建复杂的、深层的类继承层次。缺点如果多个Mixin有同名方法或属性后者会覆盖前者可能引发冲突。同时它破坏了对象的“纯洁性”使得对象的方法来源不那么清晰。在React的早期Mixin曾被广泛用于组件代码复用但由于其固有的问题命名冲突、隐式依赖现在更推荐使用高阶组件HOC或Hooks。5.2 继承与组合的抉择面向对象设计原则“优先使用对象组合而不是类继承”是GoF《设计模式》中的一条重要原则。这在JavaScript中同样适用。继承is-a关系表达的是“是一个”的关系。例如Programmer是一个Person。它强调特化子类是父类的一种更具体的类型。继承的耦合度很高父类的改动很容易影响到子类。组合has-a关系表达的是“有一个”的关系。例如一个Car有一个Engine。它将功能委托给其他对象耦合度低更灵活。// 组合示例使用依赖注入 class Engine { start() { console.log(Engine started); } } class Car { constructor(engine) { this.engine engine; // 组合Car有一个Engine } start() { this.engine.start(); // 委托给Engine对象 console.log(Car is ready to go); } } let myEngine new Engine(); let myCar new Car(myEngine); myCar.start();如何选择当你需要清晰地表达“是一个”的层级关系并且子类确实是父类的一种更特殊形式时使用继承。例如Square继承Rectangle尽管这也有争议SavingsAccount继承BankAccount。当你只是想复用代码或者关系更像是“有一个”、“能做什么”时优先使用组合或Mixin。例如一个User组件需要日志功能可以注入一个logger对象而不是继承一个Loggable类。在JavaScript这种动态语言中组合通常比继承更灵活、更易于理解和维护。React从Mixin转向Hooks正是这一思想的体现。6. 实战中的常见问题与深度排查理论懂了一写就错。下面是我在多年开发中总结的关于继承的典型“坑”和排查技巧。6.1 原型链污染与属性屏蔽这是一个隐蔽但危险的问题。由于原型链是动态查找的如果你不小心修改了原型对象上的引用类型属性所有继承自该原型的实例都会受到影响。function Parent() {} Parent.prototype.sharedData { config: default }; let child1 new Parent(); let child2 new Parent(); console.log(child1.sharedData.config); // default console.log(child2.sharedData.config); // default // 危险操作直接修改原型上的引用属性 child1.sharedData.config modified; console.log(child1.sharedData.config); // modified console.log(child2.sharedData.config); // modified child2也被影响了如何避免对于需要实例独享的引用属性务必在构造函数中初始化而不是放在原型上。谨慎操作共享的原型属性。如果必须修改考虑先进行深拷贝或为当前实例创建一个新对象。使用Object.defineProperty将原型属性设置为只读或通过getter/setter控制访问。6.2 构造函数指向丢失与修复在使用原型链继承包括组合继承时如果不注意子类实例的constructor属性会指向父类构造函数。function Parent() {} function Child() {} Child.prototype new Parent(); // 原型链继承 let instance new Child(); console.log(instance.constructor Child); // false console.log(instance.constructor Parent); // true这是因为instance.__proto__即Child.prototype本身没有constructor属性所以会沿着原型链找到Parent.prototype.constructor即Parent函数。修复方法在重写子类原型后手动修正constructor属性。Child.prototype new Parent(); Child.prototype.constructor Child; // 重要修复constructor指向ES6的class语法会自动处理这个问题。6.3 性能考量深继承链与属性查找原型链查找虽然快但过深的继承链会影响性能。引擎在查找属性时需要逐级向上遍历原型链。// 不推荐的深继承链 class Level1 {} class Level2 extends Level1 {} class Level3 extends Level2 {} // ... 假设有10层 class Level10 extends Level9 {} let obj new Level10(); // 当访问obj上不存在的属性时引擎需要查找10层原型链优化建议保持继承链扁平化。优先使用组合而非深层次的继承。将频繁访问的属性或方法放在链的更低层即子类自身或靠近子类的原型上。对于性能极其关键的代码段可以考虑在方法内部将常用属性缓存到局部变量。6.4 使用instanceof与isPrototypeOf的陷阱这两个操作符都用于检查对象与构造函数或对象在原型链上的关系但有时结果可能出乎意料。function Parent() {} function Child() {} Child.prototype Object.create(Parent.prototype); // 寄生组合式继承 // 注意这里没有调用 Parent.call(this)所以实例没有Parent的属性 let instance new Child(); console.log(instance instanceof Child); // true console.log(instance instanceof Parent); // true (原型链关系存在) console.log(Parent.prototype.isPrototypeOf(instance)); // true // 但是instance可能并不具备Parent的实例属性 // 因为构造函数继承的步骤被省略了关键点instanceof和isPrototypeOf只检查原型链不检查属性。一个对象instanceof Parent为真仅意味着Parent.prototype在对象的原型链上不代表这个对象是通过new Parent()创建的也不代表它拥有Parent构造函数中定义的属性。完整的继承需要同时建立原型链方法继承和调用父类构造函数属性继承。7. 现代JavaScript项目中的继承实践在今天的开发中我们有了ES6class有了模块系统有了各种框架。继承的使用方式也发生了一些变化。7.1 在框架中的继承以React为例在React Class组件时代继承被用来创建自定义组件基类。import React, { Component } from react; // 一个提供通用功能如日志、错误处理的基类 class BaseComponent extends Component { componentDidMount() { console.log(Component ${this.constructor.name} mounted); } // 可以在这里定义一些通用方法 handleError(error) { console.error(Error in component:, error); // 可以统一上报错误 } } // 业务组件继承基类 class MyButton extends BaseComponent { handleClick () { try { // 业务逻辑 } catch (error) { this.handleError(error); // 使用基类方法 } } render() { return button onClick{this.handleClick}Click Me/button; } }然而随着React Hooks的普及组合模式完全取代了继承在逻辑复用中的地位。通过自定义Hook可以更干净、更灵活地复用状态逻辑。// 使用自定义Hook复用逻辑而非继承 function useLogger(componentName) { useEffect(() { console.log(Component ${componentName} mounted); return () console.log(Component ${componentName} unmounted); }, [componentName]); } function MyButton() { useLogger(MyButton); // “混入”日志功能 // ... 组件其他逻辑 }结论在现代前端框架中组合Hooks、HOC、Render Props已经全面取代继承成为代码复用的首选方案。继承更多地用于表达UI组件的自然层级关系如一个Modal组件继承自BaseDialog。7.2 使用TypeScript增强继承的安全性JavaScript是动态弱类型语言继承中的类型错误往往在运行时才暴露。TypeScript通过静态类型检查可以在编译时就发现许多潜在问题。class Animal { name: string; constructor(name: string) { this.name name; } move(distance: number 0) { console.log(${this.name} moved ${distance}m.); } } class Snake extends Animal { constructor(name: string) { super(name); // TS会强制要求调用super } move(distance: number 5) { console.log(Slithering...); super.move(distance); // 类型安全的父类方法调用 } } class Horse extends Animal { constructor(name: string) { super(name); } move(distance: number 45) { console.log(Galloping...); super.move(distance); } } let sam new Snake(Sammy); let tom: Animal new Horse(Tommy); // 多态 sam.move(); // Slithering... \n Sammy moved 5m. tom.move(34); // Galloping... \n Tommy moved 34m. // TypeScript会在编译时检查以下错误 // let wrong new Snake(123); // 错误参数类型不匹配 // sam.move(far); // 错误参数类型不匹配TypeScript的extends、super、protected/private修饰符、抽象类(abstract class)等特性使得面向对象的设计更加严谨和可维护。如果你在大型项目中使用JavaScript强烈建议使用TypeScript它能将很多继承相关的运行时错误扼杀在编译阶段。7.3 面向未来JavaScript继承的新提案JavaScript语言本身也在演进。虽然class语法已经稳定但社区仍在讨论一些新的特性来增强面向对象编程的能力比如装饰器Decorators和类字段Class Fields。类字段与私有字段ES2022正式加入了公共和私有实例字段、静态字段的语法。class Parent { #privateField secret; // 私有字段仅在类内部可访问 publicField open; // 公共实例字段 static staticField shared; // 静态公共字段 getSecret() { return this.#privateField; // 可以访问 } } class Child extends Parent { childField child; tryAccess() { // console.log(this.#privateField); // SyntaxError: 私有字段不可访问 console.log(this.publicField); // 可以访问 console.log(this.getSecret()); // 通过公共方法间接访问 } }私有字段提供了真正的封装子类无法直接访问父类的私有字段这迫使开发者设计更清晰的公共接口。装饰器目前处于Stage 3提案用于修改类及其成员的行为。它可以用来实现自动绑定this、日志、验证等横切关注点以一种声明式、可组合的方式这比通过继承来混入这些功能更加优雅。// 假设有一个autobind装饰器 class MyComponent { autobind handleClick() { // 这里的this会被自动绑定为实例 } }理解这些演进方向能帮助我们在今天写出更面向未来的代码。
返回列表