JavaScript 函数
1.函数声明
function关键字
function myFunction(a, b) {
return a * b;
}
通过一个表达式定义,函数可以赋给一个变量:
var x = function(a, b) {return a * b;}; //实际是一个匿名函数
document.getElementById("p1").innerHTML=x(2, 3);
Function() 构造函数
var myFunction = new Function("a", "b", "return a * b;");
var x = myfunction(3, 4);
2.函数提升
myFunction(5); //这样的函数是可以提升的,即可以先使用,再声明
function myFunction(y) {
return y * y;
}
表达式定义函数无法提升。
3.函数自调用
(function(){var x = "Hello!!";})()
这样的函数将自调用,没有函数名,调用一次。
4.函数是对象
使用typeof操作符判断,返回的是function
函数有属性和方法
function myFunction(a, b){
return arguments.length; //返回的是函数传入的参数
}
function myFunction(a, b) {
return a * b;
}
var txt = myFunction.toString(); //toString 将函数作为一个字符串返回
// var txt = myFuction;有相同作用
5.函数参数
函数显式参数在函数定义时列出
函数隐式参数在函数调用时传递给函数真正的值
对隐式参数没有作类型和个数检测
调用时,为提供隐式参数,那么默认为undefined
function myFuntion(a, b){
if(b === undefined){
b = 0;
}
/*或者这样写
b = b || 0;
*/
return a * b;
}
document.getElementById("p1").innerHTML = myFuntion(4);
6.Arguments 对象
arguments 对象包含函数调用的参数数组,这样,当隐式参数过多时,也可以通过arguments来调用
function findMax(){
var i, max = arguments[0];
if(arguments.length < 2) return max;
for (i = 1; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
document.getElementById("p1").innerHTML = findMax(3, 4, 5, 6);
- 对象作为参数传递
对象作为参数传递至函数,那么在函数内修改对象的属性,函数外定义的对象的属性会改变
var a = {"id": 1, "name": "wong"};
function func(x){
x["id"] = 2;
}
func(a)
document.getElementById("p1").innerHTML = a["id"];
- 函数对象的call()和apply()方法使用
两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。
function myFunction(a, b){
return a * b;
}
var object = myFunction.call(object, 2, 3);
document.getElementById("p1").innerHTML = object;
function myFunction(a, b){
return a * b;
}
var array = [2, 3];
var object = myFunction.apply(object, array);
document.getElementById("p1").innerHTML = object;
- js 内嵌函数
在 JavaScript 中,所有函数都能访问它们上一层的作用域,JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。
function count(){
var counter = 0;
function plus(){
counter += 1;
}
plus();
return counter;
}
document.getElementById("p1").innerHTML = count();
- 函数闭包
个人理解: 外部函数中定义一个内部函数,通过这个内部函数来调用外部函数所定义的变量,这个变量不致被垃圾回收,同时也只有这个内部函数可以调用,保证变量安全。
对函数理解,当只有一个function的变量时,相当于这只是一个变量类型,在变量后加上(),表名执行这个函数的运行。
var f = function(){
return 2 * 3;
}
var ff = f;
document.write(ff); //function(){ return 2 * 3; }
document.write("<br>");
document.write(ff()); //6
document.write("<br>");
document.write(typeof ff); //function
document.write("<br>");
document.write(typeof ff()); //number
闭包举例:
function a(){ //外部函数
var i = 0; //一个变量
function b(){ //内部函数,调用变量i
alert(++i);
}
return b; //返回的是内部函数,而不是内部函数return的值
}
var c = a(); // c 为函数b,此时a中的变量不会被销毁
c(); //三次调用,每次++i,i依次递加,显示1,2,3
c();
c();
var add = (function(){
var counter = 0;
return function(){return ++counter;}
})();
document.write(typeof add); //function
alert(add());
alert(add());
alert(add());
上面第一个代码比较好理解,第二个代码稍微有点不好理解,掌握函数基本概念也是比较好理解的。,首先,有两个匿名函数,这两个匿名函数内嵌关系,外层函数内定义一个counter变量和一个 return 的内层函数,即调用此外层函数时,返回的是一个内层函数:function(){return counter++;} 第一条语句的最后一个括号就是说外层函数自调用。这样add赋值就是内层函数,注意 add是一个函数.下面三个alert()中的add()就是调用add这个函数,由于add这个函数是将counter自加再显示,所以会有三个alert,分别是1,2,3。
上述代码修改后等价如下:
function f1(){
var counter = 0;
function f2(){
return ++counter;
}
return f2;
}
var add = f1();
alert(add());
alert(add());
alert(add());
- 高阶函数
高阶函数,就是让函数作为参数传入一个函数
一个简单的高阶函数:
function f1(a, b, f){
return f(a)+f(b);
}
document.write(f1(-2, 3, Math.abs)); //5
Array中的高阶函数,map(),reduce(),filter(),sort()
function f(a){
return a * a;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var arrNew = arr.map(f);
document.write(arrNew); //1,4,9,16,25,36,49,64,81
function f(a, b){
return a + b;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var ans = arr.reduce(f); //[x1, x2, x3, x4].reduce(f) 等价 f(f(f(x1, x2), x3), x4)
document.write(ans); //45
function f(element, index, self){ //element为当前元素,index为当前元素在原数组中的位置,self为原数组,一般只用第一个参数即可
return self.indexOf(element) === index; //返回true,保留该元素,否则过滤掉该元素
}
var arr = [1, 2, 3, 4, 5, 6, 9, 9, 9];
var ans = arr.filter(f);
document.write(ans); //1,2,3,4,5,6,9
sort()方法注意:sort()方法默认把所有元素先转换为String再排序,故’10’排在了’2’的前面,因为字符’1’比字符’2’的ASCII码小。即转化为String后按字典序排
function f(element, index, self){
return self.indexOf(element) === index;
}
var arr = [2, 20, 10, 1];
var ans = arr.sort();
document.write(ans); //1,10,2,20
如果想排序整数,可以重写一个函数传进sort()
function f(x, y){ //从小到大
if(x < y){ //前一个小于后一个,不用交换位置,返回-1
return -1;
}
else if(x > y){ //前一个大于后一个,交换位置,返回1
return 1;
}
return 0;
}
var arr = [2, 20, 10, 1];
var ans = arr.sort(f);
document.write(ans); //1,2,10,20
function f(x, y){ //从大到小
if(x < y){ //前一个小于后一个需要交换位置,返回1
return 1;
}
else if(x > y){ //前一个大于后一个不需要交换位置,返回-1
return -1;
}
return 0;
}
var arr = [2, 20, 10, 1];
var ans = arr.sort(f);
document.write(ans); //20,10,2,1
function f(x, y){
if(x < y){
return 1;
}
else if(x > y){
return -1;
}
return 0;
}
var arr = ["CCC", "aaa", "BBB"];
var ans = arr.sort();
document.write(ans); //BBB,CCC,aaa
上述代码比较了字符串,通过字典序比较的,如果想忽略字母大小写影响,加一个回调函数即可
function f(x, y){
var s1 = x.toUpperCase();
var s2 = y.toUpperCase();
if(s1 < s2){
return -1;
}
if(s1 > s2){
return 1
}
return 0;
}
var arr = ["CCC", "aaa", "BBB"];
var ans = arr.sort(f);
document.write(ans); //aaa,BBB,CCC
单例模式,函数柯里化,泛化this,uncurrying
- 箭头函数
简写形式的函数表达式,并且它拥有词法作用域的this值(即不会新产生自己作用域下的this,arguments,super和new.target等对象),箭头函数总是匿名
语法:
N个参数:
(param1, param2, ..., paramN) => {statements}
单个参数:
param => {statements}
可变参数:
(param1, param2, ...rest) => {statements}
无参数:
() => {statements}
直接返回:
param => param * param;
相当于:
function(param){
return param * param;
}
返回是对象,且是单表达式,注意加()
x => ({foo: x});
同时支持设置默认值:
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();
箭头函数内部的this词法作用域是由上下文决定的
var obj = {
birth: 1990,
getAge: function(){
var b = this.birth; //1990
var fn = function(){
return new Date().getFullYear() - this.birth; //this 指向windows或undefined
}
return fn();
}
}
document.write(obj.getAge());
var obj = {
birth: 1990,
getAge: function(year){
var b = this.birth; //1990
var fn = y => y - this.birth; //仍是1990
return fn.call({birth: 2000},2018); //第一个参数被忽略,this已绑定
}
}
document.write(obj.getAge()); //28
即call apply方法的第一个参数将失效
不绑定arguments
function foo(){
var f = () => arguments[0]; //不指向箭头函数的arguments,指向所在域的arguments
return f(2);
}
document.write(foo(1)); //1
箭头函数没有自己的 arguments 对象,不过在大多数情形下,rest参数可以给出一个解决方案:
function foo(){
var f = (...args) => args[0]; //不指向箭头函数的arguments,指向所在域的arguments
return f(2);
}
document.write(foo(1)); //2