一些小技巧讓JS代碼更優雅

今天翻了翻一年多前寫的代碼,感受當年年輕的本身寫下的代碼真是圖樣啊(然而如今也沒好到哪裏去 /w\)。近期看了好多函數式編程以及設計模式的書和文章,因而想分享一些讓JS代碼更優雅的小技巧。node


1、善用函數式編程

假設咱們有這樣的需求,須要先把數組foo中的對象結構更改,而後從中挑選出一些符合條件的對象,而且把這些對象放進新數組result裏。git

var foo = [{
    name: 'Stark',
    age: 21
},{
    name: 'Jarvis',
    age: 20
},{
    name: 'Pepper',
    age: 16
}]

//咱們但願獲得結構稍微不一樣,age大於16的對象:
var result = [{
    person: {
        name: 'Stark',
        age: 21
    },
    friends: []
},{
    person: {
        name: 'Jarvis',
        age: 20
    },
    friends: []
}]

從直覺上咱們很容易寫出這樣的代碼:github

var result = [];

//有時甚至是普通的for循環
foo.forEach(function(person){
    if(person.age > 16){
        var newItem = {
            person: person,
            friends: [];
        };
        result.push(newItem);
    }
})

然而用函數式的寫法,代碼能夠優雅得多:數據庫

var result = foo
    .filter(person => person.age > 16)
    .map(person => ({
        person: person,
        friends: []
    }))

還有好比在各類文章裏說爛了的數組求和:編程

var foo = [1, 2, 3, 4, 5];

//不優雅
function sum(arr){
    var x = 0;
    for(var i = 0; i < arr.length; i++){
        x += arr[i];
    }
    return x;
}
sum(foo) //15

//優雅
foo.reduce((a, b) => a + b) //15

這些只是很簡單的例子,更多關於函數式編程的知識,能夠參考這裏:設計模式

JS函數式編程指南 - GitBook數組


2、lodash裏一些很好用的東西

lodash是一個著名的JS工具庫,裏面存在衆多函數式的方法和接口,在項目中引入能夠簡化不少冗餘的邏輯。緩存

lodash中文文檔閉包

一、_.flow解決函數嵌套過深

//很難看的嵌套
a(b(c(d(...args))));

//能夠這樣改善
_.flowRight(a,b,c,d)(...args)

//或者
_.flow(d,c,b,a)(...args)

二、_.memoize加速數學計算

在寫一些Canvas遊戲或者其餘WebGL應用的時候,常常有大量的數學運算,例如:app

Math.sin(1)

Math.sin()的性能比較差,若是咱們對精度要求不是過高,咱們可使用_.memoize作一層緩存

var Sin = _.memoize(function(x){
    return Math.sin(x);
})
Sin(1) //第一次使用速度比較慢
Sin(1) //第二次使用有了cache,速度極快

注意此處傳入的x最好是整數或者較短的小數,不然memoize會極其佔用內存。

事實上,不只是數學運算,任何函數式的方法都有可緩存性,這是函數式編程的一個明顯的優勢

三、_.flatten解構嵌套數組

_.flatten([1, 2], [3, 4]); // => [1, 2, 3, 4]

這個方法和Promise.all結合十分有用處。

假設咱們爬蟲程序有個getFansList方法,它能夠根據傳入的值x,異步從粉絲列表中獲取第 x*20 到 (x+1)*20 個粉絲,如今咱們但願得到前1000個粉絲:

var works = [];
for (var i = 0; i < 50; i++) {
    works.push(getFansList(i))
}
Promise.all(works)
    .then(ArrayOfFansList=> _.flatten(ArrayOfFansList))
    .then(result => console.log(result))

前段時間寫的知乎關係網爬蟲中就能看到相似的寫法

四、_.once配合單例模式

有些函數會產生一個彈出框/遮罩層,或者負責app的初始化,所以這個函數是執行且只執行一次的。這就是所謂的單例模式,_.once大大簡化了咱們的工做量

var initialize = _.once(createApplication);
initialize();
initialize();
// 這裏實際上只執行了一次 initialize
// 不使用 once 的話須要本身手寫一個閉包

3、Generator + Promise改善異步流程

有時咱們遇到這樣的狀況:

getSomethingAsync()
    .then( a => method1(a) )
    .then( b => method2(b) )
    .then( c => method3(a,b,c) ) //a和b在這裏是undefined!!!

只用 Promise 的話,解決方法只有把 a、b 一層層 return 下去,或者聲明外部變量,把a、b放到 Promise 鏈的外部。但不管如何,代碼都會變得很難看。

用 Generator 能夠大大改善這種狀況(這裏使用了Generator的執行器co):

import co from 'co';

function* foo(){
    var a = yield getSomethingAsync();
    var b = yield method1(a);
    var c = yield method2(b);
    var result = yield method3(a,b,c);
    console.log(result);
}

co(foo());

固然,Generate 的用處遠不止如此,在異步遞歸中它能發揮更大的用處。好比咱們如今須要搜索一顆二叉樹中value爲100的節點,而這顆二叉樹的取值方法是異步的(好比它在某個數據庫中)

import co from 'co';

function* searchBinaryTree(node, value){
    var nowValue = yield node.value();
    if(nowValue == value){
        return node;
    }else if(nowValue < value){
        var rightNode = yield node.getRightNode()
        return searchBinaryTree(rightNode, value);
    }else if(nowValue > value){
        var leftNode = yield node.getLeftNode()
        return searchBinaryTree(leftNode, value);
    }
}

co(searchBinaryTree(rootNode, 100))
相關文章
相關標籤/搜索