lodash用法系列(3),使用函數


 

Lodash用來操作對象和集合,比Underscore擁有更多的功能和更好的性能。

官網:https://lodash.com/
引用:<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
安裝:npm install lodash

首先通過npm安裝lodash:
npm i --save lodash

在js文件中引用lodash:
var _ = require('lodash');

 

本系列包括:

 

lodash用法系列(1),數組集合操作
lodash用法系列(2),處理對象 
lodash用法系列(3),使用函數 
lodash用法系列(4),使用Map/Reduce轉換  
lodash用法系列(5),鏈式 
lodash用法系列(6),函數種種 

■ 使用bind方法顯式綁定this

 

function say(){
    return 'Say ' + this.what;
}

//使用bind綁定this
var sayHello = _.bind(say, {what: 'hello'});

//Say hello
console.log(sayHello());

 

■ 通過bind方式顯式綁定this從而給函數參數變量賦值,或通過函數實參給函數參數變量賦值

 

//也就是,這里的what變量既可以從this中獲取,也可以從實參中獲取
function sayWhat(what){
    //如果what沒有定義
    if(_.isUndefined(what)){
        what = this.what;
    }

    return 'Say ' + what;
}

//綁定this
var sayHello = _.bind(sayWhat, {what: 'hello'});

//不綁定this,直接輸入參數
var saySth = _.bind(sayWhat,{});

//Say hello
console.log(sayHello());

//Say haha
console.log(saySth('haha'));

 

■ 通過bind和bindAll來指定對象中字段函數的上下文

 

/這里的name為某個對象的方法名稱
function bindFunctionName(name){
    return _.bind(name,{
        first: 'darren',
        last: 'ji'
    })
}

var obj = {
    first: 'jack',
    last: 'chen',
    name: function(){
        return this.first + ' ' + this.last;
    }
};

//給obj的name函數綁定上下文this
var nameFunction = bindFunctionName(obj.name);

//jack chen
console.log(obj.name());

//darren ji
console.log(nameFunction());

//讓obj再次成為name函數的上下文
//obj的name函數就不能指定上下文了
_.bindAll(obj);

nameFunction = bindFunctionName(obj.name);

//jack chen
console.log(nameFunction());

 


以上,bindFunctionName方法內部使用bind方法來改變某個對象字段函數的上下文,然后使用bindAll方法讓obj中的name字段函數的上下文再次變為obj所在的上下文。

■ 給對象中不同的字段函數指定不同的上下文

 

function getName(){
    return this.name;
}

var obj = {
    name: 'aa',
    method1: getName,
    method2: getName,
    method3: getName
};

//讓obj中method1和method2字段對應的函數上下文為obj所在上下文
_.bindAll(obj, ['method1', 'method2']);

var method3 = _.bind(obj.method3,{name: 'bb'});

console.log(obj.method1());

console.log(obj.method2());

console.log(method3());

 

以上, 通過bindAll方法讓obj的method1和method2對應的字段函數的上下文鎖定在obj所在的上下文,通過bind放讓method3的字段函數的上下文為賦值的上下文。

■ 給對象動態(延遲)添加字段和字段函數

function workLeft(){
    return 65 - this.age + ' years';
}

var obj = {
    age: 38
};

//給obj對象綁定一個字段work
var work = _.bindKey(obj, 'work');

//給obj的work字段賦值
obj.work = workLeft;

//27 years
console.log(work());

 

以上,通過bindKey方法為obj動態、延遲添加了一個work字段,再為work字段賦值,賦給一個函數。

■ 為集合中的每個元素添加字段和字段函數

 

function workLeft(retirement, period){
    return retirement - this.age + ' ' + period;
}

var collection = [
    {age: 34, retirement: 60},
    {age: 47},
    {age: 28,retirement: 55},
    {age:41}
];

var functions = [],
    result=[];

_.forEach(collection, function(item){
    //為集合中的每個元素加上work字段和retirement字段,沒有retirment字段的就加上該字段並附上初值65
    //bindKey的返回值是work字段對應的字段函數
   functions.push(_.bindKey(item, 'work', item.retirement ? item.retirement : 65));
});



_.forEach(collection, function(item){
    //為集合中的每個元素的work字段賦上函數workLeft
    _.extend(item, {work: workLeft});
});


_.forEach(functions, function(item){
   result.push(item('years'));
});

//[ '26 years', '18 years', '27 years', '24 years' ]
console.log(result);

 

以上,第一次遍歷集合,給集合延遲綁定上work字段,以及設置retirement的字段值;第二次遍歷集合,使用extend把workLeft函數賦值給work字段;第三次遍歷函數集合,實行每一個函數把結果保存到數組中。

其中extend的用法是把一個對象中的鍵值擴展到另一個對象中去。

 

var _ = require('lodash');

var obj1 = {foo:23, bar:42};
var obj2 = {bar: 99};

//把obj2的所有字段擴展到obj1上去
//如果obj2的字段obj1已經存在,會重寫obj1上該字段的值
_.extend(obj1, obj2);

//{ foo: 23, bar: 99 }
console.log(obj1);

 

