首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 函数式vs.面向对象:响应未知和不确定
02 | 如何通过闭包对象管理程序中状态的变化?
03 | 如何通过部分应用和柯里化让函数具象化?
04 | 如何通过组合、管道和reducer让函数抽象化?
05|map、reduce和monad如何围绕值进行操作?
06 | 如何通过模块化、异步和观察做到动态加载?
07 | 深入理解对象的私有和静态属性
08|深入理解继承、Delegation和组合
09|面向对象:通过词法作用域和调用点理解this绑定
10|JS有哪8种数据类型,你需要注意什么?
11|通过JS引擎的堆栈了解闭包原理
12|JS语义分析该用迭代还是递归?
13 | JS引擎如何实现数组的稳定排序?
14 | 通过SparkPlug深入了解调用栈
15 | 如何通过哈希查找JS对象内存地址?
16 | 为什么环形队列适合做Node数据流缓存?
17 | 如何通过链表做LRU/LFU缓存?
18 | TurboFan如何用图做JS编译优化?
19 | 通过树和图看如何在无序中找到路径和秩序
20 | 算法思想:JS中分治、贪心、回溯和动态规划
21 | 创建型:为什么说Redux可以替代单例状态管理
22|结构型:Vue.js如何通过代理实现响应式编程
23 | 结构型:通过jQuery看结构型模式
24 | 行为型:通过观察者、迭代器模式看JS异步回调
25 | 行为型:模版、策略和状态模式有什么区别?
26|特殊型:前端有哪些处理加载和渲染的特殊“模式”?
27|性能:如何理解JavaScript中的并行、并发?
28|性能:通过Orinoco、Jank Busters看垃圾回收
29|网络:从HTTP/1到HTTP/3,你都需要了解什么?
30|安全:JS代码和程序都需要注意哪些安全问题?
31|测试(一):开发到重构中的测试
32|测试(二):功能性测试
33|测试(三):非功能性测试
34|静态类型检查:ESLint语法规则和代码风格的检查
35|Flow:通过Flow类看JS的类型检查
36|包管理和分发:通过NPM做包的管理和分发
37|编译和打包:通过Webpack、Babel做编译和打包
38|语法扩展:通过JSX来做语法扩展
39|Polyfill:通过Polyfill让浏览器提供原生支持
40|微前端:从MVC贫血模式到DDD充血模式
41|大前端:通过一云多端搭建跨PC/移动的平台应用
42|元编程:通过Proxies和Reflect赋能元编程
当前位置:
首页>>
技术小册>>
JavaScript进阶实战
小册名称:JavaScript进阶实战
### 08|深入理解继承、Delegation(委托)和组合 在JavaScript的世界里,对象之间的关系构建是编程中不可或缺的一部分,它们不仅决定了代码的结构,还直接影响到程序的扩展性、可维护性和性能。本章将深入探讨JavaScript中的三种核心对象关系模式:继承、Delegation(委托)和组合,解析它们各自的原理、应用场景、优缺点,并通过实例展示如何在实践中灵活运用。 #### 一、继承(Inheritance) ##### 1.1 继承的基本概念 继承是面向对象编程(OOP)中的一个核心概念,它允许我们定义一个类(或构造函数/对象字面量)来继承另一个类(或对象)的属性和方法。继承的目的主要是代码复用和扩展功能。在JavaScript中,虽然语言本身是基于原型的(prototype-based),但通过一些设计模式,如借用构造函数(constructor stealing)、原型链(prototype chain)或ES6引入的`class`关键字及其`extends`关键字,我们仍然可以实现类似传统面向对象语言中的继承机制。 ##### 1.2 原型链继承 原型链继承是JavaScript中最常见的继承方式之一。当一个对象尝试访问其属性或方法时,如果该对象自身没有这些属性或方法,JavaScript引擎会沿着对象的`__proto__`(内部属性,指向其原型对象)继续查找,直到找到为止或到达原型链的末端(通常是`Object.prototype`,其`__proto__`为`null`)。 ```javascript function Parent(name) { this.name = name; } Parent.prototype.sayHello = function() { console.log(`Hello, my name is ${this.name}`); }; function Child(name, age) { Parent.call(this, name); // 借用构造函数实现属性继承 this.age = age; } Child.prototype = Object.create(Parent.prototype); // 原型链继承 Child.prototype.constructor = Child; // 修复构造函数指向 Child.prototype.sayAge = function() { console.log(`I am ${this.age} years old.`); }; const child = new Child('Alice', 10); child.sayHello(); // Hello, my name is Alice child.sayAge(); // I am 10 years old. ``` ##### 1.3 ES6 Class继承 ES6引入了`class`关键字和`extends`关键字,使得继承的语法更加直观和接近传统面向对象语言的风格。 ```javascript class Parent { constructor(name) { this.name = name; } sayHello() { console.log(`Hello, my name is ${this.name}`); } } class Child extends Parent { constructor(name, age) { super(name); // 调用父类的constructor this.age = age; } sayAge() { console.log(`I am ${this.age} years old.`); } } const child = new Child('Bob', 12); child.sayHello(); // Hello, my name is Bob child.sayAge(); // I am 12 years old. ``` ##### 1.4 继承的优缺点 - **优点**:代码复用,通过继承可以快速创建具有相似属性和方法的对象。 - **缺点**:继承可能导致紧耦合,即子类过度依赖父类的实现细节,增加了维护难度和扩展的复杂性。同时,过深的继承层次会导致“脆弱的基类”问题,即修改基类可能影响到所有继承它的子类。 #### 二、Delegation(委托) ##### 2.1 委托的概念 委托是一种设计模式,它通过在对象之间建立一种代理关系,使得一个对象(委托者)可以调用另一个对象(受托者)的方法或访问其属性,而不需要继承受托者的类或原型。委托强调了对象之间的合作而非继承关系,增强了代码的灵活性和解耦性。 ##### 2.2 实现方式 委托可以通过简单地将受托者的引用作为委托者的一个属性来实现。 ```javascript const sayHelloBehavior = { sayHello(name) { console.log(`Hello, ${name}!`); } }; const person = { name: 'Charlie', greet: function(behavior) { behavior.sayHello(this.name); } }; person.greet(sayHelloBehavior); // Hello, Charlie! ``` 在上面的例子中,`person`对象通过其`greet`方法委托了`sayHelloBehavior`对象来执行问候操作,两者之间没有继承关系。 ##### 2.3 委托的优点 - **解耦**:委托减少了对象之间的耦合度,使得系统更加灵活和易于维护。 - **复用**:委托可以轻松地复用代码,特别是在多个对象需要共享相同行为时。 - **灵活性**:委托可以在运行时动态地改变受托者,从而提供更高的灵活性。 #### 三、组合(Composition) ##### 3.1 组合的概念 组合是另一种用于构建复杂对象的方式,它通过将对象作为其他对象的属性(或成员)来实现。与继承不同,组合表示的是“有一个”(has-a)的关系,而不是“是一个”(is-a)的关系。 ##### 3.2 实现方式 组合通常通过将一个或多个对象作为另一个对象的属性来实现。 ```javascript function Engine() { this.horsepower = 150; } Engine.prototype.start = function() { console.log('Engine started!'); }; function Car() { this.engine = new Engine(); // 组合关系 } Car.prototype.go = function() { this.engine.start(); // 调用组合对象的方法 console.log('Car is moving!'); }; const myCar = new Car(); myCar.go(); // Engine started! Car is moving! ``` ##### 3.3 组合的优缺点 - **优点**: - **更好的封装**:组合允许我们更好地封装对象的行为和状态。 - **灵活性**:可以动态地改变对象的组成部分。 - **减少依赖**:降低了对象之间的依赖关系,提高了系统的可维护性。 - **缺点**: - 相对于继承,组合可能需要更多的代码来建立对象之间的关系。 #### 四、总结与比较 继承、委托和组合各有其适用场景和优缺点。继承主要用于实现“是一个”的关系,通过复用代码来减少重复;委托强调对象之间的合作,通过动态行为实现解耦;组合则通过“有一个”的关系构建复杂对象,提高了系统的灵活性和可维护性。 在实际开发中,应根据具体需求和场景灵活选择使用哪种方式。通常,组合是更加推荐的做法,因为它能够提供更清晰、更灵活的对象关系,减少不必要的耦合和依赖。然而,在某些特定情况下,继承或委托也可能是更合适的选择。 通过深入理解这三种对象关系模式,我们可以更加灵活地设计JavaScript应用程序的架构,编写出更加高效、可维护的代码。
上一篇:
07 | 深入理解对象的私有和静态属性
下一篇:
09|面向对象:通过词法作用域和调用点理解this绑定
该分类下的相关小册推荐:
Flutter核心技术与实战
编程入门课:Javascript从入门到实战
Javascript重点难点实例精讲(一)
Javascript编程指南
剑指javascript-ES6
javascript设计模式原理与实战
经典设计模式Javascript版
WebSocket入门与案例实战
Javascript-ES6与异步编程
npm script实战构建前端工作流
深入学习前端重构知识体系
JavaScript面试指南