2.1剖析函数的定义
函数的定义很简单,形如function square(x){return x*x;} 其中函数名为square。我想说的是在js中函数都是有返回值的,即使你没用return关键字,那它默认会返回undefined。另外需要强调的一点是js的一切都是值,函数也是值。
当代码在执行的时候,会先扫描所有的函数定义,所以不用担心函数定义如果写在了执行代码后面是否会去调用函数的问题。在执行之前准备工作其实已经做好了这些。
局部变量的定义前用var关键字,否则会视为定义了一个全局的变量。在函数中定义的变量一般不能被函数外调用,函数外的变量在函数内可随便调用。
在js中还有一个重要的概念就是‘栈’。当代码执行到一个函数的时候,代码的控制权交给这个函数,函数执行完毕时再交出代码控制权。而执行时该回到那个地方呢,这个信息就存在‘栈’中,每个函数没调用时它的上下文(也就是开始执行的位置信息)都会被存放在‘栈’中。这一点也造就了js对函数的灵活使用,比如函数作为参数,函数作为返回值等情况。当然如果利用不当也会出现问题:
function chicken() { return egg(); } function egg() { return chicken(); } chicken();//由于死循环导致栈被占满了,发生栈溢出。
下面再讲一个有意思的例子,能证明js的超强灵活性,同时也说明了js一切皆为值的特性:
var a = null; function b() { return "B"; } (a || b)();//执行后得到 B 。
闭包的问题,说实话这本书讲得很大概,闭包个人觉得是很难理解的一个问题,有诸多情况等你去纠结。我再查一下别人资料后再补充吧。
闭包部分的理解(读了很多园子里写的文章,自己把部分理解写下来)
首先指出在js的函数中,不仅能定义一个变量,而且也能定义一个函数。当函数执行的时候,函数内部的变量调入内存做相应操作,
当函数结束时内存释放,函数内变量从此失效,而闭包可以使得原本暂存的变量在函数结束后也能重复调用。js的作用域告诉我们
内部能访问外部,外部不能访问内部,但是闭包却能做到函数外访问函数内部的变量,在下面的代码中具体讲解:
//闭包的一个定义是:如果一个函数访问了它的外部变量,那么它就是一个闭包。 //至少现在我还没理解上面的话,我觉得下面的1、2实例没体现闭包的特点,应该 //不算是一个闭包。 //实例1 var name = "zhangsan"; function showName(){ //此处函数内部调用了函数外部的变量name。 var text = "my name is "+name; alert(text); } showName(); //实例2 function outer(three){ var two = 2; function inner(one){ alert(three+(++two)+one); } inner(1); } outer(3); //该实例虽然也是内部函数调用了外部函数的变量,但我觉得依旧不属于闭包。多次执行outer(3),数据每次都是7,不会变 //真正的闭包实例如下,实例3 //实例3 function outer(three){ var two = 2; return function inner(one){ alert(three+(++two)+one); } } var invoke = outer(3); invoke(1); invoke(1); //内部函数调用外部函数的变量,而且内部函数以返回值的形式体现,这才是一个完整的闭包。 //多次调用invoke(1),虽然前一次函数已经结束但是里面的two变量并不消失。而是随着下次的调用而累加。
闭包的概念五花八门,我的理解是闭包是一种状态,当函数在某种特定使用的情况下(具体的使用情况上面代码中有体现)突然达到
的一种状态,在这种状态下,即使函数执行完毕,函数内部的变量也不会消失,而是持续保持在内存中。
另外方便理解,可以把此时的函数想象成C#当中的对象。函数中的变量就是对象中的private成员变量,想调用该私有成员,需要一个
相对应的get、set方法。而js中,函数内部的return function就提供了相应的功能。
函数的参数都是可选参数,比如你能轻松的运行这行代码alert("one","two","three"); 不过只能输出'one',后面的几个字符串被忽略了。函数参数可变的原则就是,写多了会忽略,写少了则视为
undefined。所以参数数量上一般是没啥限制的。
2.2技巧
函数最总要的作用是用来避免重复性的,当你的一部分代码在程序中多次重复使用的时候就该考虑将它们写成一个函数了。但是函数一定要保证功能单一性的原则,切记不要为了逞能把很复杂的功能封装到一个函数中,这样才调用的时候反而大大降低了其灵活性。例如,当数字只有个位时会习惯前面加0,5->05。这是常用的方法,把它写在一个函数里。
function zeropad(number,width){ var string = String(Math.round(number)); while(string.length<width){ string = "0" + string; } return string; }
上面的例子通过,通过一个width来判断你的number前面需要填几个0.
纯函数 :当使用函数的时候同样的参数总是返回同样的值而没有副作用。(题外话:其实书中会对一些小概念展开长篇幅的描述,这对部分人其实是鸡肋,我的这个系列的笔记不想照搬书本练打字,尽量写点有意思的事情,我一直头疼这点怎么去整啊)
递归 :关于递归,大家应该都熟悉了。递归有一个特点,某些问题用递归去解决很容易理解,但是弊端是递归效率很低,调用次数越多就越低。但有时候当循环调用次数不大时,递归就能用很简洁的代码快速解决你思路上很乱的问题。下面的例子还有意思,值得一想:传入一个数,计算是否其由相加5或者相乘3得到,如果能返回其计算路径,如果没有则return null。8 -> (1*3)+5;
function findSequence(goal){ function find(start,history){ if(start == goal){ return history; }else if(start > goal){ return null; }else{ return find(start + 5,"("+history+" + 5)")||find(start * 3,"("+history+" * 3)"); } } return find(1,"1"); } alert(findSequence(1234));
当然上面计算的并不一定时一条最短的路径,只是路径之一。