js 正则是否包含某些字符串_我从Vue源码中学到的一些JS编程技巧
在我們面試的過程中,經常會遇到問源碼的環節,因為優秀的框架通常都會包含很多設計理念跟編程實踐。這段時間我一直在看Vue2的源碼,發現了很多有意思的實現。雖然現在Vue3都已經發布了,也無法否認Vue2是個優秀的框架這個事實,不影響我們從中學到一些最佳實踐。
對Vue不感興趣的同學也可以看看,因為我只是談論一些我從這個框架的實現上學到的一些JavaScript的用法,不涉及Vue的概念。
假設我們有這樣一個字符串:
var html = '<span class="red">hello world</span> <span>hello xxx</span>'。我們現在想要提取其中非標簽的文本,拿到如下結果:
'hello world hello xxx'。這該怎么辦?我們首先想到的肯定是正則表達式,但是這個場景下正則表達式寫起來肯定很煩,我們來看看Vue的開發者是怎么處理的:
- 既然這個字符串是HTML文本格式,我們就可以把它解析成對應的HTML元素。
 - HTML元素的textContent屬性可以用來獲取HTML元素中的文本內容。
 
代碼如下:
function decoder(html){let decoder = document.createElement('div')decoder.innerHTML = htmlconsole.log(decoder.textContent)// return decoder.textContent }這個代碼創建了一個div元素作為容器,然后通過設置innerHTML把字符串轉換成對應的HTML元素,最后就可以通過textContent屬性來獲取文本內容了。
隨著前端的高速發展,我們已經可以在多個環境中運行JavaScript代碼,為了針對不同的運行環境作出調整,我們需要知道我們的代碼跑在哪個環境下,我們來看看Vue是怎么確定運行時環境的:
const inBrowser = typeof window !== 'undefined' const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase() const UA = inBrowser && window.navigator.userAgent.toLowerCase() const isIE = UA && /msie|trident/.test(UA) const isIE9 = UA && UA.indexOf('msie 9.0') > 0 const isEdge = UA && UA.indexOf('edge/') > 0 const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android') const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios') const isChrome = UA && /chrome/d+/.test(UA) && !isEdge const isPhantomJS = UA && /phantomjs/.test(UA) const isFF = UA && UA.match(/firefox/(d+)/)如果我們的代碼是運行在瀏覽器中,那我們肯定會拿到一個window對象,所以我們可以通過const inBrowser = typeof window !== 'undefined'這種方式來判斷環境。
而且在瀏覽器中,我們可以通過window對象拿到瀏覽器的userAgent,
不同的瀏覽器對應的userAgent也不同,像IE的userAgent總是會包含MSIE,而Chrome的userAgent會包含Chrome。類似地安卓系統的瀏覽器userAgent就會帶Android。那我們通過userAgent就可以判斷當前用的是什么瀏覽器,運行在什么操作系統上。上面的代碼中已經列舉出了對主流的瀏覽器跟操作系統的判斷,注意由于Edge瀏覽器最新版本也基于Chromium內核,所以它的userAgent也會包含Chrome,所以我們要寫const isChrome = UA && /chrome/d+/.test(UA) && !isEdge這樣的代碼來判斷當前環境是Chrome。
一般我們使用的就兩種函數,環境提供給我們的跟我們用戶自己定義的,這兩種函數在轉換成字符串時表現形式是不同的:
Array.isArray.toString() // "function isArray() { [native code] }" function fn(){} fn.toString() // "function fn(){}"環境自帶函數調用toString方法后總是會返回類似function fnName() { [native code] }格式的字符串,我們可以利用這一點來區分函數類型:
function isNative (Ctor){return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) }很多時候我們需要一個函數只被執行一次,就算它被調用多次,也只有第一次調用時會被執行,所以我們可以寫出如下代碼:
function once (fn) {let called = falsereturn function () {if (!called) {called = truefn.apply(this, arguments)}} }這樣后續再執行時我們會直接跳過,這里是使用高階函數來實現的,感興趣的可以看看我之前的文章JavaScript高級技巧。我們來測試一下這個方法:
可以看到test方法只被執行了一次。
這個我也在之前的博客中提到過的,有時候函數執行比較耗時,我們想緩存執行的結果。這樣當后續被調用時,如果參數相同,我們可以跳過計算直接返回結果。我們需要的就是實現一個cached函數,這個函數接受實際被調用的函數作為參數,然后返回一個包裝的函數。在這個cached函數里,我們可以用一個對象或者Map來緩存結果。
function cached(fn){const cache = Object.create(null);return function cachedFn (str) {if ( !cache[str] ) {let result = fn(str);cache[str] = result;}return cache[str]} }我們每個人使用的編程風格可能都不一樣,有人喜歡駝峰寫法,有人喜歡小橫杠連接符,為了解決這個問題,我們可以寫一個函數去做統一的轉換。(比如把a-b-c轉換成aBC)
const camelizeRE = /-(w)/g const camelize = cached((str) => {return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '') }) camelize('a-b-c') // "aBC"在JavaScript中,有六種基本類型(Boolean, Number, String, Null, Undefined, Symbol)跟一個對象類型,但其實對象類型是可以細分到許多類型的,一個對象可以是數組,也可以是函數等等。我們有沒有辦法獲得它確切的類型呢?
我們可以利用Object.prototype.toString把一個對象轉換成一個字符串,如果是我們用{}創建的對象,這個方法總是返回[object Object]。
而對于數組,正則表達式等環境自帶的對象類型,它們會返回不同的結果。
基于這個特性我們可以判斷一個對象是不是我們用{}創建的對象了:
function isPlainObject (obj){return Object.prototype.toString.call(obj) === '[object Object]' }而且我們注意到,Object.prototype.toString()的返回值總是以[object tag]的形式出現,如果我們只想要這個tag,我們可以把其他東西剔除掉,這邊比較簡單用正則或者String.prototype.slice()都可以。
function toRawType (value) {const _toString = Object.prototype.toStringreturn _toString.call(value).slice(8, -1) } toRawType(null) // "Null" toRawType(/sdfsd/) //"RegExp"這樣我們就可以拿到一個變量的類型了。
我們經常需要把一個值轉換成字符串,在JavaScript里面,我們有兩種方式來得到字符串:
- String()
 - JSON.stringify()
 