■ 給函數不同的實參,函數只有一個形參

 

function sayWhat(what){
    return 'Say ' + what;
}

var hello=_.partial(sayWhat, 'hello'),
    goodbye=_.partial(sayWhat, 'goodbye');

//Say hello
console.log(hello());

//Say goodbye
console.log(goodbye());

 

■ 給函數不同的實參, 函數有多個形參

 

function greet(greeting, name){
    return greeting + ', ' + name;
}

var hello = _.partial(greet, 'hello'),
    goodbye =_.partial(greet, 'goodbye');

//hello, morning
console.log(hello('morning'));

//hello, morning
console.log(goodbye('evening'));

 

以上,_.partial(greet, 'hello')中的hello實參對應greet函數中的第一個形參greeting。

■ 給Lo-Dash內置函數提供不同的實參

 

var collection = ['a','b','c'];

var random=_.partial(_.random,1,collection.length),
    sample=_.partial(_.sample,collection);

//2
console.log(random());

//a
console.log(sample());

 

■ 把值賦值給某個函數再形成包裹函數

 

function strong(value){
    return '<strong>' + value + '</strong>';
}

function regex(exp, val){
    exp = _.isRegExp(exp) ? exp : new RegExp(exp);
    return _.isUndefined(val) ? exp : exp.exec(val);
}

//提供給strong函數,這個wrapper的值
var boldName =_.wrap('Marianne', strong),
    //提供給regex這個函數,這個wrapper的形參exp對應的值
    getNumber=_.wrap('(\\d+)', regex);

//<strong>Marianne</strong>
console.log(boldName());

//123
console.log(getNumber('abc123')[1]);

 


以上,boldName和getNumber方法就像包裹在strong和regext之上的一個函數。


■ 把函數賦值給某個函數再形成包裹函數

 

//取集合中的一個
var user = _.sample(['aa', 'bb']);

var allowed = ['aa', 'dd'];

function permission(func) {
    if (_.contains(allowed, user)) {
        return func.apply(null, _.slice(arguments, 1));
    }
    throw  new Error('denied');
}

function eccho(value) {
    return value;
}

var welcome = _.wrap(eccho, permission);

//are u here 或拋異常
console.log(welcome('are u here'));

 

■ 限制函數執行的次數,異步場景

 

var complete= 0,
    collection= _.range(9999999),
    progress= _.noop;//return undefined

function work(value){
    progress();
}

function reportProgress(){
    console.log(++complete + '%');//記錄進度
    //after的第一個形參表示先執行n-1次reportProgress,花費n-1次相當的時間,到n次的時候正真執行reportProgress
    //本方法也實現了遞歸
    progress=complete<100? _.after(0.01*collection, reportProgress) : _.noop;
}

//先把進度啟動起來
reportProgress();

//遍歷集合的過程就是執行progress方法的過程
//而progress的執行取決於變量complete的值是否小於100
_.forEach(collection, work);

 

以上,限制了progress函數執行的次數。progress執行的快慢,即控制台顯示百分比的快慢由after函數決定,progress的執行次數由變量complete的值決定。此外,使用after方法又使reportProgress實現了遞歸。

■ 限制函數執行的次數,同步異步混合場景

 

/這里的回調函數會等到所有的異步操作結束后才運行
function process(arr, callback) {
    var sync = _.after(arr.length, callback);
    
    //這里開始異步
    _.forEach(arr, function () {
        setTimeout(sync, _.random(2000));
    });
    
    //這里的同步方法先執行
    console.log('timeouts all set');


}

process(_.range(5), function () {
    console.log('callbacks completed');
});

//結果:
//timeouts all set
//callbacks completed

 

■ 限制函數執行一次

 

function getLeader(arr){
    return _.first(_.sortBy(arr, 'score').reverse());
}

var collection = [
    { name: 'Dana', score: 84.4 },
    { name: 'Elsa', score: 44.3 },
    { name: 'Terrance', score: 55.9 },
    { name: 'Derrick', score: 86.1 }
];

//leader函數只執行一次,結果被緩存起來
var leader = _.once(getLeader);

//{ name: 'Derrick', score: 86.1 }
console.log(leader(collection));

 

■ 緩存函數

function toCelsius(degrees){
    return (degrees - 32) * 5 / 9;
}

//緩存起來
var celsius =_.memoize(toCelsius);

//31.67 C
console.log(toCelsius(89).toFixed(2) + ' C');

//31.67 C
console.log(celsius(89).toFixed(2) + ' C');

 

■ 緩存函數,使用緩存函數中的變量值

 

function toCelsius(degrees) {
    return (degrees - 32) * 5 / 9;
}

function toFahrenheit(degrees) {
    return degrees * 9 / 5 + 32;
}

//根據indicator,F或C選擇相應的方法
function convertTemp(degrees, indicator){
    return indicator.toUpperCase() === 'C' ?
    toCelsius(degrees).toFixed(2) + ' C' :
    toFahrenheit(degrees).toFixed(2) + ' F';
}

//緩存
var convert = _.memoize(convertTemp, function(degrees, indicator){
   return degrees + indicator;
});

