以下笔记为学习《JavaScript 高级程序设计》》(第 3 版) 、网易云课堂上的【撩课-零基础玩转JavaScript】所做。
函数是Function类型的实例,具有属性和方法。函数是对象,函数名是一个指向函数对象的指针。
目录:1、定义函数的几个方法
2、立即执行函数
4、没有重载
6、作为值的函数
7、函数内部属性
8、函数属性和方法
10、函数的类型
11、回调函数
定义函数的方式有 2 种:一种是函数声明,另一种是函数表达式。
1 <script> 2 //1、函数声明 3 function functionName(arg0, arg1, arg2){ 4 //函数体 5 }; 6 //Firefox、Safari、Chrome 和 Opera 给函数定义一个非标准的 name 属性,通过这个属性可以访问到函数指定的名字 7 console.log( functionName.name ); //输出:functionName 8 9 //2、函数表达式 10 //创建一个函数并将它赋值给 functionName2 ,这种形式创建的函数叫做匿名函数(或者叫拉姆达函数),因为 function 关键字后面没有标识符。 11 var functionName2 = function(arg0, arg1, arg2){ 12 //函数体 13 }; 14 15 //3、使用function构造函数 16 //不推荐使用,最后一个参数被看作是函数体 17 var sum = new Function("num1", "num2", "return num1 + num2"); 18 console.log(sum(3, 4)); //输出:7 19 </script>
匿名函数属于函数表达式。匿名函数的形式是 function( 形参列表 ){ 函数体 } 。立即执行函数的形式是 ( function( 形参列表 ){ 函数体 }( 实参列表 ) ); 或者 ( function( 形参列表 ){ 函数体 } )( 实参列表 ); 。对括号的理解可以理解为 JS 里括号() 有求值的作用,括号里是函数就是起到执行函数的作用,记住就行。
1 <script> 2 //几种正确的使用方式: 3 //( function(){...}() ); 4 ( function(num1, num2){ 5 console.log( num1 + num2 ); 6 }(1, 2) ); //输出:3 7 8 //( function(){...} )(); 9 ( function(num3, num4){ 10 console.log( num3 + num4 ) 11 })(1, 2); //输出:3 12 13 //var functionName = ( function(){...}() ); 14 var fn1 = ( function(num5, num6){ 15 console.log( num5 + num6 ); 16 }(1, 2) ); //输出:3 17 18 //var functionName = ( function(){...} )(); 19 var fn2 = ( function(num7, num8){ 20 console.log( num7 + num8 ); 21 }(1, 2) ); //输出:3 22 23 //缺少括号是错误的使用方式,比如:function(){...}(); 24 //缺少括号但是匿名函数前有+/-/!也可以实现立即执行函数 25 //+ 26 + function(num1, num2){ 27 console.log( num1 + num2 ); 28 }(1, 2); //输出:3 29 //- 30 - function(num3, num4){ 31 console.log( num3 + num4 ); 32 }(1, 2); //输出:3 33 //! 34 ! function(num7, num8){ 35 console.log( num7 + num8 ); 36 }(1, 2); //输出:3 37 </script>
匿名函数的引用场景:绑定事件、做轮播图或动画时的定时器、闭包(立即执行函数等等)等等。
//绑定事件
document.onclick = function(){ console.log('点击了文档'); }
//定时器
setInterval(function(){
console.log('我被执行了');
}, 1000);
1 <script> 2 function sum(num1, num2){ 3 return num1 + num2; 4 } 5 console.log(sum(2, 2)); //输出: 4 6 7 var another = sum; //此时 another 跟 sum 都指向了同一个函数 8 console.log(another(2, 2)); //输出:4 9 10 sum = function(){ 11 return 0; 12 }; 13 console.log(sum(2, 2)); //输出:0 14 console.log(another(2, 2)); //输出:4 15 </script>
根据理解画的内存变化

