你是否曾驚嘆于React的魔力?是否曾好奇Dojo是如何運(yùn)作的?是否曾對jQuery的巧妙操作感到好奇?在本教程中,我們將潛入幕后,嘗試構(gòu)建一個超簡化的jQuery版本。
我們幾乎每天都在使用JavaScript庫。無論是實現(xiàn)算法、提供API抽象還是操作DOM,庫在大多數(shù)現(xiàn)代網(wǎng)站中都執(zhí)行許多功能。
在本教程中,我們將嘗試從頭開始構(gòu)建一個這樣的庫(當(dāng)然,這是一個簡化的版本)。我們將創(chuàng)建一個用于DOM操作的庫,類似于jQuery。是的,這很有趣,但在你興奮之前,讓我澄清幾點:
- 這不會是一個功能齊全的庫。我們將編寫一套可靠的方法,但這并非完整的jQuery。我們將做的足夠多,讓你對構(gòu)建庫時會遇到的問題類型有很好的了解。
- 我們在這里不會追求跨所有瀏覽器的完全兼容性。我們今天編寫的代碼應(yīng)該可以在Chrome、Firefox和Safari上運(yùn)行,但在IE等舊版瀏覽器上可能無法運(yùn)行。
- 我們不會涵蓋我們庫的每一個可能的用途。例如,我們的
prepend
方法只在你傳遞給它們我們的庫實例時才有效;它們不適用于原始DOM節(jié)點或節(jié)點列表。
-
創(chuàng)建庫的框架
我們將從模塊本身開始。我們將使用ECMAScript模塊(ESM),這是一種在Web上導(dǎo)入和導(dǎo)出代碼的現(xiàn)代方法。
export class Dome { constructor(selector) { } }
如你所見,我們導(dǎo)出一個名為Dome
的類,其構(gòu)造函數(shù)將接受一個參數(shù),但它可以是多種類型。如果它是一個字符串,我們將假設(shè)它是一個CSS選擇器,但我們也可以接受單個DOM節(jié)點或document.querySelectorAll
的結(jié)果來簡化元素查找。如果它具有length
屬性,我們將知道我們擁有一個節(jié)點列表。我們將把這些元素存儲在this.elements
中,Dome
對象可以包裝多個DOM元素,我們幾乎需要在每種方法中循環(huán)遍歷每個元素,因此這些實用程序?qū)⒎浅7奖恪?/p>
讓我們從一個map
函數(shù)開始,它接受一個參數(shù),一個回調(diào)函數(shù)。我們將循環(huán)遍歷數(shù)組中的項目,收集回調(diào)函數(shù)返回的內(nèi)容,Dome
實例將接收兩個參數(shù):當(dāng)前元素和索引號。
我們還需要一個forEach
方法,默認(rèn)情況下,我們可以簡單地將調(diào)用轉(zhuǎn)發(fā)到mapOne
。很容易看出這個函數(shù)的作用,但真正的問題是,為什么我們需要它?這需要一點你可能稱之為“庫理念”的東西。
簡短的理念探討
如果構(gòu)建庫只是編寫代碼,那將不是一項太難的工作。但在從事這個項目時,我發(fā)現(xiàn)更難的部分是決定某些方法應(yīng)該如何工作。
很快,我們將構(gòu)建一個Dome
對象,它包裝了多個DOM節(jié)點($("li").text()
),你將得到一個包含所有元素文本連接在一起的單個字符串。這有用嗎?我認(rèn)為沒有,但我不知道更好的返回值是什么。
對于這個項目,我將把多個元素的文本作為數(shù)組返回,除非數(shù)組中只有一個項目;然后我們只返回文本字符串,而不是包含單個項目的數(shù)組。我認(rèn)為你最常獲取單個元素的文本,所以我們對此情況進(jìn)行了優(yōu)化。但是,如果你正在獲取多個元素的文本,我們將返回你可以使用的內(nèi)容。
返回編碼
因此,mapOne
將首先調(diào)用map
,然后返回數(shù)組或數(shù)組中的單個項目。如果你仍然不確定這如何有用,請繼續(xù)關(guān)注:你將看到!
mapOne(callback) { const m = this.map(callback); return m.length > 1 ? m : m[0]; };
-
使用文本和HTML
接下來,讓我們添加text
方法來查看我們是在設(shè)置還是獲取。請注意,這只是遍歷元素并設(shè)置它們的文本。如果我們正在獲取,我們將返回元素的mapOne
方法:如果我們正在處理多個元素,這將返回一個數(shù)組;否則,它將只是一個字符串。
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; }); } }
就像我說的:幾乎相同。
-
操作類
接下來,我們要能夠添加和刪除類,所以讓我們編寫addClass
和removeClass
方法。
我們的addClass
方法將在每個元素上使用classList.add
方法。當(dāng)傳遞字符串時,只添加該類,當(dāng)傳遞數(shù)組時,我們將遍歷數(shù)組并添加其中包含的所有類。
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); } }); }
很簡單,對吧?
現(xiàn)在,刪除類呢?為此,你幾乎要做同樣的事情,只是使用classList.remove
方法。
-
使用屬性
接下來,讓我們添加attr
函數(shù)。這將很容易,因為它與我們的html
方法幾乎相同。像這些方法一樣,我們將能夠同時獲取和設(shè)置屬性:我們將接受一個屬性名稱和值來設(shè)置,只接受一個屬性名稱來獲取。
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
方法。
-
創(chuàng)建元素
我們應(yīng)該能夠創(chuàng)建新元素,任何好的庫都可以做到這一點。當(dāng)然,這作為Dome
類的方法是沒有意義的。
export function create(tagName,attrs) { }
如你所見,我們將接受兩個參數(shù):元素的名稱和屬性對象。大多數(shù)屬性將通過我們的attr
方法應(yīng)用,文本內(nèi)容將通過text
方法應(yīng)用于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; }
如你所見,我們創(chuàng)建元素并將其直接發(fā)送到新的Dome
對象中。
但是現(xiàn)在我們正在創(chuàng)建新元素,我們將希望將它們插入到DOM中,對吧?
-
附加和前置元素
接下來,我們將編寫append
和prepend
方法。這些函數(shù)有點棘手,主要是因為有多種用例。以下是我們想要能夠做的事情:
dome1.append(dome2); dome1.prepend(dome2);
我們可能想要附加或前置:
- 一個新元素到一個或多個現(xiàn)有元素
- 多個新元素到一個或多個現(xiàn)有元素
- 一個現(xiàn)有元素到一個或多個現(xiàn)有元素
- 多個現(xiàn)有元素到一個或多個現(xiàn)有元素
我使用“新”來表示尚未在DOM中的元素;現(xiàn)有元素已在DOM中。讓我們現(xiàn)在逐步講解:
append(els) { }
我們期望els
是一個Dome
對象。一個完整的DOM庫會將其作為節(jié)點或節(jié)點列表接受,但我們不會這樣做。我們必須遍歷我們的每個元素,然后在其中,我們遍歷我們想要附加的每個元素。
如果我們正在附加,則來自作為參數(shù)傳入的外部Dome
對象的i
將只包含原始(未克隆的)節(jié)點。因此,如果我們只將單個元素附加到單個元素,則所有涉及的節(jié)點都將是它們各自的prepend
方法的一部分。
-
刪除元素
為了完整起見,讓我們添加一個remove
方法。這將非常簡單,因為我們只需要使用removeChild
方法。為了使事情更簡單,我們將使用forEach
循環(huán)反向遍歷,我將使用removeChild
方法反向遍歷循環(huán),每個元素的Dome
對象仍然可以正常工作;我們可以使用任何我們想要的方法,包括將其附加或前置回DOM。不錯,對吧?
-
使用事件
最后但并非最不重要的是,我們將編寫一些事件處理程序函數(shù)。
查看on
方法,然后我們將討論它:
on(evt, fn) { return this.forEach(function (el) { el.addEventListener(evt, fn, false); }); }
這很簡單。我們只需遍歷元素并使用addEventListener
方法。off
函數(shù)(它取消掛鉤事件處理程序)幾乎相同:
off(evt, fn) { return this.forEach(function (el) { el.removeEventListener(evt, fn, false); }); }
-
使用庫
要使用Dome
,只需將其放入腳本并導(dǎo)入它。
import {Dome, create} from "./dome.js"
從那里,你可以像這樣使用它:
new Dome("li") ...
確保你導(dǎo)入它的腳本是ES模塊。
就是這樣!
我希望你能嘗試一下我們的小型庫,甚至可以擴(kuò)展它一點。正如我前面提到的,我已經(jīng)把它放在GitHub上了。隨意分叉它,玩耍,并發(fā)送拉取請求。
讓我再次澄清一下:本教程的目的并不是建議你應(yīng)該總是編寫自己的庫。有專門的團(tuán)隊在共同努力,使大型的、成熟的庫盡可能好。這里的目的是讓你對庫內(nèi)部可能發(fā)生的事情有所了解;我希望你在這里學(xué)到了一些技巧。
我強(qiáng)烈建議你在你的一些最喜歡的庫中四處挖掘。你會發(fā)現(xiàn)它們并不像你想象的那么神秘,而且你可能會學(xué)到很多東西。以下是一些不錯的起點:
- 我從jQuery源代碼中學(xué)到的11件事(Paul Irish)
- jQuery的幕后(James Padolsey)
- React 16:深入了解我們前端UI庫的API兼容重寫
這篇文章已更新,其中包含Jacob Jackson的貢獻(xiàn)。Jacob是一位網(wǎng)絡(luò)開發(fā)者、技術(shù)作家、自由職業(yè)者和開源貢獻(xiàn)者。
以上是構(gòu)建您的第一個JavaScript庫的詳細(xì)內(nèi)容。更多信息請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費脫衣服圖片

