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); //
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),使用函数的更多相关文章
- lodash用法系列(6),函数种种
Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...
- lodash用法系列(5),链式
Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...
- lodash用法系列(4),使用Map/Reduce转换
Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...
- lodash用法系列(2),处理对象
Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...
- lodash用法系列(1),数组集合操作
Lodash用来操作对象和集合,比Underscore拥有更多的功能和更好的性能. 官网:https://lodash.com/引用:<script src="//cdnjs.clou ...
- 深入理解javascript函数系列第一篇——函数概述
× 目录 [1]定义 [2]返回值 [3]调用 前面的话 函数对任何一门语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即 ...
- 深入理解javascript函数系列第二篇——函数参数
× 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...
- SSE 系列内置函数中的 shuffle 函数
SSE 系列内置函数中的 shuffle 函数 邮箱: quarrying@qq.com 博客: http://www.cnblogs.com/quarryman/ 发布时间: 2017年04月18日 ...
- php从入门到放弃系列-03.php函数和面向对象
php从入门到放弃系列-03.php函数和面向对象 一.函数 php真正的威力源自它的函数,内置了1000个函数,可以参考PHP 参考手册. 自定义函数: function functionName( ...
随机推荐
- 让linux中 history显示每条命令的操作时间及操作用户【转】
一.history 中显示日期时间用户名的办法 history 命令,用来显示命令行上的操作记录 不过默认是仅显示操作命令行本身,而没有记录操作时间等细节 例如 这样,我们查找记录时很麻烦,想回顾下某 ...
- poj1996
多项式计算,有点像母函数,注意最后要判断最高次项是否为0. #include <cstdio> #include <cstring> using namespace std; ...
- !!!sql_mode=only_full_group_by配置
Expression #7 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'invoicecer ...
- asp.net 文件下载显示中文名称
protected void Page_Load(object sender, EventArgs e) { string guid = Request.QueryString[& ...
- 【前端开发】移动端适配方案js,rem单位转换,640设计稿20px=1rem
! function() { var style = document.createElement("STYLE"), docEl = document.documentEleme ...
- 深入迁出mybatis系列
本系列教程均源于南柯梦,经好库文摘转发,感谢南柯梦的辛苦整理. 深入迁出mybatis系列 深入浅出Mybatis系列(一)---Mybatis入门 深入浅出Mybatis系列(二)---配置简介(m ...
- 在Github和Git上fork之简单指南
http://www.linuxidc.com/Linux/2014-11/109785.htm 以我的经验来看,刚接触Git和GitHub时,最困扰的一件事情就是尝试解决下面的问题:在Git和Git ...
- 【AtCoder】ARC091
C - Flip,Flip, and Flip...... 只有一个这一个是反面 只有一行那么除了两边以外都是反面 否则输出\((N - 2)*(M - 2)\) #include <bits/ ...
- 【翻译】checkbox的第三种状态
checkbox只有两种值:选中(checked)或未选中(unchecked).它可以有任何值,但是表单提交时checkbox的值只能是checked或unchecked.它的默认值是uncheck ...
- [转]C++实现平衡二叉树
作者:Rest探路者 出处:http://www.cnblogs.com/Java-Starter/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意请保留此段声明,请在文章页面明显位置给出原文 ...