Eloquent JavaScript 笔记 三: Functions

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');
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值