javascript函数用法速记
参考:https://www.liaoxuefeng.com/wiki/1022910821149312/1023021087191360
在js里,函数是一等公民。
- 函数可以分配给变量
- 函数可以作为参数传递给其他函数
- 函数可以从其他函数返回
1 定义函数
方式1
function abs(x) { if (x >= 0) { return x; } else { return -x; } }
方式2
var abs = function (x) { if (x >= 0) { return x; } else { return -x; } };
其中方式2定义了一个匿名函数,然后赋值给abs
上述两种定义完全等价
2 调用函数
JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数
abs(10, 'blablabla'); // 返回10 abs(-9, 'haha', 'hehe', null); // 返回9
传入的参数比定义的少也没有问题
abs(); // 返回NaN
此时abs(x)
函数的参数x
将收到undefined
,计算结果为NaN。
3 arguments
JavaScript还有一个关键字arguments
,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments
类似Array
但它不是一个Array
function foo(x) { console.log('x = ' + x); // 10 for (var i=0; i<arguments.length; i++) { console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30 } } foo(10, 20, 30); //运行结果为 x = 10 arg 0 = 10 arg 1 = 20 arg 2 = 30
4 ...参数
ES6引入了一个新的函数参数写法...
function foo(a,b,...rest){ console.log(a) console.log(b) console.log(rest) } foo(1,2,3,4,5) //运行结果 1 2 [3, 4, 5]
5 解构赋值
ES6引入解构赋值
var [x, y, z] = ['hello', 'JavaScript', 'ES6']; let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素 //从一个对象中取出若干属性 var person = { name: '小明', age: 20, gender: 'male', passport: 'G-12345678', school: 'No.4 middle school' }; var {name, age, passport} = person; //对一个对象进行解构赋值时,同样可以直接对嵌套的对象属性进行赋值,只要保证对应的层次是一致的 var person = { name: '小明', age: 20, gender: 'male', passport: 'G-12345678', school: 'No.4 middle school', address: { city: 'Beijing', street: 'No.1 Road', zipcode: '100001' } }; var {name, address: {city, zip}} = person; //输出结果 name; // '小明' city; // 'Beijing' zip; // undefined, 因为属性名是zipcode而不是zip // 注意: address不是变量,而是为了让city和zip获得嵌套的address对象的属性: address; // Uncaught ReferenceError: address is not defined
如果要使用的变量名和属性名不一致,可以用下面的语法获取
var person = { name: '小明', age: 20, gender: 'male', passport: 'G-12345678', school: 'No.4 middle school' }; // 把passport属性赋值给变量id: let {name, passport:id} = person; name; // '小明' id; // 'G-12345678' // 注意: passport不是变量,而是为了让变量id获得passport属性: passport; // Uncaught ReferenceError: passport is not defined
6 方法
绑定到对象的函数称为方法。
var xiaoming = { name: '小明', birth: 1990, age: function () { var y = new Date().getFullYear(); return y - this.birth; } }; xiaoming.age; // function xiaoming.age() xiaoming.age(); // 今年调用是25,明年调用就变成26了
7 this关键字
上例中方法中的this指的是当前对象。
但是,全局的this指的是window对象
8 函数的apply()方法
我们看一下下面的代码
function getAge() { var y = new Date().getFullYear(); return y - this.birth; } var xiaoming = { name: '小明', birth: 1990, age: getAge }; xiaoming.age(); // 25, 正常结果 getAge(); // NaN
如果以对象的方法形式调用,比如xiaoming.age()
,该函数的this
指向被调用的对象,也就是xiaoming
,这是符合我们预期的。
如果单独调用函数,比如getAge()
,此时,该函数的this
指向全局对象,也就是window
我们可以使用函数的apply方法对上例做一下改造,使得单独调用getAge()时指向函数内部this的指向
function getAge() { var y = new Date().getFullYear(); return y - this.birth; } var xiaoming = { name: '小明', birth: 1990, age: getAge }; xiaoming.age(); // 25 getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空
9 函数的call()方法
call()方法和与apply()
类似,唯一的区别是
-
apply()
把参数打包成Array
再传入; -
call()
把参数按顺序传入。
Math.max.apply(null, [3, 5, 4]); // 5 Math.max.call(null, 3, 5, 4); // 5
10 apply()实现装饰器
这个例子我们修改window.parseInt()方法,加上我们定制化的代码。
'use strict'; var count = 0; var oldParseInt = parseInt; // 保存原函数 window.parseInt = function () { count += 1; return oldParseInt.apply(null, arguments); // 调用原函数 };
后续执行parseInt('3')将会首先计算count值,然后再调用真实的parseInt()。起到装饰器的作用
11 高阶函数
如果一个函数,其参数也是一个函数,则我们称之为高阶函数
11.1 map
map()方法定义在Array中
用法举例
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; var results = arr.map(x=>x*x); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
上面,我们可以一眼就可以看出代码的用意,即把f(x)=x2作用在Array的每一个元素并把结果生成一个新的Array
再举例,我么把一个整型数组转换为对应的字符串数组
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']
11.2 reduce
reduce()方法也是Array中定义
在一个数组上应用reduce函数,效果如下
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
举例
var arr = [1, 3, 5, 7, 9]; arr.reduce((x, y)=>x+y); // 25
11.3 filter
filter()方法也是定义在Array上
举例
var arr = [1, 2, 4, 5, 6, 9, 10, 15]; arr.filter(x=> x % 2 !== 0);
11.4 Array上其他的高阶函数
除了上面提到的高阶函数,还有一些常用的高阶函数:sort、every、find、findIndex、forEach
12 闭包
定义枯燥无味且难于理解,我们举例说明
我们定义一个函数lazy_sum(),用于计算一个数组的和,我们不返回求和结果,而是返回一个函数
function lazy_sum(arr) { var sum = function () { return arr.reduce((x, y) => x + y); } return sum; } var f=lazy_sum([1,2,3,4,5]) f() //执行结果是15
在这个例子中,我们在函数lazy_sum
中又定义了函数sum
,并且,内部函数sum
可以引用外部函数lazy_sum
的参数和局部变量,当lazy_sum
返回函数sum
时,相关参数和变量都保存在返回的函数中,故称之为闭包。这种称为“闭包(Closure)”的程序结构拥有极大的威力。
13 箭头函数
箭头函数可以解决this指向问题
举例,
如果我们不用箭头函数
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = function () { return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn(); } };
调用结果
obj.getAge()
NaN
我们使用箭头函数,则完全修复了this
的指向,this
总是指向词法作用域,也就是外层调用者obj
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象 return fn(); } };
调用结果
obj.getAge()
32
14 generator
ES6引入了一个新的数据类型generator
generator定义
function* foo(x) { yield x + 1; yield x + 2; return x + 3; }
generator和函数不同的是,generator由function*
定义(注意多出的*
号),并且,除了return
语句,还可以用yield
返回多次
调用
var f=foo(1) f.next(); // {value: 2, done: false} f.next(); // {value: 3, done: false} f.next(); // {value: 4, done: true}
generator的一个巨大的好处,就是把异步回调代码变成“同步”代码。
有了generator,发送ajax请求时就可以很优雅的写
try { r1 = yield ajax('http://url-1', data1); r2 = yield ajax('http://url-2', data2); r3 = yield ajax('http://url-3', data3); success(r3); } catch (err) { handle(err); }