1. Define
var square = function(x) {
return x * x;
};
console.log(square(12));
参数:函数可以不带参数,也可以带多个参数:
var makeNoise = function() {
console.log('Pling!');
}
var power = function(base, exponent) {
var result = 1;
for(var count=0; count<exponent; count++) {
result *= base;
}
return result;
}
返回值:函数可以返回一个值,也可以没有返回值。当没有return语句,或者return 后面没有值时 (return ; ) ,实际的返回值是 undefined .
1. Variable Scopes
local:
函数的参数,函数内部声明的变量。
只有在本函数范围内才可以访问。
避免函数之间的变量互相影响。
每个函数都是一个小宇宙,互不影响。
global:
在任何函数体外面定义的变量。在任何函数中都可以访问。
尽量避免使用global变量。
1. Nested Scope
在函数内部定义函数,看个例子先:
var landscape = function() {
var result = "";
var flat = function(size) {
for (var count = 0; count < size; count++)
result += "_";
};
var mountain = function(size) {
result += "/";
for (var count = 0; count < size; count++)
result += "'";
result += "\\";
};
flat(3);
mountain(4);
flat(6);
mountain(1);
flat(1);
return result;
};
console.log(landscape());
// → ___/''''\______/'\_
flat( ) 和 mountain( ) 可以使用 landscape( ) 内定义的变量,如result,但各自内部的变量互不影响,如count。
lexical scoping: 内层作用域可以访问它所有的外层作用域的内容。
function 是创建scope的唯一方法,只靠花括号不能创建scope,这与其他语言不同。例如:
var something = 1;
{
var something = 2;
}
console.log(something);
// -> 2
上面的代码中,第二个 var something 并没有定义一个新的变量,它和第一个var something 是同一个变量,所以,输出结果是 2 。
1. Functions as Values
函数和其他类型的变量没有本质区别,只不过它的值是一段代码。函数可以用在任何表达式中,而不仅仅是调用它。也可以把它作为其他函数的参数。
1. Declaration Notation
function square(x) {
return x * x;
}
这种声明方式与变量赋值方式有一点区别,那就是,函数的声明可以写在函数调用的后面。
console.log("The future says:", future());
function future() {
return "We STILL have no flying cars.";
}
如果用另一种写法,代码执行会出错,如下:
1. The Call Stack
先看一个函数调用的例子:
function greet(who) {
console.log("Hello " + who);
}
greet("Harry");
console.log("Bye");
因为函数本身是一个代码块,那么,调用一个函数时,代码的执行会发生跳转,跳到这个函数内部的代码中。当函数调用执行完之后,再跳回原来的位置,顺序往下执行。这个跳回来的位置和程序当时的状态(叫做 context),需要妥善保存,否则,执行完函数就不知道往哪里跳了。在计算机中,保存context的位置叫做 call stack。
上面这段代码的call stack大概是下面这个样子:
top
greet
console.log
greet
top
console.log
top
call stack 需要内存空间,当它太大时,计算机会报错,程序也会停止执行。出错信息可能是: out of stack space 或者 too much recursion 。
看一个函数无限递归调用的例子:
function chicken() {
return egg();
}
function egg() {
return chicken();
}
console.log(chicken() + " came first.");
在Chrome中执行:
1. Growing Functions
函数产生的两个原因:
1. 相似的代码写了多次,可以放到一个函数中,避免重复。也可以减少出错的可能性。
2. 有一个独立的、明确的功能,可以封装到函数中。
函数的命名和优化
用这种格式打印牛和鸡的个数:
007 Cows
011 Chickens
function printFarmInventory(cows, chickens) {
var cowString = String(cows);
while (cowString.length < 3) {
cowString = "0" + cowString;
}
console.log(cowString + " Cows");
var chickenString = String(chickens);
while (chickenString.length < 3) {
chickenString = "0" + chickenString;
}
console.log(chickenString + " Chickens");
}
printFarmInventory(7, 11);
需求变化了,农场主想把猪的数量也按这种格式打印出来。
我们看到,打印牛和鸡的两段代码非常相似,那么打印猪的代码一定也是一样的,此时,可以定义一个函数,避免重复。
function printZeroPaddedWithLabel(number, label) {
var numberString = String(number);
while (numberString.length < 3)
numberString = "0" + numberString;
console.log(numberString + " " + label);
}
function printFarmInventory(cows, chickens, pigs) {
printZeroPaddedWithLabel(cows, "Cows");
printZeroPaddedWithLabel(chickens, "Chickens");
printZeroPaddedWithLabel(pigs, "Pigs");
}
printFarmInventory(7, 11, 3);
上面的函数名称很长,这也说明,该函数所包含的功能太多了(单一职责原则)。
function zeroPad(number, width) {
var string = String(number);
while (string.length < width)
string = "0" + string;
return string;
}
function printFarmInventory(cows, chickens, pigs) {
console.log(zeroPad(cows, 3) + " Cows");
console.log(zeroPad(chickens, 3) + " Chickens");
console.log(zeroPad(pigs, 3) + " Pigs");
}
printFarmInventory(7, 16, 3);
1. Functions and Side Effects
一个函数,要么返回一个值,要么对环境产生影响,或者,二者兼有。如果,都二者没有,它也没有存在的必要。
pure function:
1. 一个纯函数,只返回数据,而不会对环境产生影响,也不会受到环境的影响,它的执行结果只与传入的参数有关。2. 纯函数的 “封装性” 好,执行结果可预期,可测试。想想看,一个 “非纯函数” 如何进行自动化测试?
非纯函数也是必需的,如: console.log( ) 。 而且,有的时候,非纯函数可能更简洁,执行效率更高。 如何取舍要视情况而定。
1. Exercise: Minimum
function min(a, b) {
return a < b ? a : b;
}
1. Exercise: Recursion
function isEven(n) {
if (n < 0) {
return isEven(-n);
}
else if (n == 0) {
return true;
}
else if (n == 1) {
return false;
}
else {
return isEven(n-2);
}
}
1. Exercise: Bean Counting
function countBs(s) {
var ret = 0;
for (var i=0; i<s.length; i++) {
if(s.charAt(i) == 'B') {
ret++;
}
}
return ret;
}
function countChar(s, char) {
var ret = 0;
for (var i=0; i<s.length; i++) {
if(s.charAt(i) == char) {
ret++;
}
}
return ret;
}
function countBs(s) {
return countChar(s, 'B');
}