• 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

    未完待续~~

  • 相关阅读:
    面向对象编程,其属性特性,用法等
    re正则模块细解,面向对象编程思路的优劣。
    机器人学——1.2-三维空间位姿描述
    机器人学——1.1-二维空间位姿描述
    机器人学——1.0-位置与姿态概述
    latex教程:1.2-latex现状
    latex教程: 1.1-历史
    windows安装opencv
    使用pip安装Opencv
    在Ubuntu上安装opencv-python
  • 原文地址:https://www.cnblogs.com/darrenji/p/5011400.html
Copyright © 2020-2023  润新知