Undresser.AI Undress
人工智能驅(qū)動的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover
用于從照片中去除衣服的在線人工智能工具。

Clothoff.io
AI脫衣機(jī)

Video Face Swap
使用我們完全免費的人工智能換臉工具輕松在任何視頻中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的代碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
功能強(qiáng)大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級代碼編輯軟件(SublimeText3)

在JavaScript中,選擇單行注釋(//)還是多行注釋(//)取決于注釋的目的和項目需求:1.使用單行注釋進(jìn)行快速、內(nèi)聯(lián)的解釋;2.使用多行注釋進(jìn)行詳細(xì)的文檔說明;3.保持注釋風(fēng)格的一致性;4.避免過度注釋;5.確保注釋與代碼同步更新。選擇合適的注釋風(fēng)格有助于提高代碼的可讀性和可維護(hù)性。

Java和JavaScript是不同的編程語言,各自適用于不同的應(yīng)用場景。Java用于大型企業(yè)和移動應(yīng)用開發(fā),而JavaScript主要用于網(wǎng)頁開發(fā)。

評論arecrucialinjavascriptformaintainingclarityclarityandfosteringCollaboration.1)heelpindebugging,登機(jī),andOnderStandingCodeeVolution.2)使用林格forquickexexplanations andmentmentsmmentsmmentsmments andmmentsfordeffordEffordEffordEffordEffordEffordEffordEffordEddeScriptions.3)bestcractices.3)bestcracticesincracticesinclud

