搜索
首页web前端js教程构建您的第一个JavaScript库

Build Your First JavaScript Library

你是否曾惊叹于React的魔力?是否曾好奇Dojo是如何运作的?是否曾对jQuery的巧妙操作感到好奇?在本教程中,我们将潜入幕后,尝试构建一个超简化的jQuery版本。

我们几乎每天都在使用JavaScript库。无论是实现算法、提供API抽象还是操作DOM,库在大多数现代网站中都执行许多功能。

在本教程中,我们将尝试从头开始构建一个这样的库(当然,这是一个简化的版本)。我们将创建一个用于DOM操作的库,类似于jQuery。是的,这很有趣,但在你兴奋之前,让我澄清几点:

  • 这不会是一个功能齐全的库。我们将编写一套可靠的方法,但这并非完整的jQuery。我们将做的足够多,让你对构建库时会遇到的问题类型有很好的了解。
  • 我们在这里不会追求跨所有浏览器的完全兼容性。我们今天编写的代码应该可以在Chrome、Firefox和Safari上运行,但在IE等旧版浏览器上可能无法运行。
  • 我们不会涵盖我们库的每一个可能的用途。例如,我们的prepend方法只在你传递给它们我们的库实例时才有效;它们不适用于原始DOM节点或节点列表。

  1. 创建库的框架

我们将从模块本身开始。我们将使用ECMAScript模块(ESM),这是一种在Web上导入和导出代码的现代方法。

export class Dome {
    constructor(selector) {

    }
}

如你所见,我们导出一个名为Dome的类,其构造函数将接受一个参数,但它可以是多种类型。如果它是一个字符串,我们将假设它是一个CSS选择器,但我们也可以接受单个DOM节点或document.querySelectorAll的结果来简化元素查找。如果它具有length属性,我们将知道我们拥有一个节点列表。我们将把这些元素存储在this.elements中,Dome对象可以包装多个DOM元素,我们几乎需要在每种方法中循环遍历每个元素,因此这些实用程序将非常方便。

让我们从一个map函数开始,它接受一个参数,一个回调函数。我们将循环遍历数组中的项目,收集回调函数返回的内容,Dome实例将接收两个参数:当前元素和索引号。

我们还需要一个forEach方法,默认情况下,我们可以简单地将调用转发到mapOne。很容易看出这个函数的作用,但真正的问题是,为什么我们需要它?这需要一点你可能称之为“库理念”的东西。

简短的理念探讨

如果构建库只是编写代码,那将不是一项太难的工作。但在从事这个项目时,我发现更难的部分是决定某些方法应该如何工作。

很快,我们将构建一个Dome对象,它包装了多个DOM节点($("li").text()),你将得到一个包含所有元素文本连接在一起的单个字符串。这有用吗?我认为没有,但我不知道更好的返回值是什么。

对于这个项目,我将把多个元素的文本作为数组返回,除非数组中只有一个项目;然后我们只返回文本字符串,而不是包含单个项目的数组。我认为你最常获取单个元素的文本,所以我们对此情况进行了优化。但是,如果你正在获取多个元素的文本,我们将返回你可以使用的内容。

返回编码

因此,mapOne将首先调用map,然后返回数组或数组中的单个项目。如果你仍然不确定这如何有用,请继续关注:你将看到!

mapOne(callback) {
    const m = this.map(callback);
    return m.length > 1 ? m : m[0];
};
  1. 使用文本和HTML

接下来,让我们添加text方法来查看我们是在设置还是获取。请注意,这只是遍历元素并设置它们的文本。如果我们正在获取,我们将返回元素的mapOne方法:如果我们正在处理多个元素,这将返回一个数组;否则,它将只是一个字符串。

html方法与text方法几乎相同,只是它将使用innerHTML

html(html) {
    if (typeof html !== "undefined") {
        this.forEach(function (el) {
            el.innerHTML = html;
        });
        return this;
    } else {
        return this.mapOne(function (el) {
            return el.innerHTML;
        });
    }
}

就像我说的:几乎相同。


  1. 操作类

接下来,我们要能够添加和删除类,所以让我们编写addClassremoveClass方法。

我们的addClass方法将在每个元素上使用classList.add方法。当传递字符串时,只添加该类,当传递数组时,我们将遍历数组并添加其中包含的所有类。

addClass(classes) {
    return this.forEach(function (el) {
        if (typeof classes !== "string") {
            for (const elClass of classes) {
                el.classList.add(elClass);
            }
        } else {
            el.classList.add(classes);
        }
    });
}

很简单,对吧?

现在,删除类呢?为此,你几乎要做同样的事情,只是使用classList.remove方法。

  1. 使用属性

接下来,让我们添加attr函数。这将很容易,因为它与我们的html方法几乎相同。像这些方法一样,我们将能够同时获取和设置属性:我们将接受一个属性名称和值来设置,只接受一个属性名称来获取。

