( 譯、持續更新 ) JavaScript 上分小技巧(二)

来源:http://www.cnblogs.com/ys-ys/archive/2016/01/30/5170653.html
-Advertisement-
Play Games

考慮到文章過長,不便於閱讀,這裡分出第二篇,如有後續,每15個知識點分為一篇... #29 - 使用緩存的記憶讓遞歸函數加速運行波非那切數列(Fibonacci sequence)想必大家都不陌生(針對學霸而言,在這之前本獸完全不知道這是個什麼鬼,雖然經常會用到遞歸),我們可以在20秒內寫出以下的函


考慮到文章過長,不便於閱讀,這裡分出第二篇,如有後續,每15個知識點分為一篇...  

#30 - 將true的/false的值轉換成boolean類型
你可以通過!!操作將為true/為false的值轉換為boolean類型。

!!"" // false
!!0 // false
!!null // false
!!undefined // false
!!NaN // false

!!"hello" // true 字元串長度不為0時
!!1 // true
!!{} // true
!![] // true

          ### 2016-01-31 更新 ###          

#29 - 使用緩存的記憶讓遞歸函數加速運行
波非那切數列(Fibonacci sequence)想必大家都不陌生(針對學霸而言,在這之前本獸完全不知道這是個什麼鬼,雖然經常會用到遞歸),我們可以在20秒內寫出以下的函數:

var fibonacci = function(n){
    return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2);
}

它確實是運行了,但是效率並不高。它做了大量的重覆計算的工作,我們可以通過緩存先前計算結果的方法來加快速度。

var fibonacci = (function(){
    var cache = {
        0: 0,
        1: 1
    };
    return function(n){
        return n <= 1 ? cache[n] : (cache[n] = cache[n-1] + cache[n-2]);
    }
})()

同時,我們可以定義一個高階函數,該高階函數將接收一個函數作為參數並且該參數函數會返回一個已記憶的緩存版本。

var memoize = function(func){
    var cache = {};
    return function(){
        var key = Array.prototype.slice.call(arguments).toString();
        return key in cache ? cache[key] : (cache[key] = func.apply(this,arguments));
    }
}
fibonacci = memoize(fibonacci);

我們可以在很多情況下使用memoize()
GCD(最大公約數)

var gcd = memoize(function(a,b){
    var t;
    if (a < b) t=b, b=a, a=t;
    while(b != 0) t=b, b = a%b, a=t;
    return a;
})
gcd(27,183); //=> 3

階乘計算

var factorial = memoize(function(n) {
    return (n <= 1) ? 1 : n * factorial(n-1);
})
factorial(5); //=> 120

#28 - 柯里化(局部套用)和局部應用
柯里化(局部套用)
柯里化將以下函數

f:X*Y_-R

轉換為以下形式的函數:

f':X_>(Y->R)

我們僅調用f'配合第一個參數,來代替配合兩個參數來調用f。返回的結果是一個我們配合第二個參數調用的函數並且返回結果值的函數。
因此,如果我們調用未柯里化的函數f:

f(3,5)

然後調用柯里化的函數f':

f(3)(5)

案例:
未柯里化的add()

function add(x, y) {
  return x + y;
}
add(3, 5);   // _> 8

柯里化的add()

function addC(x) {
  return function (y) {
    return x + y;
  }
}
addC(3)(5);   // _> 8

柯里化的運演算法則:
柯里化取一個二元函數,返回一個包含一元函數的一元函數。
柯里化: 

(X × Y → R) → (X → (Y → R))

javascript 代碼:

function curry(f) {
  return function(x) {
    return function(y) {
      return f(x, y);
    }
  }
}

局部應用:
局部應用將以下函數

f:X*Y_-R

給定一個固定的值,傳入一個參數,以此產生個新函數:

f':Y->R

f'和f不一樣,但是他只需要填入第二個參數,這就是為什麼f'比f的參數數量少一個。
案例:
綁定函數的第一個參數通過函數plus5進行與加5:

function plus5(y) {
  return 5 + y;
}
plus5(3);  // _> 8

局部應用的運演算法則:
局部調用取一個二元函數和一個值,並且產生一個一元函數。
局部: ((X × Y → R) × X) → (Y → R)
javascript 代碼:

function partApply(f, x) {
  return function(y) {
    return f(x, y);
  }
}

          ### 2016-01-30 更新 ###          

#27 - Short-circuit evaluation(沒看懂是個什麼東東,於是暫且不翻譯它)
Short-circuit evaluation指出,僅當第一個參數的論據不足以確定表達式的值的時候才執行第二個參數:當第一個參數的AND(&&)函數返回false,整體value也將是false;當第一個參數的OR(||)為true,則整體的值也是true。
以下是test情況和isTrue函數和isFalse函數:

