探討奇技淫巧

探討奇技淫巧

起源

在工程實踐中,咱們經常會遇到一些奇技淫巧。所謂奇技淫巧,就是官方在設計或者實踐中並未想象出的代碼風格或者使用場景。其實也就是相似於 react 的 hoc,原本源自於社區,可是該方案卻成爲了官方確定的方案。那麼究竟應不該在平時學習呢?究竟應不該該在工程中使用呢,或者使用怎麼樣的奇技淫巧。前端

兩年前。我尚未畢業,在大學的最後一個學期中選擇了進入前端,同時,被吸引到前端陣營中一個不得不說的緣由就是 js 的奇技淫巧,同時我的是一個比較獵奇的人,因此就學了不少關於 js 的奇技淫巧。react

如今這些奇技淫巧要麼變成了這門語言不可或缺的一部分,要麼隨着時間的推移而消失,還有一些在不知不覺中卻忘記了,既然此次的文章是介紹這方面的知識,也就多介紹一下以前學習的一些例子。git

~ 運算符 + indexOf

在 es6 includes 還沒有推行以前,咱們判斷判斷字符串或者數組包含只能使用 indexOf 這個方法,可是 indexOf 返回的確實元素的索引,若是不存在則返回 -1。 由於在以前寫 c 語言的時候,咱們每每使用 0 表明成功,1 2 3表明着不一樣的錯誤。由於0是獨一無二的。在類c的語言中是具備 truthy falsy 這個概念。並不指代bool的 true 與 false。es6

下表表明了js 的 truthy 以及 falsy。github

變量類型 falsy truthy
布爾 false true
字符串 " " 非空字符串
數值 0 NaN 任何不爲falsy的數值
null
undefined
對象(數組), {} 以及 []

對於數值而言,咱們知道 0 對於數值是惟一的,而 -1不是。那麼咱們能夠經過 ~ 運算符來把-1 變爲 0.typescript

~-1
// 0
~1
//-2

複製代碼

解釋下
對每個比特位執行非(NOT)操做。NOT a 結果爲 a 的反轉(即反碼)。數組

9 (base 10) = 00000000000000000000000000001001 (base 2)   
         
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)
複製代碼

由於在計算機中第一位表明着 符號位置。瀏覽器

同時簡單理解。對任一數值 x 進行按位非操做的結果爲 -(x + 1)。 也就是說經過 ~ 能夠把 -1(且僅僅只是 -1) 變爲 falsy。性能優化

var str = 'study pwa';
var searchFor = 'a';

// 這是 if (str.indexOf('a') > -1) 或者 if ( -1 * str.indexOf('a') <= 0) 條件判斷的另外一種方法
if (~str.indexOf(searchFor)) {
  // searchFor 包含在字符串 str 中
} else {
  // searchFor 不包含在字符串 str 中
}
複製代碼

惰性函數

沒學習惰性函數時候,若是建立 xhr,每次都須要判斷。bash

function createXHR(){
  var xmlhttp;
  try{
    //firfox,opear,safari
    xmlHttp=new XMLHttpRequest();
  } catch(e) {
    try{
      xmlHttp=new ActiveXobject('Msxm12.XMLHTTP');
    } catch(e) {
      try{
        xmlHttp=new ActiveXobject("Microsoft.XMLHTTP")
      } catch(e) {
        alert("您的瀏覽器不支持AJAX")
        return false;
      }
    }
  }
  return xmlHttp;
}


複製代碼

在學習完了惰性函數以後

function createXHR(){
  // 定義xhr,
  var xhr = null;
  if (typeof XMLHttpRequest!='undefined') {
    xhr=new XMLHttpRequest();
    createXHR=function(){
      return new XMLHttpRequest();  //直接返回一個懶函數
    }
  } else {
    try{
      xhr=new ActiveXObject("Msxml2.XMLHTTP");
      createXHR=function(){
        return new ActiveXObject("Msxml2.XMLHTTP");
      }
    } catch(e) {
      try{
        xhr =new ActiveXObject("Microsoft.XMLHTTP");
        createXHR=function(){
          return new ActiveXObject("Microsoft.XMLHTTP");
        }
      } catch(e) {
        createXHR=function(){
          return null
        }
      }        
    }
  }
  // 第一次調用也須要 返回 xhr 對象,因此須要返回 xhr
  return xhr;
}
複製代碼

若是代碼被使用於兩次調用以上則會有必定的性能優化。第一次調用時候 把 xhr 賦值並返回,且在進入層層 if 判斷中把 createXHR 這個函數賦值爲其餘函數。

// 若是瀏覽器中有 XMLHttpRequest 對象在第二次調用時候
  createXHR=function(){
    return XMLHttpRequest();  //直接返回一個懶函數
  }