不過這兩種方式的實現機制是不同的:
我們里看到,他們是基于完全不同的規則去轉換字符串的,String(arg)會嘗試調用arg.toString()或者arg.valueOf(),那么那我們該用哪個比較好?
- 對于null跟undefined,我們希望把它轉成空字符串
 - 當轉換一個數組或者我們創建的對象時,我們會使用JSON.stringify
 - 如果對象的toString方法被重寫了,那我們會偏向使用String()
 - 其它情況下,一般都用String()
為了匹配上面的需求,Vue開發者是這么實現的:
function isPlainObject (obj){
return Object.prototype.toString.call(obj) === '[object Object]'
}
function toString (val) {
if(val === null || val === undefined) return ''
if (Array.isArray(val)) return JSON.stringify(val)
if (isPlainObject(val) && val.toString === Object.prototype.toString)
return JSON.stringify(val)
return String(val)
}
又是收獲滿滿的一天,通過閱讀優秀框架的代碼實現可以快速地提高我們對語言的運用,加強我們對于一些特性的理解,總結出一些編程實踐,我們的編程能力也在無形中得到質的飛躍,非常建議大家深入學習一門語言時就去閱讀用那個語言實現的優秀代碼。對于JavaScript而言我們光討論了Vue的這三個源碼文件就學到這么多東西,還有比這更開心的事嗎?希望本文也能給大家帶來一些幫助,happy coding~ 
總結
以上是生活随笔為你收集整理的js 正则是否包含某些字符串_我从Vue源码中学到的一些JS编程技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: .bat文件该图标_电脑桌面图标变成白色
 - 下一篇: stm32气压传感器 带探头的_ST S