JavaScript中的工厂函数(推荐)

在学习jQuery的时候,我们经常会看到“工厂函数”这个概念,那么究竟什么是“工厂函数”呢?我们来看看概念,“所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,实际上是创建了一个类实例”。意思就是当我调用这个函数,实际上是先利用类创建了一个对象,然后返回这个对象。由于Javascript本身不是严格的面向对象的语言(不包含类),实际上来说,Javascript并没有严格的“工厂函数”,但是在Javascript中,我们能利用函数模拟类。

我们首先通过new关键字创建了一个对象,obj就相当于Object的实例。我们通过类实例化了一个对象,然后给这个对象相应的属性,最后返回对象。我们可以通过调用这个函数来创建对象,这样的话,实际上工厂函数也很好理解了:

1,它是一个函数。

2,它用来创建对象。

3,它像工厂一样,“生产”出来的函数都是“标准件”(拥有同样的属性)

不学习函数和对象,你不可能成为一名JavaScript程序员,并且当他们一起使用时,是构建块,我们需要从一个称为 组合(composition) 的强大对象范例开始。今天我们来看一些惯用的模式,使用工厂函数来组成函数,对象和 Promises 。组合模式是将一批子对象组织为树形结构,一条顶层的命令会在操作树中所有的对象。当一个函数返回一个对象时,我们称之他为 工厂函数(factory function) 。

让我们来看一个简单的例子。

function createJelly() {
 return {
 type: 'jelly',
 colour: 'red'
 scoops: 3
 };
 }

下面我们通过一些实例给大家介绍。

每次我们调用这个工厂函数,它将返回一个新的 jelly(果冻) 对象实例。要注意的重点是,我们不必在工厂函数名称前面加上 create ,但它可以让其他人更清楚函数的意图。对于 type 属性也是如此,但通常它可以帮助我们区分我们程序的对象。

    1.带参数的工厂函数

像所有函数一样,我们可以通过参数来定义我们的工厂函数 (icecream 冰淇淋),这可以用来改变返回对象的模型。

function createIceCream(flavour='Vanilla') {
 return {
 type: 'icecream',
 scoops: 3,
 flavour
 }
 }

理论上,您可以使用带有数百个参数的工厂函数来返回非常特使的深层嵌套对象,但正如我们将看到的,这根本不是组合的精髓。

2.组合的工厂函数

在一个工厂函数中定义另一个工厂函数,可以帮助我们把复杂的工厂函数拆分成更小的,可重用的碎片。

例如,我们可以创建一个 dessert(甜点)工厂函数,通过前面的 jelly(果冻)和 icecream(冰淇淋)工厂函数来定义。

 function createDessert() {
 return {
 type: 'dessert',
 bowl: [
 createJelly(),
 createIceCream()
 ]
 };
 }

我们可以组合工厂函数来构建任意复杂的对象,这不需要我们结合使用 new 或 this 。对象可以用 has-a (具有) 关系而不是 is-a (是) 来表示。也就是说,可以用组合而不是继承来实现。

例如,使用继承。

 // A trifle *is a* dessert 蛋糕*是*甜点
 function Trifle() {
 Dessert.apply(this, arguments);
 }
 Trifle.prototype = Dessert.prototype;
 // 或者
 class Trifle extends Dessert {
 constructor() {
 super();
 }
 }

我们可以用组合模式表达相同的意思。

 // A trifle *has* layers of jelly, custard and cream. It also *has a* topping.
 // 蛋糕 *有* 果冻层,奶酪层和奶油层,顶部还 *有* 装饰配料。
 function createTrifle() {
 return {
 type: 'trifle',
 layers: [
 createJelly(),
 createCustard(),
 createCream()
 ],
 topping: createAlmonds()
 };
 }

   3.异步的工厂函数

并非所有工厂都会立即返回数据。例如,有些必须先获取数据。在这些情况下,我们可以返回 Promises 来定义工厂函数。

function getMeal(menuUrl) {
 return new Promise((resolve, reject) => {
 fetch(menuUrl)
 .then(result => {
 resolve({
 type: 'meal',
 courses: result.json()
 });
 })
 .catch(reject);
 });
 }

这种深度嵌套的缩进会使异步工厂难以阅读和测试。将它们分解成多个不同的工厂通常是有帮助的,可以使用如下编写。

function getMeal(menuUrl) {
 return fetch(menuUrl)
 .then(result => result.json())
 .then(json => createMeal(json));
 }
 function createMeal(courses=[]) {
 return {
 type: 'meal',
 courses
 };
 }

当然,我们可以使用回调函数,但是我们已经有了 Promise.all 这样的工具返回 Promises 来定义工厂函数。

 function getWeeksMeals() {
 const menuUrl = 'jsfood.com/';
 return Promise.all([
 getMeal(`${menuUrl}/monday`),
 getMeal(`${menuUrl}/tuesday`),
 getMeal(`${menuUrl}/wednesday`),
 getMeal(`${menuUrl}/thursday`),
 getMeal(`${menuUrl}/friday`)
 ]);
 }

我们使用 get 而不是 create 作为命名约定来显示这些工厂做一些异步工作和返回promise。

4.函数和方法

到目前为止,我们还没有看到任何工厂用方法返回对象,这是故意的。这是因为一般来说,我们不需要这么做。工厂允许我们从计算中分离我们的数据。这意味着我们总是能够将对象序列化为JSON,这对于在会话之间持久化,通过HTTP或WebSockets发送它们,并将它们放入数据存储很重要。

例如,不是在 jelly(果冻) 对象上定义 eat 方法,我们可以定义一个新的函数,它接受一个对象作为参数并返回一个修改的版本。

function eatJelly(jelly) {
 if(jelly.scoops > 0) {
 jelly.scoops -= 1;
 }
 return jelly;
 }

一点点句法帮助使这是一个可行的模式,那些喜欢编程而不改变数据结构的人。对于那些喜欢编程而不改变数据结构的人来说,使用 ES6 的 ... 语法 是一个可行的模式。

 function eat(jelly) {
 if(jelly.scoops > 0) {
 return { ...jelly, scoops: jelly.scoops - 1 };
 } else {
 return jelly;
 }
 }

现在,不是这样写:

 import { createJelly } from './jelly';
 createJelly().eat();

而是这样写

 import { createJelly, eatJelly } from './jelly';
 eatJelly(createJelly()); 

最终结果是一个函数,它接受一个对象并返回一个对象。我们称之为返回对象的函数是什么? 一个工厂!

    5.高级工厂

将工厂传递给 高阶函数 ,这将给我们带来巨大的控制力。例如,我们可以使用这个概念来创建一个增强的对象。

 function giveTimestamp(factory) {
 return (...args) => {
 const instance = factory(...args);
 const time = Date.now();
 return { time, instance };
 };
 }
 const createOrder = giveTimestamp(function(ingredients) {
 return {
 type: 'order',
 ingredients
 };
 });

这个增强的对象采用一个现有工厂,并将其包装以创建返回带有时间戳实例的工厂。或者,如果我们想要确保一个工厂返回不可变的对象,我们可以用 freezer 来增强它。

 function freezer(factory) {
 return (...args) => Object.freeze(factory(...args)));
 }
 const createImmutableIceCream = freezer(createIceCream);
 createImmutableIceCream('strawberry').flavour = 'mint'; // Error!

    6.结论

作为一个 聪明的程序员 曾经说过:从没有抽象比错误的抽象更容易回收。JavaScript项目有一个趋势,变得难以测试和重构,因为我们经常鼓励使用复杂的抽象层。原型和类实现一个简单的想法使用复杂和不人性的工具,如 new 和 this ,即使现在,这仍然引起 各种各样的困惑 -几年后他们被添加到语言。对象和函数对于来自大多数语言背景的程序员来说是有意义的,并且都是JavaScript中的原始类型,所以可以说工厂不是抽象的!对象和函数对来自大多数背景的程序员都有意义,并且都是JavaScript中的原始类型,所以可以说工厂不是抽象的!使用这些简单的构建块使得我们的代码对于没有经验的程序员更加友好,这绝对是我们应该关心的事情。工厂鼓励我们用原始数据来模拟复杂和异步数据,原始数据具有组合的自然能力,而不强迫我们去做一些高级抽象。 当我们坚持简单时,JavaScript更甜蜜!

以上所述是小编给大家介绍的JavaScript中的工厂函数,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • javascript设计模式之对象工厂函数与构造函数详解

    下面通过文字详解加代码分析的方式给大家分享下javascript设计模式之对象工厂函数与构造函数的相关知识. 概述使用对象字面量,或者向空对象中动态地添加新成员,是最简单易用的对象创建方法.然而,除了这两种常用的对象创建方式,JavaScript还提供了其他方法创建对象.1).使用工厂函数创建对象我们可以编写一个函数,此函数的功能就是创建对象,可将其. 概述 使用对象字面量,或者向空对象中动态地添加新成员,是最简单易用的对象创建方法. 然而,除了这两种常用的对象创建方式,JavaScript还提

  • JavaScript中的工厂函数(推荐)

    在学习jQuery的时候,我们经常会看到"工厂函数"这个概念,那么究竟什么是"工厂函数"呢?我们来看看概念,"所谓工厂函数,就是指这些内建函数都是类对象,当你调用他们时,实际上是创建了一个类实例".意思就是当我调用这个函数,实际上是先利用类创建了一个对象,然后返回这个对象.由于Javascript本身不是严格的面向对象的语言(不包含类),实际上来说,Javascript并没有严格的"工厂函数",但是在Javascript中,我

  • JavaScript中利用构造器函数模拟类的方法

    前言 本文小编带大家一起学习的是在 JavaScript 中使用构造器函数(construcor function)模拟类.下面话不多说,感兴趣的朋友们下面来一起看看吧. 构造器函数简介 你可以使用 ES6 的 class 关键字来实现类,不过我建议你使用传统的构造器函数来模拟类,因为这样可以给人一种你是个 JavaScript 老手的错觉,哈哈! 什么是构造器函数?构造器函数是编写对象的方法之一.一般情况下,你可以这样编写一个对象: var obj = { a:1, b:2 }; 但也可以使用

  • javascript中Array()数组函数详解

    在程序语言中数组的重要性不言而喻,JavaScript中数组也是最常使用的对象之一,数组是值的有序集合,由于弱类型的原因,JavaScript中数组十分灵活.强大,不像是Java等强类型高级语言数组只能存放同一类型或其子类型元素,JavaScript在同一个数组中可以存放多种类型的元素,而且是长度也是可以动态调整的,可以随着数据增加或减少自动对数组长度做更改. Array()是一个用来构建数组的内建构造器函数.数组主要由如下三种创建方式: array = new Array() array =

  • JavaScript中字符串分割函数split用法实例

    本文实例讲述了JavaScript中字符串分割函数split用法.分享给大家供大家参考.具体如下: 先来看下面这段代码: <script type="text/javascript"> var str="How are you doing today?" document.write(str.split(" ") + "<br />") document.write(str.split("&q

  • JavaScript中push(),join() 函数 实例详解

    定义和用法 push方法 可向数组的末尾添加一个或多个元素,并返回一个新的长度. join方法 用于把数组中所有元素添加到一个指定的字符串,元素是通过指定的分隔符进行分割的. 语法 arrayObject.push(newelement1,newelement2,....,newelementX) arrayObject.join(separator). 参数描述newelement1必需.要添加到数组的第一个元素.newelement2可选.要添加到数组的第二个元素.newelementX可选

  • 深入认识javascript中的eval函数

    1)介绍javascript中的eval函数的用法 (2)如何在函数内执行全局代码 ►先来说eval的用法,内容比较简单,熟悉的可以跳过. eval函数接收一个参数s,如果s不是字符串,则直接返回s.否则执行s语句.如果s语句执行结果是一个值,则返回此值,否则返回undefined. 需要特别注意的是对象声明语法"{}"并不能返回一个值,需要用括号括起来才会返回值,简单示例如下: 复制代码 代码如下: var code1='"a" + 2'; //表达式 var c

  • 浅谈JavaScript中变量和函数声明的提升

    现象: 1. 在JavaScript中变量和函数的声明会提升到最顶部执行. 2. 函数的提升高于变量的提升. 3. 函数内部如果用var声明了相同名称的外部变量,函数将不再向上寻找. 4. 匿名函数不会提升. 5. 不同<script>块中的函数互不影响. 例子: 函数声明提升高于变量声明 //同时声明变量a和函数a var a; function a() {} alert(typeof a); //显示的是"function",初步证明function的优先级高于var.

  • JavaScript中cookie工具函数封装的示例代码

    一. 语法 1.1 获取当前页面的所有cookie: var allCookies = document.cookie; allCookies 是一个字符串,其中包含了以分号分隔的cookie列表字符串 (即 key=value 键值对). 1.2 写一个新cookie: document.cookie = updatedCookie; updatedCookie是一个键值对形式的字符串.只能用这个方法一次设置或更新一个cookie,而且写入并不是覆盖,而是添加.例如: document.coo

  • jQuery中的read和JavaScript中的onload函数的区别

    在JavaScript中,onload函数是最经常使用的,几乎涉及到JavaScript的童鞋都少不了要接触它.这个函数的作用就是等待网页完全装载完了以后再去执行代码块内的语句,因为按照文档流的执行顺序,通常用于头部加载JavaScript的时候需要用到. 复制代码 代码如下: window.onload = function(){     // 当网页加载完成后执行这里的代码块 }; 而在jQuery中也有一个对应的函数,即等待网页加载完后执行代码块 复制代码 代码如下: $(document

  • JavaScript中的普通函数和箭头函数的区别和用法详解

    最近被问到了一个问题: javaScript 中的箭头函数 ( => ) 和普通函数 ( function ) 有什么区别? 我当时想的就是:这个问题很简单啊~(flag),然后做出了错误的回答-- 箭头函数中的 this 和调用时的上下文无关,而是取决于定义时的上下文 这并不是很正确的答案--虽然也不是完全错误 箭头函数中的 this 首先说我的回答中没有错误的部分:箭头函数中的 this 确实和调用时的上下文无关 function make () { return ()=>{ consol

随机推荐