複製代碼

該方案能夠在不須要第二個變量的狀況下直接對函數調用進行優化。同時對於調用方也是透明的,不須要修改任何代碼。

擴展運算符號的另類用法

在最近的學習中,我看到了一篇關於 ... (擴展運算符)的另類用法,The shortest way to conditional insert properties into an object literal, 這篇文章介紹瞭如何最簡化的寫出條件性插入對象屬性。

在沒有看過這篇文章時會寫出以下代碼:

// 得到手機號
const phone = this.state.phone

const person = {
  name: 'gogo',
  age: 11
}

// 若是手機號不爲空,則添加到person中
if (phone) {
  person.phone = phone
}

複製代碼

可是,看完該文章以後能夠寫出這樣的代碼

// 得到手機號
const phone = this.state.phone

const person = {
  name: 'gogo',
  age: 11,
  ...phone && {phone}
}

複製代碼

上面的代碼與該代碼功能相同,可是代碼量卻減小不少。

要理解上述代碼的運行原理,首先先介紹一下 ... 運算符, 對象的擴展運算符(...)用於取出參數對象的全部可遍歷屬性,拷貝到當前對象之中。

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

// 若是是 空對象,沒有任何效果
{...{}, a: 1}
// { a: 1 }

// 若是擴展運算符後面不是對象,則會自動將其轉爲對象。可是若是對象沒有屬性,就會返回空對象
// {...1} 會變爲 {...Object(1)} 可是由於沒有屬性
{...1} 
// {}

// 同理獲得
{...undefined} {...null} {...true}
// 都會變爲 {}
複製代碼

能夠參考 阮一峯的 es6入門的對象的擴展運算符

原理是由於代碼能夠以下理解:

const obj = {
  ...(phone && {phone})
}

// 若是 phone 有數據,&& 執行則會變爲
const obj = {
  ...{phone}
}
// 而對象擴展運算符 執行就會變爲
const obj = {
  phone
}

可是 若是 phone 爲空字符串或者其餘 falsy 數據,則代碼會直接短路
const obj = {
  ...false
  ...null
  ...0
  ...undefined
}
則不會添加任何屬性進入對象

複製代碼

討論與思考

關於 ~ 操做符 + indexOf 其實加深了對位運算與比特位的理解。可是在es6以後咱們徹底可使用 includes。徹底能夠再也不使用~indexOf。

對於惰性函數,在typescript中,該代碼是不可使用的。固然,咱們能夠經過函數變量以及增長代碼實現上述功能。

function createXHR(){}
// 修改成
let createXHR = function() {
  // ...
}
複製代碼

這裏也能夠看出 ts 不承認函數聲明的函數名是一個變量。

對於擴展運算符的特殊用法。關於 typescript 使用,上述代碼是能夠在ts中使用的,不過不可使用 &&,要使用 三元運算符

{
   ...phone ? {phone} : {}
}
複製代碼

可是不建議在ts中使用,由於該代碼不會被代碼ts檢測到。

const phone = '123'

// 定義接口
interface Person {
  name: string;
}

// 不會爆出 error
const person: Person = {
  name: 'ccc',
  ...phone ? {phone} : {}
}
複製代碼

該代碼是與 ts 嚴重相悖的,ts首要就是類型定義,而使用該代碼逃出了 ts 的類型定義,這個對於語言上以及工程維護上是沒法接受的。 一樣的代碼,我認爲 js 是能夠接受的(可是未必要在工程中使用),可是 ts 確實沒法接受的,這也是不一樣的語言之間的差別性。

在關於這片文章的評論中,最大的論點在於 爲何要使用最簡的代碼,最好的代碼應該是不言自明的。

而做者也相對而言探討了本身的一些見解,應該學習一些本身不理解的東西。同時若是一個東西可以解釋前因後果,徹底能夠從原理性解釋,那麼值得學習與使用。同時我我的實際上是和做者持着相贊成見的。

總結

  • js 是一門靈活的語言(手動滑稽)。
  • 應該多學習一些奇技淫巧,由於不少奇技淫巧每每表明一些混合的知識,每每會有一些新奇的思考與體驗(怎麼我想不出來?)同時,在別人使用了奇技淫巧時候我能夠迅速理解。
  • 在項目中是否使用此類代碼要取決團隊類型,以及項目體系,並不是我的喜惡。

鼓勵一下

若是你以爲這篇文章不錯,但願能夠給與我一些鼓勵,在個人 github 博客下幫忙 star 一下。 博客地址

參考資料

The shortest way to conditional insert properties into an object literal

對象的擴展運算符

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息