var test = true;
var isTrue = function(){
  console.log('Test is true.');
};
var isFalse = function(){
  console.log('Test is false.');
};

使用AND(&&)邏輯:
// 正常語句.

// 正常語句.
if(test){
  isTrue();    // _> true
}
// 以上的功能使用 '&&' 完成
( test && isTrue() );  // _> true

使用OR(||)邏輯:

test = false;
if(!test){
  isFalse();    // _> false.
}
( test || isFalse());  // _> false.

邏輯OR(||)語句同時可以用來給函數的參數設置預設值:

function theSameOldFoo(name){ 
    name = name || 'Bar' ;
    console.log("My best friend's name is " + name);
}
theSameOldFoo();  // My best friend's name is Bar
theSameOldFoo('Bhaskar');  // My best friend's name is Bhaskar

邏輯AND(&&)語句同時可以用來避免使用undefined的屬性的例外,案例:

var dog = { 
  bark: function(){
     console.log('Woof Woof');
   }
};
// 調用 dog.bark();
dog.bark(); // Woof Woof.
//但是如果bog是undefined, dog.bark() 會拋出個錯誤 "Cannot read property 'bark' of undefined."
// 我們可以使用 && 來避免這種情況.
dog&&dog.bark();   // 這樣的話,只有在bog存在的時候才會調用 dog.bark()

         ### 2016-01-28 更新 ###          

#26 - 字元串列表的過濾和排序
你可能會有一個很大的字元串列表,你需要去對這個數組進行過濾和按字母排序。
在我們的例子中,我們將使用JavaScript來保存一系列的不同語言的關鍵字,但正如你所見,他們之間有重覆,也沒按字母順序進行排序。所以這是一個完美的字元串列表(數組)來測試這個JavaScript小技巧。

var keywords = ['do', 'if', 'in', 'for', 'new', 'try', 'var', 'case', 'else', 'enum', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'delete', 'export', 'import', 'return', 'switch', 'typeof', 'default', 'extends', 'finally', 'continue', 'debugger', 'function', 'do', 'if', 'in', 'for', 'int', 'new', 'try', 'var', 'byte', 'case', 'char', 'else', 'enum', 'goto', 'long', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'final', 'float', 'short', 'super', 'throw', 'while', 'delete', 'double', 'export', 'import', 'native', 'public', 'return', 'static', 'switch', 'throws', 'typeof', 'boolean', 'default', 'extends', 'finally', 'package', 'private', 'abstract', 'continue', 'debugger', 'function', 'volatile', 'interface', 'protected', 'transient', 'implements', 'instanceof', 'synchronized', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof', 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'await', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof'];

因為我們不打算改變原始列表,我們將使用一個名為filter的高階函數,它會基於我們傳遞給它的函數進行過濾並且返回一個經過過濾的新數組。判斷語句會將當前關鍵字的索引與新列表中的索引進行比較,如果索引能匹配上,則會將這項推到新的數組中。
最後,我們要使用排序功能以一個比較函數作為唯一的參數來對過濾後的列表進行排序,返回一個按字母順序排序的列表。

var filteredAndSortedKeywords = keywords
  .filter(function (keyword, index) {
      return keywords.indexOf(keyword) === index;
    })
  .sort(function (a, b) {
      if (a < b) return -1;
      else if (a > b) return 1;
      return 0;
    });

ES6(ES 2015)使用arrow函數將會看上去簡單些:

const filteredAndSortedKeywords = keywords
  .filter((keyword, index) => keywords.indexOf(keyword) === index)
  .sort((a, b) => {
      if (a < b) return -1;
      else if (a > b) return 1;
      return 0;
    });

這是進行過濾和排序後的關鍵字列表:

console.log(filteredAndSortedKeywords);
// ['abstract', 'arguments', 'await', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', 'enum', 'eval', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import', 'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', 'with', 'yield']

          ### 2016-01-27 更新 ###             

#25 - 使用立即執行函數表達式

被稱為"Iffy"(IIFE-立即執行函數表達式)是一個在JavaScript中挺有用的並且能夠立即調用的匿名函數。

(function() {
// Do something
})()

用括弧將匿名函數包裹起來,會將函數匿名函數轉化成一個函數表達式或者變數表達式。因此,我們用一個未命名的函數表達式代替全局作用域下(或其他任何地方)的簡單的匿名函數。

類似的,我們也可以為他創建一個名稱,立即執行函數:

(someNamedFunction = function(msg) {
    console.log(msg || "Nothing for today !!")
}) (); // 輸出 -> Nothing for today !!
someNamedFunction("Javascript rocks !!"); // 輸出 -> Javascript rocks !!
someNamedFunction(); // 輸出 -> Nothing for today !!

更多的信息,點擊以下的鏈接 link1 link2
演示:jsPerf

          ### 2016-01-26 更新 ###          

#24 - 使用 === 代替 ==
==(或者!=)做對比的時候會將進行對比的兩者轉換到同一類型再比較。===(或者!==)則不會,他會將進行對比的兩者做類型對比和值對比,相對於 == ,=== 的對比會更加嚴謹。

[10] == 10     // true
[10] === 10    // false
"10" == 10     // true
"10" === 10    // false
[] == 0        // true
[] === 0       // false
"" == false    // true 但是 true == "a" 是false
"" === false   // false

#23 - 轉換數值的更加的方法
將字元串轉換為數字是非常常見的。最簡單和最快的(jspref)方式來實現,將使用+(加)演算法。

var one = '1';
var numberOne = +one; // Number 1

你也可以使用-(減號)演算法的轉換類型並且變成負數值。

var one = '1';
var negativeNumberOne = -one; // Number -1

#22 - 清空一個數組
你定義一個數組,並希望清空它的內容。通常,你會這樣做:

var list = [1, 2, 3, 4];
function empty() {
    //清空數組
    list = [];
}
empty();

但是還有一種更高性能的方法。
你可以使用這些代碼:

var list = [1, 2, 3, 4];
function empty() {
    //清空數組
    list.length = 0;
}
empty();

· list = [] 將一個變數指定個引用到那個數組,而其他引用都不受影響。這意味著,對於先前數組的內容的引用仍然保留在記憶體中,從而導致記憶體泄漏。
· list.length = 0 刪除數組內的所有東西,這不需要引用任何其他的東西
然而,如果你有一個copy的數組(A和copy-A),如果你使用list.length = 0 刪除其內容,副本也會失去它的內容。

var foo = [1,2,3];
var bar = [1,2,3];
var foo2 = foo;
var bar2 = bar;
foo = [];
bar.length = 0;
console.log(foo, bar, foo2, bar2);
//[] [] [1, 2, 3] []

StackOverflow上的更多詳情:difference-between-array-length-0-and-array

#21 - 對數組排序進行"洗牌"(隨機排序)
這段代碼在這裡使用Fisher Yates洗牌演算法給一個指定的數組進行洗牌(隨機排序)。