attr(attr, val) {
    if (typeof val !== "undefined") {
        return this.forEach(function (el) {
            el.setAttribute(attr, val);
        });
    } else {
        return this.mapOne(function (el) {
            return el.getAttribute(attr);
        });
    }
}

如果val已定义,我们将使用setAttribute方法。否则,我们将使用getAttribute方法。

  1. 创建元素

我们应该能够创建新元素,任何好的库都可以做到这一点。当然,这作为Dome类的方法是没有意义的。

export function create(tagName,attrs) {

}

如你所见,我们将接受两个参数:元素的名称和属性对象。大多数属性将通过我们的attr方法应用,文本内容将通过text方法应用于Dome对象。以下是所有这些的实际操作:

export function create(tagName, attrs) {
    let el = new Dome([document.createElement(tagName)]);
    if (attrs) {
        for (let key in attrs) {
            if (attrs.hasOwnProperty(key)) {
                el.attr(key, attrs[key]);
            }
        }
    }
    return el;
}

如你所见,我们创建元素并将其直接发送到新的Dome对象中。

但是现在我们正在创建新元素,我们将希望将它们插入到DOM中,对吧?

  1. 附加和前置元素

接下来,我们将编写appendprepend方法。这些函数有点棘手,主要是因为有多种用例。以下是我们想要能够做的事情:

dome1.append(dome2);
dome1.prepend(dome2);

我们可能想要附加或前置:

  • 一个新元素到一个或多个现有元素
  • 多个新元素到一个或多个现有元素
  • 一个现有元素到一个或多个现有元素
  • 多个现有元素到一个或多个现有元素

我使用“新”来表示尚未在DOM中的元素;现有元素已在DOM中。让我们现在逐步讲解:

append(els) {

}

我们期望els是一个Dome对象。一个完整的DOM库会将其作为节点或节点列表接受,但我们不会这样做。我们必须遍历我们的每个元素,然后在其中,我们遍历我们想要附加的每个元素。

如果我们正在附加,则来自作为参数传入的外部Dome对象的i将只包含原始(未克隆的)节点。因此,如果我们只将单个元素附加到单个元素,则所有涉及的节点都将是它们各自的prepend方法的一部分。

  1. 删除元素

为了完整起见,让我们添加一个remove方法。这将非常简单,因为我们只需要使用removeChild方法。为了使事情更简单,我们将使用forEach循环反向遍历,我将使用removeChild方法反向遍历循环,每个元素的Dome对象仍然可以正常工作;我们可以使用任何我们想要的方法,包括将其附加或前置回DOM。不错,对吧?

  1. 使用事件

最后但并非最不重要的是,我们将编写一些事件处理程序函数。

查看on方法,然后我们将讨论它:

on(evt, fn) {
    return this.forEach(function (el) {
        el.addEventListener(evt, fn, false);
    });
}

这很简单。我们只需遍历元素并使用addEventListener方法。off函数(它取消挂钩事件处理程序)几乎相同:

off(evt, fn) {
    return this.forEach(function (el) {
        el.removeEventListener(evt, fn, false);
    });
}

  1. 使用库

要使用Dome,只需将其放入脚本并导入它。

import {Dome, create} from "./dome.js"

从那里,你可以像这样使用它:

new Dome("li")
...

确保你导入它的脚本是ES模块。

就是这样!

我希望你能尝试一下我们的小型库,甚至可以扩展它一点。正如我前面提到的,我已经把它放在GitHub上了。随意分叉它,玩耍,并发送拉取请求。

让我再次澄清一下:本教程的目的并不是建议你应该总是编写自己的库。有专门的团队在共同努力,使大型的、成熟的库尽可能好。这里的目的是让你对库内部可能发生的事情有所了解;我希望你在这里学到了一些技巧。

我强烈建议你在你的一些最喜欢的库中四处挖掘。你会发现它们并不像你想象的那么神秘,而且你可能会学到很多东西。以下是一些不错的起点:

  • 我从jQuery源代码中学到的11件事(Paul Irish)
  • jQuery的幕后(James Padolsey)
  • React 16:深入了解我们前端UI库的API兼容重写

这篇文章已更新,其中包含Jacob Jackson的贡献。Jacob是一位网络开发者、技术作家、自由职业者和开源贡献者。

以上是构建您的第一个JavaScript库的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
JavaScript在行动中:现实世界中的示例和项目JavaScript在行动中:现实世界中的示例和项目Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

了解JavaScript引擎:实施详细信息了解JavaScript引擎:实施详细信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:学习曲线和易用性Python vs. JavaScript:学习曲线和易用性Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

Python vs. JavaScript:社区,图书馆和资源Python vs. JavaScript:社区,图书馆和资源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

从C/C到JavaScript:所有工作方式从C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript引擎:比较实施JavaScript引擎:比较实施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

超越浏览器:现实世界中的JavaScript超越浏览器:现实世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)