如果声明了两个函数同名,则后声明的函数将覆盖前声明的函数。
1 <script> 2 //使用函数声明语法定义函数时 3 function num(num1){ 4 return num1 + 10; 5 } 6 function num(num1){ 7 return num1 + 20; 8 } 9 console.log(num(1)); //输出:21 10 11 //使用函数表达式定义函数时 12 var num = function(num1){ 13 return num1 + 30; 14 } 15 var num = function(num1){ 16 return num1 + 40; 17 } 18 console.log(num(1)); //输出:41 19 </script>
在代码执行之前,解析器有一个函数声明提升的过程。函数提升的过程即读取并且将函数声明添加到执行环境中的过程。
使用函数声明语法定义函数,可以先调用函数再定义函数。
1 console.log(num(1)); //输出:11 2 function num(num1){ 3 return num1 + 10; 4 }
使用函数表达式定义函数,不可以先调用函数再定义函数。
1 console.log(num(1)); 2 var num = function(num1){ 3 return num1 + 10; 4 } 5 //报错:Uncaught TypeError: num is not a function
函数也可以作为值。去掉函数名后面的圆括号,则只访问函数的指针不执行函数。
1 <script> 2 //可以像传递参数一样将函数传递给另一个函数 3 //这里像传递参数一样将函数someFunction传递给另一个函数 functionOne 4 function functionOne(someFunction, someArgument){ 5 //可以将一个函数作为另一个函数的结果返回 6 //这里将函数 someFunction 作为另一个函数 functionOne 的结果返回 7 //这里 functionOne 函数接受2个参数,第一个参数是一个函数即 someFunction, 第二个参数 someArgument 是传递给函数 someFunction 的一个值 8 return someFunction(someArgument); 9 } 10 function functionTwo(num){ 11 return num + 10; 12 } 13 var result = functionOne(functionTwo, 100); 14 console.log(result); //输出:110 15 </script>
可以从一个函数中返回另一个函数。createComparisonFunction() 返回一个匿名函数。可以根据某个对象属性对数组进行排序:
1 <script> 2 //从一个函数中返回另一个函数 3 //定义函数createComparisonFn接收属性名propertyName 4 function createComparisonFn(propertyName){ 5 //比较函数接收2个参数 6 return function(a, b){ 7 //根据属性名propertyName创建一个比较函数 8 var value1 = a[propertyName]; 9 var value2 = b[propertyName]; 10 if(value1 < value2){ 11 //如果value1 应该位于 value2 之前则返回一个负数 12 return -1; 13 }else if(value1 > value2){ 14 //如果value1 应该位于 value2 之后则返回一个正数 15 return 1; 16 }else{ 17 //如果value1 = value2 则返回0 18 return 0; 19 } 20 }; 21 } 22 var data = [{name: "xiaoxu", age:20 }, {name: "zhangsan", age: 10}]; 23 //将比较函数createComparisonFn传递给数组sort()方法 24 //指明按照对象的 name 属性来比较 25 data.sort(createComparisonFn("name")); 26 console.log(data[0].name); //输出:xiaoxu 27 //指明按照对象的 age 属性来比较 28 data.sort(createComparisonFn("age")); 29 console.log(data[0].age); //输出:10 30 </script>
callee
arguments 和 this 是函数内部的两个特殊对象。arguments 是一个包含传入函数中的所有参数的一个类数组对象。arguments 对象有一个 callee 属性。callee 属性是一个指向拥有 arguments 对象的函数的指针。在严格模式下访问 arguments.callee 会报错。
1 <script> 2 function factorial(num){ 3 if( num<=1 ){ 4 return 1; 5 }else{ 6 console.log(arguments.callee); 7 } 8 } 9 factorial(); 10 </script>
输出:

可以看到执行语句“ console.log(arguments.callee); ”输出的是拥有arguments对象的函数factorial, 说明 callee 属性是一个指向拥有 arguments 对象的函数的指针。
定义一个阶乘函数:
1 <script> 2 function factorial(num){ 3 if( num<=1 ){ 4 return 1; 5 }else{ 6 return num * factorial(num-1); 7 } 8 } 9 </script>
上面这样函数的执行与函数名 factorial 紧密耦合。使用 arguments.callee 可以消除这种紧密耦合的情况。
1 <script> 2 function factorial(num){ 3 if( num<=1 ){ 4 return 1; 5 }else{ 6 //callee 属性是一个指向拥有 arguments 对象的函数的指针 7 return num * arguments.callee(num-1) 8 } 9 } 10 //使用arguments.callee消除耦合之后,无论用什么函数名调用该函数都不影响执行。 11 var anotherName = factorial; 12 factorial = function(num){ 13 return 0; 14 } 15 console.log(factorial(4)); //输出:0 16 console.log(anotherName(4)); //输出:24 17 </script>
this
this引用的是函数据以执行的环境对象。当在全局作用域中调用函数时 this 对象引用的是 window。
1 <script> 2 window.num = 1; 3 var i = { 4 num : 2 5 } 6 function sayNum(){ 7 console.log(this.num); 8 } 9 //在全局作用域中调用 sayNum 中时,this 引用的是全局对象 window,此时 this.color 等同于 window.color 10 sayNum(); //输出:1 11 //把函数赋给对象 i 并调用 i.sayNum() 12 i.sayNum = sayNum; 13 //this引用的是对象 i,此时 this.color 等同于 i.color 14 i.sayNum(); //输出:2 15 16 //函数名仅仅时一个包含指针的变量,即使函数在不同的环境中执行,window.sayNum()函数跟i.sayNum()指向的仍是同一函数。 17 //但是对window.sayNum()或i.sayNum()进行更改,均不影响另一环境中的sayNum()函数。 18 //如下,对全局的sayNum()进行了更改,不影响i.sayNum() 19 sayNum = function(){ 20 console.log("3"); 21 }; 22 sayNum(); //输出:3 23 i.sayNum(); //输出: 2 24 25 </script>
caller
caller 是一个函数属性,保存着调用当前函数的函数的引用。
1 <script> 2 function outer(){ 3 //调用 inner 函数 4 inner(); 5 } 6 function inner(){ 7 console.log(inner.caller); 8 } 9 outer(); //输出: ƒ outer(){ 10 // //调用 inner 函数 11 // inner(); 12 // } 13 14 //如果在全局作用域中调用,返回 null 15 inner(); //输出:null 16 </script>
如果想要解除紧密耦合,可以用 arguments.callee 代替函数名。
1 <script> 2 function outer(){ 3 //调用 inner 函数 4 inner(); 5 } 6 function inner(){ 7 console.log(arguments.callee.caller); 8 } 9 outer(); //输出: ƒ outer(){ 10 // //调用 inner 函数 11 // inner(); 12 // } 13 14 //如果在全局作用域中调用,返回 null 15 inner(); //输出:null 16 </script>
严格模式下不支持 arguments.caller ,在非严格模式下定义caller属性是 undefined。不支持向函数的caller属性赋值。
每个函数都有 length 跟 prototype 这两个属性。每个函数都有两个非继承而来的方法:apply() 和 call()。apply() 跟 call() 作用相同,区别在于接收参数的方式。bind() 方法会创建一个函数的实例,第一个参数 this 值会被绑定传给 bind() 函数的值。
length
length 表示函数希望接收命名参数的个数。
1 <script> 2 function a(){} 3 function b( num ){} 4 function c( num1, num2){} 5 console.log( a.length ); //输出:0 6 console.log( b.length ); //输出:1 7 console.log( c.length ); //输出:2 8 </script>
prototype
prototype 保存引用类型的所有方法的真正所在。例如 toString() 跟 valueOf() 方法实际上都是保存在 prototype 之下。prototype 方法是不可枚举的。
apply()
apply() 接收2个参数。第一个参数是在其中运行的作用域,第二个参数可以是参数数组、Array的实例或者arguments对象。
1 <script> 2 function sum(num1, num2){ 3 return num1 + num2; 4 } 5 function applySum(num1, num2){ 6 //在执行sum()函数时传入this作为this值,因为是在全局作用域中调用的,所以传入的this就是window对象 7 return sum.apply(this, arguments); 8 } 9 function applySum2(num1, num2){ 10 return sum.apply(this, [num1, num2]) 11 } 12 console.log(applySum(1, 2)); //输出:3 13 console.log(applySum2(1, 2)); //输出:3 14 </script>
call()
call() 的第一个参数是在其中运行的作用域,其余传给函数的参数必须逐个列举出来。
1 <script> 2 function sum(num1, num2){ 3 return num1 + num2; 4 } 5 function applySum(num1, num2){ 6 return sum.call(this, num1, num2) 7 } 8 console.log(applySum(1, 2)); //输出:3 9 </script>
apply() 跟 call() 的强大之处是他们可以扩充函数赖以生存的作用域。
1 <script> 2 window.num = 1; 3 var i = { 4 num : 2 5 }; 6 function sayNum(){ 7 console.log( this.num ); 8 } 9 //call()的第一个参数为运行函数的作用域 10 //在全局作用域中调用,this值等同于window 11 sayNum.call(this); //输出:1 12 sayNum.call(window); //输出:1 13 //在全局作用域中调用i,那么运行函数的作用域为对象 i。即先将sayNum()函数放到对象 i 中,然后再通过对象 i 来调用它 14 sayNum.call(i); //输出:2 15 </script>
bind()
1 <script> 2 window.num = 1; 3 var i = { 4 num : 2 5 }; 6 function sayNum(){ 7 console.log( this.num ); 8 } 9 //创建一个函数实例 a 。sayNum() 调用 bind() 并传入对象 i, 创建 a() 函数。a函数的 this值等于 i。 10 var a = sayNum.bind( i ); 11 //即使在全局作用域中调用 a(), 它的作用域仍为 i。 12 a(); //输出:2 13 </script>
当一个函数被调用,通常会从函数的开始执行到结束。如果想提前结束该函数的执行可以使用 return 语句,return 语句后面所有的语句将永远不会执行。
一般 return 用于返回结果。
例:
说明:调用 sum 函数,函数返回执行结果 num1+num2,变量 rs 接受返回结果,打印 rs。
function sum(num1, num2){
return num1+num2;
}
var rs = sum(1, 2);
console.log(rs);
结果:
3
如果函数没显式使用 return 语句,那么函数有默认的返回值:undefined。
例:
说明:函数 log 没有显示使用 return 语句,调用 log 函数会打印 123,变量 rs 接受 log 函数返回值,因为函数没显示使用 return 语句,所以函数返回 undefined。
function log(){
console.log('123');
}
var rs = log();
console.log(rs);
结果:
123 undefined
如果函数使用 return 语句,那么跟在 return 后面的值,就成了函数的返回值。
例:
function log(){
return '我是返回值';
}
var rs = log();
console.log(rs);
结果:
我是返回值
如果函数使用 return 语句,但是 return 后面没有任何值,那么函数的返回值也是 undefined。
例:
function log(){
return;
}
var rs = log();
console.log(rs);
结果 :
undefined
推荐的做法是要么让函数始终都返回一个值,要么不要有返回值。
在 return 后的代码是永远不会被执行的。
例:
function log(){
return;
console.log('123');
}
var rs = log();
console.log(rs);
结果:
undefined
可以利用 return 的特性(return 语句后面所有的语句将永远不会执行)来做判断,不满足某条件时,结束执行函数并返回 undefined。
例:
说明:如果调用函数时,传入参数 num1 或 num2 不是数值,则结束执行函数。这里传入参数 '1' 不是数值,所以直接 return 结束执行函数,并返回 undefined。
1 function sum(num1, num2){
2 if(typeof num1!=='number' || typeof num2!=='number'){
3 return;
4 }
5 console.log('123');
6 }
7 var rs = sum('1', 2);
8 console.log(rs);
结果:
undefined
函数是一种复杂数据类型。
例:
说明:sum 的类型是函数 function
function sum(){}
console.log( typeof sum);
结果:
function
说明:sum() 的类型是函数 sum 的返回值的类型
function sum(){
return 123;
}
console.log( typeof sum());
结果:
number
回调函数就是一个通过函数调用的函数。
可以像传递参数一样将函数传递给另一个函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
例:
说明:做一个有加减乘除功能的计算器。因为加减程度是单独的功能,所以可将分别将加减乘除封装成函数(add、sub、mul、divide),可将 add/sub/mul/divide函数作为参数传递给 cal 函数,在 cal 函数里调用 add/sub/mul/divide 函数。注意,cal 的第三个参数应该是 add/sub/mul/divide,而不是add()/sub()/mul()/divide(),add 是函数的指针,add() 是函数执行返回的结果。
这里的 add、sub、mul、divide 函数就是回调函数。
//代码 ===========================================================
//单独的功能
//加
function add(num1, num2){
return num1+num2;
}
//减
function sub(num1, num2){
return num1-num2;
}
//乘
function mul(num1, num2){
return num1*num2;
}
//除
function divide(num1, num2){
return num1/num2;
}
//汇总功能
//计算器
function cal(num1, num2, func){
return func(num1, num2);
}
//结果 ===========================================================
//加
console.log(cal(100, 200, add)); //输出:300
//减
console.log(cal(100, 200, sub)); //输出:-100
//乘
console.log(cal(100, 200, mul)); //输出:20000
//除
console.log(cal(100, 200, divide));//输出:0.5
浙公网安备 33010602011771号