function shuffle(arr) {
    var i,
        j,
        temp;
    for (i = arr.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    return arr;    
};

案例:

var a = [1, 2, 3, 4, 5, 6, 7, 8];
var b = shuffle(a);
console.log(b);
// [2, 7, 8, 6, 5, 3, 1, 4]

網友@幻天芒在評論中提供的隨機技巧:

arr.sort(function(){return Math.random() - 0.5;});

實測可用,感謝分享!(性能方面相差3-4倍之間,shuffle執行比較快,根據個人喜歡來使用)

#20 - 返回對象的函數能夠用於鏈式操作
當創建面向對象的JavaScript對象的function時,函數返回一個對象將能夠讓函數可鏈式的寫在一起來執行。

function Person(name) {
  this.name = name;
  this.sayName = function() {
    console.log("Hello my name is: ", this.name);
    return this;
  };
  this.changeName = function(name) {
    this.name = name;
    return this;
  };
}
var person = new Person("John");
person.sayName().changeName("Timmy").sayName();
//Hello my name is:  John
//Hello my name is:  Timmy

#19 - 字元串安全連接
假設你有一些類型未知的變數,你想將它們連接起來。可以肯定的是,演算法操作不會在級聯時應用:

var one = 1;
var two = 2;
var three = '3';
var result = ''.concat(one, two, three); //"123"

這樣的連接不正是你所期望的。相反,一些串聯和相加可能會導致意想不到的結果:

var one = 1;
var two = 2;
var three = '3';
var result = one + two + three; //"33"  而不是 "123"

談到性能,對join和concat進行比較,他們的執行速度是幾乎一樣的。你可以在MDN瞭解更多與concat相關的知識

#18 - 更快的四捨五入
今天的技巧是關於性能。見到過雙波浪線"~~"操作符嗎?它有時也被稱為double NOT運算符。你可以更快的使用它來作為Math.floor()替代品。為什麼呢?
單位移~將32位轉換輸入-(輸入+1),因此雙位移將輸入轉換為-(-(輸入+1)),這是個趨於0的偉大的工具。對於輸入的數字,它將模仿Math.ceil()取負值和Math.floor()取正值。如果執行失敗,則返回0,這可能在用來代替Math.floor()失敗時返回一個NaN的時候發揮作用。

// 單位移
console.log(~1337)    // -1338
// 雙位移
console.log(~~47.11)  // -> 47
console.log(~~-12.88) // -> -12
console.log(~~1.9999) // -> 1
console.log(~~3)      // -> 3
//失敗的情況
console.log(~~[]) // -> 0 
console.log(~~NaN)  // -> 0
console.log(~~null) // -> 0
//大於32位整數則失敗
console.log(~~(2147483647 + 1) === (2147483647 + 1)) // -> 0

雖然~~可能有更好的表現,為了可讀性,請使用Math.floor()。

#17 - Node.js:讓module在沒被require的時候運行
在node里,你可以根據代是運行了require('./something.js')還是node something.js,來告訴你的程式去做兩件不同的事情。如果你想與你的一個獨立的模塊進行交互,這是很有用的。

if (!module.parent) {
    // 運行 `node something.js`
    app.listen(8088, function() {
        console.log('app listening on port 8088');
    })
} else {
    // 使用 `require('/.something.js')`
    module.exports = app;
}

更多信息,請看the documentation for modules

#16 - 給回調函數傳遞參數
在預設情況下,你無法將參數傳給回調函數,如下:

function callback() {
  console.log('Hi human');
}
document.getElementById('someelem').addEventListener('click', callback);

你可以採取JavaScript閉包的優點來給回調函數傳參,案例如下:

function callback(a, b) {
  return function() {
    console.log('sum = ', (a+b));
  }
}
var x = 1, y = 2;
document.getElementById('someelem').addEventListener('click', callback(x, y));

什麼是閉包呢?閉包是指一個針對獨立的(自由)變數的函數。換句話說,閉包中定義的函數會記住它被創建的環境。瞭解更多請參閱MDN所以這種方式當被調用的時候,參數X/Y存在於回調函數的作用域內。
另一種方法是使用綁定方法。例如:

var alertText = function(text) {
  alert(text);
};
document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello'));

兩種方法在性能上有一些略微區別,詳情參閱jsperf

          ### 2016-01-25 更新 ###          

上一篇地址:( 譯、持續更新 ) JavaScript 上分小技巧(一)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 工廠顧名思義就是生產商品的,造紙廠生產紙,鞋廠生產鞋,在面向對象的編程思想中,那麼我們這裡設計模式中的簡單工廠就是為我們生產對象的。 在介紹簡單工廠模式之前,我們先來瞭解一下怎麼生產一個商品,以生產筆記本為例,我們怎麼讓一個筆記本生產出來呢,最簡單的方式,就是我們通過實例化的方式,就能產生一個筆記本
  • 2.14 pluck 2.14.1 語法: _.pluck(list, key) 2.14.2 說明: pluck方法根據key對list數組中的每個對象進行檢索,返回檢索成功的屬性值,否則返回undefined,返回一個數組 list為數組和arguments(數組中需要是對象類似:{x: 1})
  • 這章本來準備寫成jQuery的表單操作和表格操作的。 然而昨天吧jQuery的表單操作看完,發現全部在炒之前章節的剩飯,所以就沒寫出來。 那麼今天就來看看表格吧。 因為平常做的都是公司的內部管理系統,所以說數據表格用到的還是比較多的。那麼在這裡寫出來說不定還能用上。 關於jQuery的表格應用 隔行
  • 本文同步至微信公眾號:http://mp.weixin.qq.com/s?__biz=MzAxMzgwNDU3Mg==&mid=401616238&idx=1&sn=3c6e965283c632e9035875be43e6a305&scene=0#wechat_redirect 二維碼: 一直覺得c
  • 目前做了一個easyui項目需要顯示多級菜單,菜單配置到資料庫中,因此每級菜單都需要到資料庫中取,用了jQuery EasyUI方便多了。 效果體驗:http://hovertree.com/texiao/jeasyui/2/下載:http://hovertree.com/h/bjaf/kbtdmn
  • JavaScript的主要特點:解釋性,基於對象,事件驅動,跨平臺,安全性 JavaScript的位置:①在HTML文檔里的<script></script>標簽中直接編寫腳本代碼程式,使用最多的情況(一般放在head裡面)。<script>標簽的位置並不是固定的,可以出現在<head>或者<bod
  • 簡單介紹下NodeJS現有API。 Assert(斷言):該模塊用於編寫程式的單元測試用例。 Buffer(緩衝塊) :處理二進位數據。 C/C++ Addons(拓展):Addons插件就是動態連接庫。 Child Processes(子進程):提供了類似 popen(3) 的處理三向數據流(st
  • Hack是針對不同的瀏覽器去寫不同的CSS樣式,從而讓各瀏覽器能達到一致的渲染效果,那麼針對不同的瀏覽器寫不同的CSS CODE的過程,就叫CSS HACK,同時也叫寫CSS Hack。然後將Hack放在瀏覽器特定的CSS文件中,讓其符合條件的瀏覽器解析這些代碼,就如前面所說的條件樣式,我們將CSS
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...