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); //
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()); //
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); //
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

未完待续~~

lodash用法系列(3),使用函数的更多相关文章

  1. lodash用法系列(6),函数种种

    Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...

  2. lodash用法系列(5),链式

    Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...

  3. lodash用法系列(4),使用Map/Reduce转换

    Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...

  4. lodash用法系列(2),处理对象

    Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...

  5. lodash用法系列(1),数组集合操作

    Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...

  6. 深入理解javascript函数系列第一篇——函数概述

    × 目录 [1]定义 [2]返回值 [3]调用 前面的话 函数对任何一门语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即 ...

  7. 深入理解javascript函数系列第二篇——函数参数

    × 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...

  8. SSE 系列内置函数中的 shuffle 函数

    SSE 系列内置函数中的 shuffle 函数 邮箱: quarrying@qq.com 博客: http://www.cnblogs.com/quarryman/ 发布时间: 2017年04月18日 ...

  9. php从入门到放弃系列-03.php函数和面向对象

    php从入门到放弃系列-03.php函数和面向对象 一.函数 php真正的威力源自它的函数,内置了1000个函数,可以参考PHP 参考手册. 自定义函数: function functionName( ...

随机推荐

  1. mysqldump只导出表结构或只导出数据的实现方法【转】

    mysql mysqldump 只导出表结构 不导出数据 mysqldump --opt -d 数据库名 -u root -p > xxx.sql 备份数据库 #mysqldump 数据库名 & ...

  2. 关于iTerm2中颜色配置及快捷键使用技巧(亲测)

    https://github.com/mbadolato/iTerm2-Color-Schemes http://chriskempson.com/projects/base16 (同事用的) 按照g ...

  3. Shiro:授权控制

    对容易忽略的地方记录如下: 1.需要引入下面2个依赖,具体版本根据自身环境修改: <dependency> <groupId>org.apache.geronimo.bundl ...

  4. Service Mesh 及其主流开源实现解析(转)

    什么是 Service mesh Service Mesh 直译过来是 服务网格,目的是解决系统架构微服务化后的服务间通信和治理问题.服务网格由 sidecar 节点组成.在介绍 service me ...

  5. docker 要点学习

    本文主要记录学习和使用docker时遇到的一些问题和踩过的坑 1.本地docker新建redis容器,映射6379端口到本地,本机的java项目再去连,会一直连不上,原因是redis容器中映射端口时需 ...

  6. MySQL问题解决:-bash:mysql:command not found

      问题:        [root@linux115 /]# mysql -uroot -p          -bash: mysql: command not found    www.2cto ...

  7. Oracle数据库创建表空间

    //创建表空间create tablespace ACQUISITION_DATA datafile 'F:\app\kelly\oradata\acquisition\acquisition_dat ...

  8. day10--协成\异步IO\缓存

       协成(Gevent) 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程.CPU只认识线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将 ...

  9. 迷茫于Hibernate/JPA的人提一些建议。

    想对那些“迷惑”于Java ORM框架的J2EE开发人员提一些建议,希望能够对他们 更深入的理解和运用J2EE ORM框架来提速工作有所帮助,这些建议可能显得有些”陈旧“和”肤浅“, 因为最近半年我没 ...

  10. 【LeetCode】287. Find the Duplicate Number

    Difficulty:medium  More:[目录]LeetCode Java实现 Description Given an array nums containing n + 1 integer ...