簡體   English   中英

有沒有比使用 IIFE 更好的方法來做到這一點?

[英]Is there a better way to do this than using an IIFE?

我正在嘗試為 forms 創建一個模板,該模板檢查 HTML 元素上的data-regex自定義參數,然后從中創建一個正則表達式以用於驗證輸入。 這是我到目前為止所擁有的。

 var textInputs = document.getElementsByClassName("form__text"); for (var i = 0; i < textInputs.length; ++i) { if (textInputs[i].getElementsByTagName("input")[0].type == "email") { (function() { var pattern = /.*[a-zA-Z0-9]+.*@.*\.[a-zA-Z]+/; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", function(e) { checkText(pattern, e); }); })(); } else if (textInputs[i].getElementsByTagName("input")[0].type == "text") { (function() { var patternStr = textInputs[i].getAttribute("data-regex"); var pattern = patternStr? new RegExp(patternStr): null; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", function(e) { checkText(pattern, e); }); })(); } } function checkText(pattern, e) { if (pattern && e.target.value.search(pattern) == -1) { e.target.parentElement.classList.add("form__text--error"); } else { e.target.parentElement.classList.remove("form__text--error"); } }
 * { margin: 0; padding: 0; box-sizing: border-box; }.form { margin: 10px; }.form.form__text { position: relative; margin: 2rem 0 4rem 0; display: block; }.form.form__text__label { position: absolute; font-size: 1.4rem; padding: 10px; opacity: 0.5; top: 50%; left: 0; pointer-events: none; transition: all 0.2s ease-out; transform: translateY(-50%); }.form.form__text__error-label { color: red; opacity: 0; transition: all 0.2s ease-out; position: absolute; top: 110%; left: 0; }.form.form__text input[type=text], .form.form__text input[type=email] { padding: 10px; width: 100%; border: 1px solid #ccc; border-radius: 5px; transition: all 0.2s ease-out; }.form.form__text input[type=text]:focus ~.form__text__label, .form.form__text input[type=text]:not(:placeholder-shown) ~.form__text__label, .form.form__text input[type=email]:focus ~.form__text__label, .form.form__text input[type=email]:not(:placeholder-shown) ~.form__text__label { transform: translateX(-15px) translateY(-125%) scale(0.75); opacity: 1; }.form.form__text input[type=text]:focus, .form.form__text input[type=email]:focus { outline: none; background: rgba(122, 217, 255, 0.075); }.form.form__text--error.form__text__label { color: red; }.form.form__text--error.form__text__error-label { opacity: 1; }.form.form__text--error input[type=text], .form.form__text--error input[type=email] { border: 1px solid red; }.form.form__text--error input[type=text]:focus, .form.form__text--error input[type=email]:focus { background: rgba(255, 0, 0, 0.05); }
 <form class="form"> <label class="form__text"> <input type="email" id="email" name="email" placeholder=" " /> <span class="form__text__label">Email</span> <span class="form__text__error-label">Invalid Email</label> </label> <label class="form__text" data-regex="[a-zA-z ]{4,}"> <input type="text" id="name" name="name" placeholder=" " /> <span class="form__text__label">Name</span> <span class="form__text__error-label">Invalid Name</span> </label> <label class="form__text"> <input type="text" id="random" name="random" placeholder=" " /> <span class="form__text__label">Random Fact</span> </label> </form>

我不得不將兩個addEventListener塊包裝在 IIFE 中,因為如果沒有,只要觸發回調,模式變量就會被覆蓋。 我很好奇是否有更清潔的方法來做到這一點。 我假設創建正則表達式對象需要一些費用,這就是為什么我試圖在回調之外創建它們。 提前致謝。

問題是pattern將是整個 function 的共享變量名。 然后稍后當事件偵聽器觸發時,它將拾取pattern的最新版本而不是當前版本。 這是一個經典的問題

ES6 解決方案

隨着letconst的引入,您可以在塊 scope 中擁有變量,因此解決方案非常簡單 - 將任何var更改為letconst

 const textInputs = document.getElementsByClassName("form__text"); for (let i = 0; i < textInputs.length; ++i) { if (textInputs[i].getElementsByTagName("input")[0].type == "email") { const pattern = /.*[a-zA-Z0-9]+.*@.*\.[a-zA-Z]+/; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", function(e) { checkText(pattern, e); }); } else if (textInputs[i].getElementsByTagName("input")[0].type == "text") { const patternStr = textInputs[i].getAttribute("data-regex"); const pattern = patternStr? new RegExp(patternStr): null; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", function(e) { checkText(pattern, e); }); } } function checkText(pattern, e) { if (pattern && e.target.value.search(pattern) == -1) { e.target.parentElement.classList.add("form__text--error"); } else { e.target.parentElement.classList.remove("form__text--error"); } }
 * { margin: 0; padding: 0; box-sizing: border-box; }.form { margin: 10px; }.form.form__text { position: relative; margin: 2rem 0 4rem 0; display: block; }.form.form__text__label { position: absolute; font-size: 1.4rem; padding: 10px; opacity: 0.5; top: 50%; left: 0; pointer-events: none; transition: all 0.2s ease-out; transform: translateY(-50%); }.form.form__text__error-label { color: red; opacity: 0; transition: all 0.2s ease-out; position: absolute; top: 110%; left: 0; }.form.form__text input[type=text], .form.form__text input[type=email] { padding: 10px; width: 100%; border: 1px solid #ccc; border-radius: 5px; transition: all 0.2s ease-out; }.form.form__text input[type=text]:focus ~.form__text__label, .form.form__text input[type=text]:not(:placeholder-shown) ~.form__text__label, .form.form__text input[type=email]:focus ~.form__text__label, .form.form__text input[type=email]:not(:placeholder-shown) ~.form__text__label { transform: translateX(-15px) translateY(-125%) scale(0.75); opacity: 1; }.form.form__text input[type=text]:focus, .form.form__text input[type=email]:focus { outline: none; background: rgba(122, 217, 255, 0.075); }.form.form__text--error.form__text__label { color: red; }.form.form__text--error.form__text__error-label { opacity: 1; }.form.form__text--error input[type=text], .form.form__text--error input[type=email] { border: 1px solid red; }.form.form__text--error input[type=text]:focus, .form.form__text--error input[type=email]:focus { background: rgba(255, 0, 0, 0.05); }
 <form class="form"> <label class="form__text"> <input type="email" id="email" name="email" placeholder=" " /> <span class="form__text__label">Email</span> <span class="form__text__error-label">Invalid Email</label> </label> <label class="form__text" data-regex="[a-zA-z ]{4,}"> <input type="text" id="name" name="name" placeholder=" " /> <span class="form__text__label">Name</span> <span class="form__text__error-label">Invalid Name</span> </label> <label class="form__text"> <input type="text" id="random" name="random" placeholder=" " /> <span class="form__text__label">Random Fact</span> </label> </form>

您可以使用 Babel 將代碼從 ES6+ 轉換為 ES5,因此您可以編寫上述代碼,但它會自動轉換為與新代碼相同的舊代碼。

注意: letconst將被 IE11 接受,但是它們的行為與var完全相同。 IE11 僅使它們成為允許的語法,但作為var的別名。 它們不會是塊范圍的。

ES5 解決方案

如果你必須使用 ES5,那么這不是什么大問題。 問題是缺少塊作用域,只有全局作用域和函數作用域。 因此,您需要在功能 scope 中捕獲變量。 IIFE 可以做到這一點,但它看起來有點難看。

咖喱checkText()

我們可以改為使checkText成為一個高階function ,它是柯里化的 - 它不是采用兩個參數,而是首先采用一個參數,然后返回一個采用第二個參數的 function。 這看起來像這樣:

function checkText(pattern){
  return function(e) {
    if (pattern && e.target.value.search(pattern) == -1) {
      e.target.parentElement.classList.add("form__text--error");
    } else {
      e.target.parentElement.classList.remove("form__text--error");
    }
  }
}

它在這里是一個強大的構造,因為它允許我們在調用var returnedFunction = checkText(pattern)時立即捕獲變量 - 現在即使變量模式在封閉上下文中發生變化, returnedFunction仍將保留前一個。

替換舊調用

作為獎勵,這使我們能夠消除一些無用的代碼。 讓我們一步一步來完全理解 - 首先這個

.addEventListener("input", function(e) {
  checkText(pattern, e);
});

必須變成

.addEventListener("input", function(e) {
  var returnedFunction = checkText(pattern);
  returnedFunction(e);
});

因為我們現在必須將參數傳遞給兩個不同的函數。 此時仍然存在與之前相同的問題 現在這並不能解決任何問題,但我想展示干預步驟。 同樣的問題 - 當偵聽器觸發時, checkText checkText(pattern)將在該點pattern已更改時執行。

確保checkText(pattern)捕獲正確的值

我們需要確保它在事件偵聽器中初始化之前觸發:

var pattern = patternStr ? new RegExp(patternStr) : null;
var returnedFunction = checkText(pattern);
/* ...code... */
.addEventListener("input", function(e) {
  returnedFunction(e);
});

當我們將它放在與pattern變量相同的級別時,就在.addEventListener回調之外,它可以按預期工作而無需使用 IIFE。 我們將捕獲當前模式變量,對其進行更改不會影響returnedFunction

簡化抽象

然而,回調 function 現在是 this function(e) { returnedFunction(e); } - 一個function(e) { returnedFunction(e); }接受一個參數並調用一個 function 傳遞相同的參數作為參數。 調用<anonymous function>(e)與調用returneFunction(e)相同 外部包裝器 function 和內部returneFunction具有完全相同的簽名,並且使用單個參數調用它們的語義幾乎相同。 因此,包裝器現在沒用了。 我們可以刪除它並執行 lambda 演算調用 Eta 歸約來簡化抽象,因此整個回調變成

var pattern = patternStr ? new RegExp(patternStr) : null;
var returnedFunction = checkText(pattern);
/* ...code... */
.addEventListener("input", returnedFunction);

現在事件監聽器只是returnedFunction函數。 當它被觸發時,它將傳遞與以前相同的參數。

刪除額外的變量

最后,簡化的最后一步是內聯checkText(pattern)調用。 我們在這里並不需要額外的變量。 擺脫它並擁有它很容易

.addEventListener("input", checkText(pattern));

完畢。 這很詳細,因為我想展示這個過程。 實際上,它只是將checkText() function 轉換為咖喱變體,然后用它替換回調。 希望這些步驟將它從一些奇怪的指令轉變為理解為什么要這樣做。

最終結果是這樣的:

 var textInputs = document.getElementsByClassName("form__text"); for (var i = 0; i < textInputs.length; ++i) { if (textInputs[i].getElementsByTagName("input")[0].type == "email") { var pattern = /.*[a-zA-Z0-9]+.*@.*\.[a-zA-Z]+/; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", checkText(pattern)); } else if (textInputs[i].getElementsByTagName("input")[0].type == "text") { var patternStr = textInputs[i].getAttribute("data-regex"); var pattern = patternStr? new RegExp(patternStr): null; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", checkText(pattern)); } } function checkText(pattern){ return function(e) { if (pattern && e.target.value.search(pattern) == -1) { e.target.parentElement.classList.add("form__text--error"); } else { e.target.parentElement.classList.remove("form__text--error"); } } }
 * { margin: 0; padding: 0; box-sizing: border-box; }.form { margin: 10px; }.form.form__text { position: relative; margin: 2rem 0 4rem 0; display: block; }.form.form__text__label { position: absolute; font-size: 1.4rem; padding: 10px; opacity: 0.5; top: 50%; left: 0; pointer-events: none; transition: all 0.2s ease-out; transform: translateY(-50%); }.form.form__text__error-label { color: red; opacity: 0; transition: all 0.2s ease-out; position: absolute; top: 110%; left: 0; }.form.form__text input[type=text], .form.form__text input[type=email] { padding: 10px; width: 100%; border: 1px solid #ccc; border-radius: 5px; transition: all 0.2s ease-out; }.form.form__text input[type=text]:focus ~.form__text__label, .form.form__text input[type=text]:not(:placeholder-shown) ~.form__text__label, .form.form__text input[type=email]:focus ~.form__text__label, .form.form__text input[type=email]:not(:placeholder-shown) ~.form__text__label { transform: translateX(-15px) translateY(-125%) scale(0.75); opacity: 1; }.form.form__text input[type=text]:focus, .form.form__text input[type=email]:focus { outline: none; background: rgba(122, 217, 255, 0.075); }.form.form__text--error.form__text__label { color: red; }.form.form__text--error.form__text__error-label { opacity: 1; }.form.form__text--error input[type=text], .form.form__text--error input[type=email] { border: 1px solid red; }.form.form__text--error input[type=text]:focus, .form.form__text--error input[type=email]:focus { background: rgba(255, 0, 0, 0.05); }
 <form class="form"> <label class="form__text"> <input type="email" id="email" name="email" placeholder=" " /> <span class="form__text__label">Email</span> <span class="form__text__error-label">Invalid Email</label> </label> <label class="form__text" data-regex="[a-zA-z ]{4,}"> <input type="text" id="name" name="name" placeholder=" " /> <span class="form__text__label">Name</span> <span class="form__text__error-label">Invalid Name</span> </label> <label class="form__text"> <input type="text" id="random" name="random" placeholder=" " /> <span class="form__text__label">Random Fact</span> </label> </form>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM