定义函数
函数的定义(也称为函数的声明)由一系列的函数关键词组成
- 函数的名称
- 函数引数列表,包围在括号( )中并由逗号
,
区隔 - 函数功能,包围在花括号{ }中,用于定义函数功能的一些JavaScript语句
例如
function square(number) {
return number * number;
}
函数square使用了一个参数,叫作number
这个函数只有一个语句,它说明该函数会将函数的参数(即number)自乘后返回
函数的return语句确定了函数的返回值
如果你传递一个对象(pass an object),作为参数,而函数改变了这个对象的属性,这样的改变对函数外部是可见的
function myFunc(theObject) {
theObject.make = "Toyota";
}
var mycar = {make: "Honda", model: "Accord", year: 1998},
var x, y;
x = mycar.make; // x 获取的值为 "Honda"
myFunc(mycar);
y = mycar.make; // y 获取的值为 "Toyota"
// (make属性的值在函数中被改变了)
函数表达式
var square = function(number) {
return number * number
};
var x = square(4); // x 得到的值为16
函数表达式也可以提供函数名,用于在函数内部使用来代指其本身
var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)};
console.log(factorial(3));
函数表达式在将函数作为一个引数传递给其它函数时十分方便
下面的例子演示了一个叫map的函数如何被定义,而后调用一个匿名函数作为其第一个参数
function map(f,a) {
var result = [], // 创建一个新的数组
i;
for (i = 0; i != a.length; i++)
result[i] = f(a[i]);
return result;
}
map(function(x) {return x * x * x}, [0, 1, 2, 5, 10]);
//return [0, 1, 8, 125, 1000]
根据条件来定义一个函数,例如下面的例子
var myFunc;
if (num == 0){
myFunc = function(theObject) {
theObject.make = "Toyota"
}
}
当一个对象的属性是函数时,其称之为方法
调用函数
调用函数会以给定的参数真正执行这些动作
例如,定义了函数square,你可以如下这样调用它
square(5);
函数一定要处于调用它们的域中,但是函数的声明可以在它们的调用语句之后,例如
console.log(square(5));
/* ... */
function square(n) { return n*n }
函数域是指函数声明时的所在的地方,或者函数在顶级被声明时指整个程序
意只有使用如上的语法形式 function funcName(){}
才可以,下面的例子是无效的
console.log(square(5)); // square is not defined
square = function (n) {
return n * n;
}
函数可以被递归;就是说函数可以调用其本身,下面的例子是计算递归的阶乘值
function factorial(n){
if ((n == 0) || (n == 1))
return 1;
else
return (n * factorial(n - 1));
}
var a, b, c, d, e;
a = factorial(1); // 1赋值给a
b = factorial(2); // 2赋值给b
c = factorial(3); // 6赋值给c
d = factorial(4); // 24赋值给d
e = factorial(5); // 120赋值给e
函数的作用域
一个函数可以取得在它的域中定义的任何变量和子函数
- 定义在全局域中的函数可以取得所有定义在全局域中的变量
- 定义在一个函数内部的子函数可以取得定义在其父函数内的,或其父函数取得的任何变量
// 下面的变量定义在全局作用(global scope)域中
var num1 = 20,
num2 = 3,
name = "Chamahk";
// 本函数定义在全局作用域
function multiply() {
return num1 * num2;
}
multiply(); // Returns 60
// 嵌套函数的例子
function getScore () {
var num1 = 2,
num2 = 3;
function add() {
return name + " scored " + (num1 + num2);
}
return add();
}
getScore(); // Returns "Chamahk scored 5"
作用域和函数堆栈
递归
个函数可以指向并调用自身(call itself),有三种方法:
- 通过使用函数名(the function's name)
- 使用arguments.callee(ECMAScript (ES5) forbids use of arguments.callee() in strict mode)
- 使用作用域下的一个变量名来指向函数(an in-scope variable refers to the function)
函数定义:
var foo = function bar() {
// statements go here
};
在这个函数体内,以下语句是等价的
- bar()
- arguments.callee()
- foo()
调用自身的函数我们称之为递归函数(recursive function)
在某种意义上说,递归近似于循环
两者都重复执行相同的代码,并且两者都需要一个终止条件以避免无限循环或者无限递归
var x = 0;
while (x < 10) { // "x < 10" is the loop condition
// do stuff
x++;
}
转化成一个递归函数和对其的调用
function loop(x) {
if (x >= 10) // "x >= 10" is the exit condition (equivalent to "!(x < 10)")
return;
// do stuff
loop(x + 1); // the recursive call
}
loop(0);
有些算法并不能简单的用循环来实现。例如,获取树结构中所有的节点时,递归来实现要容易得多
function walkTree(node) {
if (node == null) //
return;
// do something with node
for (var i = 0; i < node.childNodes.length; i++) {
walkTree(node.childNodes[i]);
}
}
跟循环函数相比,这里每个递归调用都产生了更多的递归
事实上,递归函数就使用了堆栈:函数堆栈
function foo(i) {
if (i < 0)
return;
console.log('begin:' + i);
foo(i - 1);
console.log('end:' + i);
}
foo(3);
// Output:
// begin:3
// begin:2
// begin:1
// begin:0
// end:0
// end:1
// end:2
// end:3
嵌套函数和闭包
在一个函数里面嵌套另外一个函数
嵌套函数是容器函数的私有成员。它自身也形成了一个闭包
一个闭包是一个可以自己拥有独立的环境与变量的的表达式(通常是函数)
嵌套函数是一个闭包,就意味着一个嵌套函数可以继承容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。
- 内部函数只可以在外部函数中访问
- 内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量
嵌套函数:
function addSquares(a,b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
a = addSquares(2,3); // returns 13
b = addSquares(3,4); // returns 25
c = addSquares(4,5); // returns 41
内部函数形成了闭包,你可以调用外部函数并且指定外部和内部函数的参数
function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
// Think of it like: give me a function that adds 3 to whatever you give it
fn_inside = outside(3);
result = fn_inside(5); // returns 8
result1 = outside(3)(5); // returns 8
保存变量
一个闭包必须保存它可见作用域中所有的参数和变量
每一次调用传入的参数都可能不同,每一次对外部函数的调用都实际上重新创建了一遍这个闭包
只有当inside的返回值没有再被引用时,内存才会被释放
多层嵌套函数
函数可以被多层嵌套
例如,函数A可以包含函数B,函数B可以再包含函数C。B和C都形成了闭包,所以B可以访问A,C可以访问B和A
闭包可以包含多个作用域,他们递归式的包含了所有包含它的函数作用域。这个称之为域链(scope chaining)
function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1); // return 6 (1 + 2 + 3)
C可以访问B的y和A的x
- B形成了一个包含A的闭包,B可以访问A的参数和变量
- C形成了一个包含B的闭包
- B包含A,所以C也包含A,C可以访问B和A的参数和变量。换言之,C用这个顺序链接了B和A的作用域
反过来却不是这样。A不能访问C,因为A看不到B中的参数和变量,C是B中的一个变量,所以C是B私有的。
命名冲突
当同一个闭包作用域下两个参数或者变量同名时,就会产生命名冲突
更近的作用域有更高的优先权,最远的优先级最低,这就是作用域链
链的第一个元素就是最里面的作用域,最后一个元素便是最外层的作用域
function outside() {
var x = 10;
function inside(x) {
return x;
}
return inside;
}
result = outside()(20); // returns 20 instead of 10
命名冲突发生在return x上,inside的参数x和外部变量x发生了冲突
作用链域是 {inside, outside, 全局对象}
inside具有最高优先权,返回了传入的20而不是外部函数的变量值10
阅读资料:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide
复习方式:实践(代码写一次、跑一次、测试一次),不懂的地方谷歌,阅读和做笔记
底线原则:宁可重写一次,也不复制粘贴
本次复习内容有:函数的一部分···
复习耗时:大概3小时···我也不知道为什么这么久···
本文由 Chakhsu Lau 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。