JavascriptconcommentsenceenceEncorenceEnterential gransimenting,reading and guidingCodeeXecution.1)單inecommentsareusedforquickexplanations.2)多l(xiāng)inecommentsexplaincomplexlogicorprovideDocumentation.3)

JavaScripthasseveralprimitivedatatypes:Number,String,Boolean,Undefined,Null,Symbol,andBigInt,andnon-primitivetypeslikeObjectandArray.Understandingtheseiscrucialforwritingefficient,bug-freecode:1)Numberusesa64-bitformat,leadingtofloating-pointissuesli

JavaScriptIspreferredforredforwebdevelverment,而Javaisbetterforlarge-ScalebackendsystystemsandSandAndRoidApps.1)JavascriptexcelcelsincreatingInteractiveWebexperienceswebexperienceswithitswithitsdynamicnnamicnnamicnnamicnnamicnemicnemicnemicnemicnemicnemicnemicnemicnddommanipulation.2)

JavaScript中的日期和時間處理需注意以下幾點:1.創(chuàng)建Date對象有多種方式,推薦使用ISO格式字符串以保證兼容性;2.獲取和設(shè)置時間信息可用get和set方法,注意月份從0開始;3.手動格式化日期需拼接字符串,也可使用第三方庫;4.處理時區(qū)問題建議使用支持時區(qū)的庫,如Luxon。掌握這些要點能有效避免常見錯誤。

javascripthassevenfundaMentalDatatypes:數(shù)字,弦,布爾值,未定義,null,object和symbol.1)numberSeadUble-eaduble-ecisionFormat,forwidevaluerangesbutbecautious.2)
