Open toxic-johann opened 7 years ago
为期两周的课程已经结束了,其中月影长达10小时的课程还是价值连城的。
月影在课堂上十分注重一个概念,就是函数式编程。那么什么是函数式编程呢?为什么需要函数式编程呢?函数式编程好不好呢?这些问题,每个人都有自己的答案,就像每个人都会有属于自己的设计模式,无需要强行去争论。
于是今天开始去研究这个问题。现在写下来,总结一下自己的理解,希望大家能够指正。
JavaScript有一个十分著名的的概念,叫做回调地狱。就是形容一个函数中嵌入了太多的回调函数,令到函数十分难以理解。
于是大家提出了好多种方法去解决这个问题,并且也诞生了诸如promise这种优秀的策略。但是为什么会有这种策略呢,这种策略的优秀点何在呢?他用了什么方法呢。
我们先看一个面试题。
var User={ count:1, getCount:function(){return this.count} } console.log(User.getCount());//1 var func = User.getCount; console.log(func());//undefined
这种原因是因为,func=User.getCount只是简单的移交了函数,而func执行的时候,this指向的是window,即console.log所在的对象。
func=User.getCount
那么这种问题怎么解决呢?有好几种方式的,第一就是如上面第一个做法那样,不要把名字换了,调用整个函数。
但是如果函数之前的对象十分长呢?我们这样运行起来十分不方便,我就是想让代码短一点呢。
这种情况下,我们可以用bind
var func = User.getCount.bind(User);
又或者在一些老一点的浏览器上,没有bind,我们可以这么写。
Function.prototype.bind = Function.protyotype.bind || function(context){ var self=this return function(){ return self.apply(context,arguments); }; }
我们就可以创造一个bind了。然后我们可以看到这个函数十分特别,因为他返回了一个函数。我们先记下这个特点。
这个时候我们就会想,我每次都要自己去bind这不很麻烦,可不可以在函数体内自身就bind好,我调用的时候不用想这些问题。
这个当然也是可以的啦。我们用apply来举个例子。
var User={ count:1, getCount:function(){ return function(){ return this.count; }.apply(User,arguments); } } var func = User.getCount; console.log(func());//1
我们可以看到,getCount也是返回了一个函数。所以返回函数的优点在哪里?
首先,我们可以看到,我们可以通过这种方式指定上下文,让我们在使用的时候无需担心this的问题。
假设我们业务线上有这么个需求,我们需要增加一个相加的函数,于是我们很快就写了出来了。
function add(x,y){ return x+y; }
某一天我们增加了需求,要累加。于是我们这么写。
function addMore(){ var args = [].slice.call(arguments); var tmp = 0; for(var i = 0;i<args.length;i++){ tmp = tmp + args[i]; } return tmp; }
我们发现,我们并没有复用add。这让我们做了些多余的事情。于是我们将它复用。
function addMore2(){ var args = [].slice.call(arguments); var tmp = 0; for(var i = 0;i<args.length;i++){ tmp = add(tmp,args[i]); } return tmp; }
有一天我们发现,我们要做一个累乘,我们发现累乘和累加原则上是相同的,但是我们没有办法复用累加这种模式。所以,我们要曾加一种累*的方法。这很明显,就是要求我们为创造一个服务于函数的函数。我们发现,当业务放大到一定程度的时候,我们需要复用的是一套模式,一套方法。所以我们需要将方法抽取出来。我们可以这么写。
function add(x,y){ return x+y; } function time(x,y){ return x*y; } function concat(x,y){ return x.toString()+y.toString(); } function myReduce(func){ return function(){ var args = [].slice.call(arguments); var tmp=args[0]; for(var i=1;i<args.length;i++){ tmp = func(tmp,args[i]); } return tmp; } } var addMore = myReduce(add); var timeMore = myReduce(time); var concatMore = myReduce(concat); console.log(addMore(1,2,3,4));//10 console.log(timeMore(1,2,3,4));//24 console.log(concatMore(1,2,3,4));//1234
我们可以看到这就实现了复用,这个myReduce就是和ES5中十分有用的reduce函数十分相似。我们可以通过这种方式创造新函数,增加复用性。
就像月影在课堂上说的,相对于数据,方法永远少的多和固定的多。如果我们可以将他抽取出来并且进行使用,毫无疑问,我们可以省下十分多的体力。
这一段主要来自于对阮一峰老师的《函数式编程初探》的研究。
里面举了这么一个例子。
我们平常编写数学运算,我们这么写
(1+2)*3/4
在程序里,我们这么写
var a = 1 + 2; var b = a * 3; var c = b / 4;
如果我们设计了函数,我们这么写
function add(x,y){return x+y); function time(x,y){return x*y); function except(x,y){return x/y); except(time(add(1,2),3),4);//2.25
其实这种时候已经比较接近自然语言了,我们只要再增加一下链式调用,就可以实现更加人性化的语言了。
var myMath = { answer:0, add:function(){ var args = [].slice.call(arguments); if(args.length>1){ this.answer = args[0]+args[1]; } else { this.answer+=args[0] } return this; }, time:function(){ var args = [].slice.call(arguments); if(args.length>1){ this.answer = args[0]*args[1]; } else { this.answer*=args[0] } return this; }, except:function(){ var args = [].slice.call(arguments); if(args.length>1){ this.answer = args[0]/args[1]; } else { this.answer/=args[0] } return this; } } console.log(myMath.add(1,2).time(3).except(4).answer);//2.25
为了偷懒,我们再抽取一部分。
var myMath = { answer:0, myPromise:function(func){ var self = this; return function(){ return function(){ self.answer = func.apply(this,arguments); return self; }.apply(myMath,arguments); } }, myThen:function(){ var self=this; var args = [].slice.call(arguments); var func = args[0]; args.splice(0,1); return function(){ var args = [].slice.call(arguments); args.unshift(this.answer); func.apply(self,args); return self; }.apply(myMath,args); } } function add(){ var args = [].slice.call(arguments); return args[0]+args[1]; } function time(){ var args = [].slice.call(arguments); return args[0]*args[1]; } function except(){ var args = [].slice.call(arguments); return args[0]/args[1]; } ad = myMath.myPromise(add); ti = myMath.myPromise(time); ex = myMath.myPromise(except); console.log(ad(0,1).myThen(ad,2).myThen(ti,3).myThen(ex,4).answer);//2.25
于是,我们就可以拿到一个更加贴近我们需要的运算函数了。
好吧,我的理解大概就是这样子,感觉还是挺好玩的。
为期两周的课程已经结束了,其中月影长达10小时的课程还是价值连城的。
月影在课堂上十分注重一个概念,就是函数式编程。那么什么是函数式编程呢?为什么需要函数式编程呢?函数式编程好不好呢?这些问题,每个人都有自己的答案,就像每个人都会有属于自己的设计模式,无需要强行去争论。
于是今天开始去研究这个问题。现在写下来,总结一下自己的理解,希望大家能够指正。
为什么我的代码出错了
JavaScript有一个十分著名的的概念,叫做回调地狱。就是形容一个函数中嵌入了太多的回调函数,令到函数十分难以理解。
于是大家提出了好多种方法去解决这个问题,并且也诞生了诸如promise这种优秀的策略。但是为什么会有这种策略呢,这种策略的优秀点何在呢?他用了什么方法呢。
我们先看一个面试题。
这种原因是因为,
func=User.getCount
只是简单的移交了函数,而func执行的时候,this指向的是window,即console.log所在的对象。那么这种问题怎么解决呢?有好几种方式的,第一就是如上面第一个做法那样,不要把名字换了,调用整个函数。
但是如果函数之前的对象十分长呢?我们这样运行起来十分不方便,我就是想让代码短一点呢。
这种情况下,我们可以用bind
又或者在一些老一点的浏览器上,没有bind,我们可以这么写。
我们就可以创造一个bind了。然后我们可以看到这个函数十分特别,因为他返回了一个函数。我们先记下这个特点。
这个时候我们就会想,我每次都要自己去bind这不很麻烦,可不可以在函数体内自身就bind好,我调用的时候不用想这些问题。
这个当然也是可以的啦。我们用apply来举个例子。
我们可以看到,getCount也是返回了一个函数。所以返回函数的优点在哪里?
首先,我们可以看到,我们可以通过这种方式指定上下文,让我们在使用的时候无需担心this的问题。
我们的一些坏思想
假设我们业务线上有这么个需求,我们需要增加一个相加的函数,于是我们很快就写了出来了。
某一天我们增加了需求,要累加。于是我们这么写。
我们发现,我们并没有复用add。这让我们做了些多余的事情。于是我们将它复用。
有一天我们发现,我们要做一个累乘,我们发现累乘和累加原则上是相同的,但是我们没有办法复用累加这种模式。所以,我们要曾加一种累*的方法。这很明显,就是要求我们为创造一个服务于函数的函数。我们发现,当业务放大到一定程度的时候,我们需要复用的是一套模式,一套方法。所以我们需要将方法抽取出来。我们可以这么写。
我们可以看到这就实现了复用,这个myReduce就是和ES5中十分有用的reduce函数十分相似。我们可以通过这种方式创造新函数,增加复用性。
就像月影在课堂上说的,相对于数据,方法永远少的多和固定的多。如果我们可以将他抽取出来并且进行使用,毫无疑问,我们可以省下十分多的体力。
让程序语言更加可读
这一段主要来自于对阮一峰老师的《函数式编程初探》的研究。
里面举了这么一个例子。
我们平常编写数学运算,我们这么写
在程序里,我们这么写
如果我们设计了函数,我们这么写
其实这种时候已经比较接近自然语言了,我们只要再增加一下链式调用,就可以实现更加人性化的语言了。
为了偷懒,我们再抽取一部分。
于是,我们就可以拿到一个更加贴近我们需要的运算函数了。
好吧,我的理解大概就是这样子,感觉还是挺好玩的。