1:函数的概述
函数就是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
2:函数的声明
1:具有函数
function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。
2:匿名函数(函数表达式)
这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式
3:函数的重复声明
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明
4:函数的属性
name:返回函数的名字
length:形参的个数
5:参数的省略
函数参数不是必须的,Javascript允许参数省略,但是,没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined。
被省略的参数的值就变为undefined。需要注意的是,函数的length属性与实际传入的参数个数无关,只反映函数预期传入的参数个数。
6:传值方式(基本类型,复合类型)
函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。这意味着,在函数体内修改参数值,不会影响到函数外部。
如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。
7:同名参数
如果有同名的参数,则取最后出现的那个值。
调用函数f的时候,没有提供第二个参数,a的取值就变成了undefined。这时,如果要获得第一个a的值,可以使用arguments对象。
8:arguments对象
由于JavaScript允许函数有不定数目的参数,所以我们需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。
arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
9:函数返回值
函数的返回值用return返回,如果没有return,那么函数返回值undefined
function sum(x,y)
{
return x+y; //函数返回值:return -- 输出(做完事后能得到什么)
//注:无return时,返回undefined , return 后面语句不执行
}
alert(sum(3,5)); //传参调用
10:函数的返回值可以是函数
function fn(a){
alert('fn run');
return function(b){
alert('run ..');
returna+b;
}
}
alert(fn(10)(20));
11:return后如果换行系统会默认加上分号
下面程序运行后,返回值是什么?
functionfoo(){
return
{
bar: "hello"
};
}
console.log(foo());
12:需求练习
写一个按照下面方式调用都能正常工作的 sum 方法
console.log(sum(2,3)); // Outputs 5
console.log(sum(2)(3)); // Outputs 5
function sum2(x, y) {
if (arguments.length === 2) {
return x + y;
}
return function (a) {
return x + a;
}
}
console.log(sum2(4, 5));
console.log(sum2(4)(5));
function sum3() {
var first = arguments[0];
if (arguments.length === 2) {
return (arguments[0] +arguments[1]);
} else {
return function (sec) {
return first + sec;
}
}
}
13:函数练习
1:用户录入两个数据,返回两个数中的较大值
function getMax(x, y) {
/*if(x>y) {
return x;
}else {
return y;
}*/
return x > y ? x : y;
}
var num1 = prompt("第一个数");
var num2 = prompt("第二个数");
console.log(getMax(num1, num2));
2:用户录入两个数据,比较两个数是否相等
function isEqual(x, y) {
return x === y ? true : false;
}
console.log(isEqual(num1, num2));
3:实现字符串的indexOf函数
function myIndexOf(str, c) {
for (var i = 0; i < str.length;i++) {
if (c == str[i]) {
return i;
}
}
return -1;
}
var str = "javaSrcipt";
var index = myIndexOf(str, "h");
if (index != -1) {
console.log("您要找的字符的下标是" + index);
} else {
console.log("没有找到该字符");
}
4:实现一个函数,判断字符串是否是回文
function isHuiwen(str) {
str = str.toLowerCase();
var arr = str.split("");
return (str ===arr.reverse().join(""));
}
var b = isHuiwen("level");
if (b) {
console.log("是回文")
} else {
console.log("不是回文");
}
5:实现数组的join函数
function myJoin(arr, c) {
var empty = "";
for (var i = 0; i < arr.length;i++) {
empty += arr[i];
if (i == arr.length - 1) {
break;
}
empty += c;
}
return empty;
}
var arr = [4, 5, 6];
console.log(myJoin(arr, "*"));
6:实现数组的map函数
function myJoin(arr, c) {
var empty = "";
for (var i = 0; i < arr.length;i++) {
empty += arr[i];
if (i == arr.length - 1) {
break;
}
empty += c;
}
return empty;
}
var arr = [4, 5, 6];
console.log(myJoin(arr, "*"));
7:数组去重
function isHave(arr, x) {
for (var i = 0; i < arr.length; i++) {
if (x == arr[i]) {
return true;
}
}
return false;
}
function delRepeat(arr) {
var n = [];
for (var i = 0; i < arr.length;i++) {
if((isHave(n,arr[i]))==false) {
n[n.length] = arr[i];
}
}
return n;
}
var arr1 = [3, 44, 5, 5, 6, 6, 7];
console.log(delRepeat(arr1));
14:函数的递归
函数可以调用自身,这就是递归(recursion)。下面就是通过递归,计算斐波那契数列的代码。
求x的阶乘
function getX(x) {
if(x<=1) return 1;
return x*getX(x-1);
}
console.log(getX(4));
15:函数作用域
1:作用域(scope)指的是变量存在的范围。Javascript只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。
2:在函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。
在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。
3:函数内部定义的变量,会在该作用域内覆盖同名全局变量。
注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块(if,for)中声明,一律都是全局变量。
4:如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。
// 此处可调用 b变量
function myFunction() {
b ="haha";
// 此处可调用 b 变量
}
5:函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
小练习:
var v='Hello World';
(function(){
alert(v);
var v='javascript';
})()
//等价于
var v='Hello World';
(function(){
var v;
alert(v);
v='haha';
})()
16:作用域链
函数嵌套--> 形成链条 --> 变量回溯(当前没有,沿着链条追查上家)
var age = 30;
function fn(){
var age = 50;
function fn1(){
varage = 40;
functionfn2(){
//var age = 50;
console.log(age); //变量的回溯机制(当前没有,追查上家)
//var age = 90; //声明定义的提升。
}
fn2();
}
fn1();
}
fn();
var num=5;
functionfunc1(){
varnum=3;
varage =4;
function func2(){
console.log(num);
var num ='ivan';
function func3(){
age =6;
}
func3();
console.log(num);
console.log(age);
}
func2();
}
func1();
易错点:
函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
17:闭包
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
要理解闭包,首先必须理解变量作用域。前面提到,JavaScript有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。但是,在函数外部无法读取函数内部声明的变量。
如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。
上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是JavaScript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了。
闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果
为什么会这样呢?原因就在于inc始终在内存中,而inc的存在依赖于createIncrementor,因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
start是函数createIncrementor的内部变量。通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包inc使得函数createIncrementor的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。
闭包的作用: 闭包:通过引用访问函数内的函数,实现信息的驻留。
驻留:信息保持(引用在,空间则不销毁)