很长的一段时间中,Vue 官方都以简单上手作为其推广的重点。这确实给 Vue 带来了非常大的用户量,尤其是最追求需求开发效率, 往往不那么在意工程代码质量的国内中小企业中,Vue 占据的份额极速增长。但是作为开发者自身,我们必须要认清一个重点,简单易用从来不应该在技术选型中占据很大的份额,可维护性才是。
以防万一有的同学实在不看官方文档,我先提一嘴,SFC 就是写 Vue 组件的时候写的.vue文件,这一个文件就是一个 SFC,全称 Single File Component,也即单文件组件。
在开始说我个人的观点之前,我们先来看几个事实:
一是:Vue3 的定义原生支持 JSX,并且 Vue3 源码中有jsx.d.ts来便于使用 JSX。 不知道同学们看到这里会想到什么, 我的第一反应是:社区对于 JSX 的需求声音是不小的,所以会反向推动 Vue3 官方对于 JSX 的支持。
二是:AntDesign 的 vue3 版本,基本全部都是用 JSX 开发的,而且 Vue3 现在官方的 babel-jsx 插件就是阿里的人一开始维护的, 虽然我向来不喜欢阿里系的 KPI 推动技术方式,而且现在的 JSX 语法支持也不是很符合我的期望,但至少在使用 JSX 开发是更优秀的选择这点上,我还是很认可 AntDesign 团队的。
OK,说这些呢,主要是先摆出一些事实作为依据,让有些同学可以不需要拿什么:
这些观点来批斗我,首先我都会从客观的角度来分析为什么,至少是我是能讲出优劣势的理由的。
OK,前言差不多到这里,接下来咱给您分析分析,为什么你应该选择 JSX 来开发 Vue。
其实第一点就已经是杀手了,对于想要使用 TypeScript 来开发 Vue3 应用的同学来说,这简直就是 SFC 无法克服的世界难题。
一句话概括:TypeScript 原生支持 JSX 语法,而基本无望 TS 官方能支持 SFC 的 template 语法。
TS 毫无疑问在前端社区的重要性越来越大,但凡未来对于代码质量有一定要求的前端团队,都应该会选择使用 TS 来进行开发。 而且现在基本上在 NPM 上都能看到包你都能找到对应的 TS 定义,现在使用 TS 开发成本已经只剩下你是不是会 TS 语法了,在这种情况下是否支持 TS 则是开发模式在未来走不走的远的重要原因。
目前 SFC 只能通过shim让 TS 可以引入.vue文件,但是对于所有 SFC 的组件的定义都是一样的:
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, {}, any>
export default component
}
也就是说你引入的 SFC 组件,TS 是不知道这个组件的 Props 应该接收什么的。所以你无法享受到这些 TS 的优势:
当然你会说既然 Vue 官方能开发处 SFC 的语法,自然会支持这些特性。我表示这当然有可能,但是这个难度是非常大的,需要很多方面的支持,甚至可能需要 TS 官方团队愿意协助, 但是我想不到 TS 官方有什么理由来支持 SFC,因为这只是 Vue 自己创建的方言,在其他场景下是没有使用的,TS 是面向全社区的,我觉得他们不会考虑主动来支持 SFC。
那么有同学要问了,JSX 不也是非原生的 JS 语法么,他怎么就能让 TS 官方支持了呢,是不是 FB 和微硬之间有什么 PY 交易?
这就涉及第二点了,JSX 和静态模板的灵活性区别。
很多人弄错了一个问题,就是觉得 SFC 的模板语法和 JSX 是一样的,都是一种别人发明的语法,并不是 JS 原生的。这是事实,但又有一些区别,这个区别主要是体现在对于 JSX 的认知上。
一句话概括:JSX 并没有扩展 JS 的语法,他只是缩略了 JS 的写法!其本质就是 JS 的语法糖
就像 es6 给增加的语法糖,比如
const a=1
const b=2
const obj={ a, b }
// 其实就等价于
const obj={ a: a, b: b }
这种写法并没有扩展 JS 的能力,只是简便了写法,JSX 也是一样的。
JSX 其实就是方法调用,他和 JS 是有一对一对应关系的,我们来看一个例子:
const element=<div id="root">Hello World</div>
这里的 JSX 语法编译之后其实就是:
const element=createElement('div', { id: 'root' }, 'Hello World')
而 JSX 就是这些了,没有什么更多的内容,所以说 JSX 只是方便我们写嵌套的函数调用的语法糖,而其本身没有扩展任何其他的内容。
但是 SFC 就不一样了。
SFC 定义的不仅是语法,更是文件。
SFC 的具体定义是单文件组件,它本身就是把一个文件看作一个单位,所以他的约束性是要大很多的,你必须具有固定的文件结构才能使用 SFC,这做了很多的限制:
我们一点点来讲
这个说实话非常非常不方便,很多时候我们写一个页面的时候其实经常会需要把一些小的节点片段拆分到小组件里面进行复用(如果你现在没有这个习惯可能就是因为 SFC 的限制让你习惯了全部写在一个文件内)。
React 生态中丰富的 css-in-js 方案就是很好的例子,我们可以通过:
const StyledButton=styled('button', {
color: 'red',
})
如果我们这个页面需要使用特定样式的按钮,通过这种方式在页面文件里面封装一下是非常常见的。因为没必要把这个组件拆分出去,他也不是一个可复用的组件,拆分出去了还要多一次import。
Vue 生态基本没有 css-in-js 的成熟方案其实跟这个限制也很有关系。
再来一个例子,比如我们封装了一个 Input 组件,我们希望同时导出 Password 组件和 Textarea 组件来方便用户根据实际需求使用,而这两个组件本身内部就是用的 Input 组件,只是定制了一些 props:
const Input={ ... }
export default Input
export const Textarea=(props)=> <Input multiline={true} {...props} />
export const Password=(props)=> <Input type="password" {...props} />
在 JSX 中可以非常简单地实现,但是如果通过 SFC,你可能就要强行拆成三个文件,另外为了方便,你可能还要增加一个index.js来导出这三个组件,你能想象这多了多少工作量么。
我不知道有多少同学看过 Vue 的 template 编译出来之后的代码,以我的经验来说看过的可能不会超过 50%(乐观估计),建议同学们如果还不了解的,可以去尝试看一下。
为什么要看这个呢?因为你看了之后你会发现,你在 template 里面写的类似 HTMl 的内容,其实跟 HTML 根本没啥关系,他们也会被编译成类似 JSX 编译出来的结果。
{
render(h) {
return h('div', {on: {}, props: {}}, h('span'))
}
}
类似这样的结果,而这里面h函数调用的结果就是一个 VNode,是 Vue 中的节点的基础单元。那么既然这些单元就是一个对象,其实理所当然的,他们是可以作为参数传递的。 也就是说,理论上他们是可以通过props把节点当作参数传递给其他组件的。
这个做法在 React 中非常常见,叫做renderProps,并且其非常灵活:
const Comp=()=> <Layout header={<MyHeader />} footer={<MyFooter />} />
但是因为 SFC 模板的限制,我们很难在 SFC 里面的 props 上写节点:
<template>
<Layout :header="<MyHeader/>"></Layout>
</template>
这样写是不行的,因为 SFC 定义了:header绑定接受的只能是 js 表达式,而<MyHeader/>显然不是。
因为通过 props 传递不行,所以 Vue 才发明了 slot 插槽的概念
虽然我们一直再说 Vue 简单,但是事实上ScopedSlots一度成为新手理解 Vue 的噩梦,很多同学都被这个绕来绕去的作用域整的死去活来。
我们看一个ScopedSlots的例子:
<template>
<Comp>
<template v-slot:scope="ctx">
<div>{{ctx.name}}</div>
</template>
</Comp>
</template>
这里ctx是Comp里面的属性,通过这种方式传递出来,让我们在当前组件可以调用父组件里面的属性。这简直就是理解的噩梦,但是如果用 JSX 实现类似功能就非常简单:
<Comp scope={name=> <div>{name}</div>} />
我们只是给一个叫做scope的 props 传递来一个函数,这个函数接受一个name属性,在Comp里面会调用这个函数并传入name。 简单来说我们传入的就是一个构建节点片段的函数,就是这么简单。
这就是因为 SFC 的模板的限制,导致灵活性不足,Vue 需要去创造概念,创造关键字来抹平这些能力的不足,而创造的概念自然就引入了学习成本。
所以其实我一直不认可 Vue 比 React 好学的说法的,如果你真的认真研究所有用法,并且总是尝试用最合理的方式实现功能,那么 Vue 绝对不会比 React 简单。
这个体现在两个方面,一个是我们定义在全局的一些固定数据如果要在组件内使用的话,就要通过this挂载到组件上。
比如我们缓存了一份城市数据,这种数据基本上是不会改的,所以也没必要挂载到组件上让其能够响应式。但是在 SFC 里面这是做不到的, 因为模板的执行上下文是在编译时绑定。你在模板里面访问的变量,都会在编译时自动绑定到this上,因为模板需要编译,其本身也是字符串不具有作用域的概念。
而这在 JSX 中则不复存在:
const citys=[]
const Comp=()=> {
return citys.map(c=> <div>{c}</div>)
}
另外一个方面则是在组件使用上,在 SFC 中,组件必须事先注册,因为我们在模板里面写的只能是字符串而不能是具体某个组件变量。 那么模板中的组件和真实的组件对象只能通过字符串匹配来实现绑定。这带来了以下问题:
在 JSX 中则没有这些问题,因为 JSX 里面直接使用组件引用作为参数:
const Comp={...}
const App=()=> <Comp />
其实上面能看出来,除了 SFC 本身的问题之外,Vue 使用字符串模板也会带来很多的灵活性问题。 最直接的证据,就是 Vue 使用了directive来扩展功能(当然这不是 Vue 发明的,老早的模板引擎就有类似问题)。
为什么说directive是不得已的选择呢?因为静态模板缺失逻辑处理的能力。我们拿列表循环举例,在 JS 中我们可以非常方便地通过map函数来创建列表:
const list=arr.map(name=> <span key={name}>{name}</span>)
而因为 JSX 本身就是函数调用,所以上面的代码和 JSX 结合起来也非常自然:
const App=()=> (
<div>
<Header />
{arr.map(name=> (
<span key={name}>{name}</span>
))}
</div>
)
上面的例子对应到 JS 如下:
const App=()=>
createElement('div', {}, [
<Header />,
arr.map(name=> createElement('span', { key: name }, name)),
])
这仍然是因为 JSX 只是 JS 的语法糖的原因,所有能在 JS 中实现的在 JSX 里面都能实现。
而 SFC 的模板是基于字符串编译的,其本身就是一段字符串,我们不能直接在模板里面写map来循环节点,(当然我们可以在可以接收表达式的地方写,比如v-on里面)。
那么我们不能循环节点,有需要这样的功能来渲染列表,怎么办呢?就是发明一个标志来告诉编译器这里需要循环,在 Vue 中的体现就是v-for指令。
同学们可能要问了,既然 Vue 能实现v-for,为什么不直接实现表达式循环列表呢?他当然也可以实现,但是他肯定不会这么选,因为成本太高了。 他要这么做就相当于他要实现一个 JS 引擎,而其实里面很多内容又是不必须的,一个v-for其实就能够适用大部分情况了。
但有了v-for就需要v-if,那么后面还会需要其他各种能力,这就是一种方言的产生和发展的过程。
当然指令也不仅仅是 JS 表达式的代替品,其本身也是增加了一些其他能力的,比如它能够让我们更方便地访问 DOM 节点, 但是嘛,我们用框架的理由不就是为了能够尽可能的屏蔽 DOM 操作嘛~
以上就是我对应该选择使用 JSX 还是 SFC 进行开发的分析,其实归根到底 SFC 的问题在于其没有拥抱 JS, 他的语法是自己发明的,他需要有一个 JS 实现的 compiler 来让其最终能在 JS 环境中运行,这本质上就是一种发明, 我们不能否认发明确实有优点,但我们也不能只看有点不看问题,没能拥抱 JS 自然就很难完全复用 JS 社区的优势 而 JS 社区一直在蓬勃发展,好用的工具一直在涌现,而 SFC 想要使用 JS 社区的这些工具还要自己再实现一份,我们可以细数以下 SFC 做了哪些兼容
基本上常用的工具我们都需要等待 Vue 社区或者官方开发了插件之后才能运行。而 JSX 因为有 babel 和 typescript 的官方支持, 基本上所有新的 JS 生态工具原生都是支持的。
在这 Vue3 开始预备发力的阶段,我们还是希望 Vue 社区能够使用更优秀更规范的方式来进行开发, 其实如果我们直接使用 JSX 开发 Vue3,我们会发现很多时候我们都不需要用到emit、attrs这些概念, 甚至如果 Vue3 的 JSX 插件支持,我们甚至能够抛弃slots。
但是因为 Vue3 一定要考虑兼容 Vue2,导致本身潜力很好的 Vue3 总是显得缩手缩脚,这不得不说是一种遗憾。
接标签
定义:从一个网页指向另一个网页的目的地,
有四种类型:
(1)链接覆盖原有的链接
(2)新开一个窗口
(3)图片链接
(4)死链接
html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>链接标签</title>
</head>
<body>
<a href="https://www.baidu.com/"><font color="blue">百度</font></a>
<a href="https://www.jd.com/" target="_blank"><font color="blue">京东</font></a>
<a href="https://www.hao123.com/" target="_blank"><img src="../img/男孩子.png"/></a>
<a href="#" target="_blank">死链接</a>
</body>
</html>
文地址:Understanding Design Patterns in JavaScript
原文作者:Sukhjinder Arora
译者:HelloGitHub-Robert
当启动一个新的项目时候,我们不应该马上开始编程。而是首先应该定义项目的目的和范围,然后列出其功能或规格。如果你已经开始编程或者正在从事一个复杂的项目,则应该选择一个最适合你项目的设计模式。
在软件工程中,设计模式是针对软件设计中常见问题的可重用解决方案。设计模式也是经验丰富的开发人员针对特定问题的最佳实践。它可以被当作编程的模板。
许多工程师要么认为设计模式浪费时间,要么不知道如何恰当的使用设计模式。但如果能正确使用设计模式,则可以帮助你写出更好的可读性更高的代码,并且代码更容易被维护和理解。
最重要的是,设计模式为软件开发人员提供了通用的词汇表。它们能让学习你代码的人很快了解代码的意图。例如,如果你的项目中使用了装饰器模式,那么新的开发可以很快就知道这段代码的作用,从而他们可以将更多精力放在解决业务问题上,而不是试图理解代码在做什么。
我们已经知道了什么是设计模式和它的重要性,下面我们深入研究一下 JavaScript 中的 7 种设计模式。
模块是一段独立的代码,因此我们可以更新模块而不会影响代码的其它部分。模块还允许我们通过为变量创建单独的作用域来避免命名空间污染。当它们与其它代码解耦时,我们还可以在其它项目中重用模块。
模块是任何现代 JavaScript 应用程序不可或缺的一部分,有助于保持代码干净,独立和有条理。在 JavaScript 中有许多方法可以创建模块,其中一种是模块模式。
与其它编程语言不同,JavaScript 没有访问修饰符,也就是说,你不能将变量声明为私有的或公共的。因此,模块模式也可用来模拟封装的概念。
模块模式使用 IIFE(立即调用的函数表达式),闭包和函数作用域来模拟封装的概念。例如:
const myModule = (function() {
const privateVariable = 'Hello World';
function privateMethod() {
console.log(privateVariable);
}
return {
publicMethod: function() {
privateMethod();
}
}
})();
myModule.publicMethod();
由于是 IIFE 因此代码会被立即执行,并将返回对象赋值给了 myModule 变量。由于闭包,即使在 IIFE 完成后,返回的对象仍可以访问 IIFE 内部定义的函数和变量。
因此,IIFE 内部定义的变量和函数对外部是看不见的,从而使其成为 myModule 模块的私有成员。
执行代码后,myModule 变量看起来像下面所示:
const myModule = {
publicMethod: function() {
privateMethod();
}};
因此当我们调用 publicMethod() 时候,它将调用 privateMethod() 例如:
// Prints 'Hello World'
module.publicMethod();
揭示模块模式是 Christian Heilmann 对模块模式的略微改进。模块模式的问题在于,我们必须创建新的公共函数才能调用私有函数和变量。
在这种模式下,我们将返回的对象的属性映射到要公开暴露的私有函数上。这就是为什么将其称为揭示模块模式。例如:
const myRevealingModule = (function() {
let privateVar = 'Peter';
const publicVar = 'Hello World';
function privateFunction() {
console.log('Name: '+ privateVar);
}
function publicSetName(name) {
privateVar = name;
}
function publicGetName() {
privateFunction();
}
/** reveal methods and variables by assigning them to object properties */
return {
setName: publicSetName,
greeting: publicVar,
getName: publicGetName
};
})();
myRevealingModule.setName('Mark');
// prints Name: Mark
myRevealingModule.getName();
这种模式让我们更容易知道哪些函数和变量是公共的,无形中提高了代码的可读性。执行代码后 myRevealingModule 看起来像下所示:
const myRevealingModule = {
setName: publicSetName,
greeting: publicVar,
getName: publicGetName
};
当我们调用 myRevealingModule.setName('Mark') 时,实际调用了内部的 publicSetName。当调用 myRevealingModule.getName() 时,实际调用了内部的 publicGetName 例如:
myRevealingModule.setName('Mark');
// prints Name: Mark
myRevealingModule.getName();
与模块模式相比,揭示模块模式的优势有:
在 ES6 之前,JavaScript 没有内置模块,因此开发人员必须依靠第三方库或模块模式来实现模块。但是自从 ES6,JavaScript 内置了模块。
ES6 的模块是以文件形式存储的。每个文件只能有一个模块。默认情况下,模块内的所有内容都是私有的。通过使用 export 关键字来暴露函数、变量和类。模块内的代码始终在严格模式下运行。
有两种方法可以导出函数和变量声明:
// utils.js
export const greeting = 'Hello World';
export function sum(num1, num2) {
console.log('Sum:', num1, num2);
return num1 + num2;
}
export function subtract(num1, num2) {
console.log('Subtract:', num1, num2);
return num1 - num2;
}
// This is a private function
function privateLog() {
console.log('Private Function');
}
// utils.js
function multiply(num1, num2) {
console.log('Multiply:', num1, num2);
return num1 * num2;
}
function divide(num1, num2) {
console.log('Divide:', num1, num2);
return num1 / num2;
}
// This is a private function
function privateLog() {
console.log('Private Function');
}
export {multiply, divide};
与导出模块相似,有两种使用 import 关键字导入模块的方法。例如:
// main.js
// importing multiple items
import { sum, multiply } from './utils.js';
console.log(sum(3, 7));
console.log(multiply(3, 7));
// main.js
// importing all of module
import * as utils from './utils.js';
console.log(utils.sum(3, 7));
console.log(utils.multiply(3, 7));
// utils.js
function sum(num1, num2) {
console.log('Sum:', num1, num2);
return num1 + num2;
}
function multiply(num1, num2) {
console.log('Multiply:', num1, num2);
return num1 * num2;
}
export {sum as add, multiply};
// main.js
import { add, multiply as mult } from './utils.js';
console.log(add(3, 7));
console.log(mult(3, 7));
一个单例对象是只能实例化一次的对象。如果不存在,则单例模式将创建类的新实例。如果存在实例,则仅返回对该对象的引用。重复调用构造函数将始终获取同一对象。
JavaScript 是一直内置单例的语言。我们只是不称它们为单例,我们称它们为对象字面量。例如:
const user = {
name: 'Peter',
age: 25,
job: 'Teacher',
greet: function() {
console.log('Hello!');
}
};
因为 JavaScript 中的每个对象都占用一个唯一的内存位置,并且当我们调用该 user 对象时,实际上是在返回该对象的引用。
如果我们尝试将 user 变量复制到另一个变量并修改该变量。例如:
const user1 = user;
user1.name = 'Mark';
我们将看到两个对象都被修改,因为 JavaScript 中的对象是通过引用而不是通过值传递的。因此,内存中只有一个对象。例如:
// prints 'Mark'
console.log(user.name);
// prints 'Mark'
console.log(user1.name);
// prints true
console.log(user === user1);
可以使用构造函数来实现单例模式。例如:
let instance = null;
function User() {
if(instance) {
return instance;
}
instance = this;
this.name = 'Peter';
this.age = 25;
return instance;
}
const user1 = new User();
const user2 = new User();
// prints true
console.log(user1 === user2);
调用此构造函数时,它将检查 instance 对象是否存在。如果对象不存在,则将 this 变量分配给 instance 变量。如果该对象存在,则只返回该对象。
单例也可以使用模块模式来实现。例如:
const singleton = (function() {
let instance;
function init() {
return {
name: 'Peter',
age: 24,
};
}
return {
getInstance: function() {
if(!instance) {
instance = init();
}
return instance;
}
}
})();
const instanceA = singleton.getInstance();
const instanceB = singleton.getInstance();
// prints true
console.log(instanceA === instanceB);
在上面的代码中,我们通过调用 singleton.getInstance 方法来创建一个新实例。如果实例已经存在,则此方法仅返回该实例。如果该实例不存在,则通过调用该 init() 函数创建一个新实例。
工厂模式使用工厂方法创建对象而不需要指定具体的类或构造函数的模式。
工厂模式用于创建对象而不需要暴露实例化的逻辑。当我们需要根据特定条件生成不同的对象时,可以使用此模式。例如:
class Car{
constructor(options) {
this.doors = options.doors || 4;
this.state = options.state || 'brand new';
this.color = options.color || 'white';
}
}
class Truck {
constructor(options) {
this.doors = options.doors || 4;
this.state = options.state || 'used';
this.color = options.color || 'black';
}
}
class VehicleFactory {
createVehicle(options) {
if(options.vehicleType === 'car') {
return new Car(options);
} else if(options.vehicleType === 'truck') {
return new Truck(options);
}
}
}
这里,创建了一个 Car 和一个 Truck 类(具有一些默认值),该类用于创建新的 car 和 truck对象。而且定义了一个VehicleFactory 类,用来根据 options 对象中的 vehicleType 属性来创建和返回新的对象。
const factory = new VehicleFactory();
const car = factory.createVehicle({
vehicleType: 'car',
doors: 4,
color: 'silver',
state: 'Brand New'
});
const truck= factory.createVehicle({
vehicleType: 'truck',
doors: 2,
color: 'white',
state: 'used'
});
// Prints Car {doors: 4, state: "Brand New", color: "silver"}
console.log(car);
// Prints Truck {doors: 2, state: "used", color: "white"}
console.log(truck);
我为类 VehicleFactory 创建了一个新的 factory 对象。然后,我们通过调用 factory.createVehicle 方法并且传递 options 对象,其 vehicleType 属性可能为 car 或者 truck 来创建新 Car 或 Truck 对象。
装饰器模式用于扩展对象的功能,而无需修改现有的类或构造函数。此模式可用于将特征添加到对象中,而无需修改底层的代码。
此模式的一个简单示例为:
function Car(name) {
this.name = name;
// Default values
this.color = 'White';
}
// Creating a new Object to decorate
const tesla= new Car('Tesla Model 3');
// Decorating the object with new functionality
tesla.setColor = function(color) {
this.color = color;
}
tesla.setPrice = function(price) {
this.price = price;
}
tesla.setColor('black');
tesla.setPrice(49000);
// prints black
console.log(tesla.color);
这种模式的一个更实际的例子是:
假设汽车的成本取决于其功能的数量。如果没有装饰器模式,我们将不得不为不同的功能组合创建不同的类,每个类都有一个 cost 方法来计算成本。例如:
class Car() {
}
class CarWithAC() {
}
class CarWithAutoTransmission {
}
class CarWithPowerLocks {
}
class CarWithACandPowerLocks {
}
但是,通过装饰器模式,我们可以创建一个基类 car 并且通过装饰器函数给不同的对象添加对应的成本逻辑。
class Car {
constructor() {
// Default Cost
this.cost = function() {
return 20000;
}
}
}
// Decorator function
function carWithAC(car) {
car.hasAC = true;
const prevCost = car.cost();
car.cost = function() {
return prevCost + 500;
}
}
// Decorator function
function carWithAutoTransmission(car) {
car.hasAutoTransmission = true;
const prevCost = car.cost();
car.cost = function() {
return prevCost + 2000;
}
}
// Decorator function
function carWithPowerLocks(car) {
car.hasPowerLocks = true;
const prevCost = car.cost();
car.cost = function() {
return prevCost + 500;
}
}
首先,我们创建了小轿车的基类 Car。然后针对要添加的特性创建了装饰器并且此装饰器以 Car 对象为参数。然后通过返回更新后的小汽车成本来覆盖对象的成本函数,且添加了一个用来标识某个特性是否已经被添加的属性。
要添加新的功能,我们只需要像下面一样就可以:
const car = new Car();
console.log(car.cost());
carWithAC(car);
carWithAutoTransmission(car);
carWithPowerLocks(car);
最后,我们可以像这样计算汽车的成本:
// Calculating total cost of the car
console.log(car.cost());
我们已经了解了 JavaScript 中使用的各种设计模式,但是这里没有涉及到可以用 JavaScript 实现的设计模式。
尽管了解各种设计模式很重要,但不要过度使用它们也同样重要。在使用设计模式之前,你应该仔细考虑你的问题是否适合该设计模式。要知道某个模式是否适合你的问题,应该好好研究该设计模式以及它的应用。
最后,欢迎优秀的你加入 HelloGitHub 的「译文亦舞」系列,让你的才华舞动起来!把优秀的文章分享给更多的人。要求:
关注 HelloGitHub 头条号第一时间收到推送
*请认真填写需求信息,我们会在24小时内与您取得联系。