//192.20 F
console.log(convert(89, 'F'));

 


■ 延遲調用函數,不帶參數

 

var cnt=-1,
    max=5,
    interval=3000,
    timer;

function poll(){
    if(++cnt<max){
        console.log('polling round ' + (cnt + 1));
        timer= _.delay(poll,interval);
    }else{
        clearTimeout(timer);
    }
}

//polling round 1
//polling round 2
//polling round 3
//polling round 4
//polling round 5
poll();

 

■ 延遲調用函數,帶參數

 

function sayHi(name, delay){

    //函數內部定義一個函數
    function sayHiImp(name){
        console.log('hi '+name);
    }

    if(_.isUndefined(delay)){
        _.delay(sayHiImp,1,name);
    } else{
        _.delay(sayHiImp, delay, name);
    }
}

sayHi('Darren');

 

■ 所有堆棧被清理后延遲執行某個函數

 

function cal(){
    _.forEach(_.range(Math.pow(2, 25)), _.noop);
    console.log('done');
}

_.defer(cal);

//computing...
//done
console.log('computing...')

 

■ 通過包裹函數延遲執行某個函數

 

function deferred(func){
    return _.defer.apply(_,([func]).concat(_.slice(arguments,1)));
}

function setTitle(title){
    console.log('Title: "' + title + '"');
}

//app為傳入的對象,包含state字段
function setState(app){
    console.log('State: "' + app.state + '"');
}

//分別包裹下setTitle和setState函數
var title =_.wrap(setTitle, deferred),
    state=_.wrap(setState, deferred),
    app={state: 'stopped'};

//Title: "Home"
//State: "started"
title('Home');
state(app);
app.state = 'started';

 

■ 實現Throttle

Throttle的存在是為了回答"在某段時間內一個函數需要被怎樣執行"這個問題。throttle經常用來更好地控制事件。通常的事件,在一個標准時間內只執行一次,標准時間,一次,這些都是固定的,而throttle可以控制自定義的時間段內執行的次數。

 

var ele = document.querySelector('#container');
var onMouseMove = _.throttle(function(e){
    console.log('x: ' + e.clintX + 'y:' + e.clientY);
}, 750); 


//給元素加上trottle事件
ele.addEventListener('mousemove', onMouseMove);

//給窗口加事件
window.addEventListener('haschange', function cleanup(){
    ele.removeEventListener('mousemove', onMouseMove);
    window.removeEventListener('mousemove', cleanup);
});

 


■ 實現Debounce

Debounce的存在也是為了回答"在某段時間內一個函數需要被怎樣執行"這個問題。debounce可以控制自定義時間段內完成一組動作,就好像把多個動作放在了一個單元中。

 

var size=1500;

function log(type, item){
    console.log(type + ' ' + item);
}

var debounced = _.debounce(_.partial(log, 'debounced'),1),
    throttled=_.throttle(_.partial(log,'throttled'),1);

//throttled 0
//throttled 1
//throttled 3
//throttled 858
//debounced 1499
//throttled 1499
_.forEach(_.range(size), debounced);
_.forEach(_.range(size), throttled);

 

■ 合成函數

 

//面團
function dough(pizza){
    if(_.isUndefined(pizza)){
        pizza={};
    }

    return _.extend({dough:true},pizza);
}

//調料
function sauce(pizza){
    if(!pizza.dough){
        throw new Error('Dough not ready');
    }
    return _.extend({sauce:true}, pizza);
}

//奶酪
function cheese(pizza){
    if(!pizza.sauce){
        throw  new Error('Sauce not ready');
    }

    return _.extend({cheese:true}, pizza);
}

var pizza = _.compose(cheese, sauce, dough);

//{ cheese: true, sauce: true, dough: true }
console.log(pizza());

如果想控制合成的順序:

var pizza = _.flow(dough, sauce, cheese);

//{ cheese: true, sauce: true, dough: true }
console.log(pizza());

 


也可以也成這樣:

 

function makePizza(dough, sauce, cheese){
    return {
        dough: dough,
        sauce: sauce,
        cheese: cheese
    };
}

function dough(pizza){
    return pizza(true);
}

function sauceAndCheese(pizza){
    return pizza(true, true);
}

var pizza = _.curry(makePizza);

//{ dough: true, sauce: true, cheese: true }
console.log(sauceAndCheese(dough(pizza)));

 

■ 從右到左執行一系列函數

 

var _ = require('lodash');

function square(n){
    return n*n;
}

var addSuare = _.flowRight(square, _.add);

//9
console.log(addSuare(1,2));

 


■ 從右開始依次輸入形參對應的實參

 

var greet = function(greeting, name){
    return greeting + ' ' + name;
}

//從右開始依次輸入形參對應的實參
var greetDarren = _.partialRight(greet, 'Darren');

//hi Darren
console.log(greetDarren('hi'));

//從右開始使用形參的對應的實參占位符
var sayGoodMorning = _.partialRight(greet, 'Good Morning', _);

//Good Morning Darren
console.log(sayGoodMorning('Darren'));

 

參考資料:lodash essentials

 

未完待續~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM