JavaScript的函数
[TOC]
函数的定义
普通函数定义
1234567function abs(x) {if (x >= 0) {return x;} else {return -x;}}指定变量的函数定义
1234567var abs = function (x) {if (x >= 0) {return x;} else {return -x;}}; //此种方法末尾需要添加;
Arguments关键字
该关键字只在函数内部齐作用,永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array。利用arguments,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值以及参数的数量
1234567function foo(x) {alert(x); // 10for (var i=0; i<arguments.length; i++) {alert(arguments[i]); // 10, 20, 30}}foo(10, 20, 30);
Rest参数
在ES6中定义了一种方便获取可变参数的方式。
1234567891011function foo(a, b, ...rest) {console.log('a = ' + a);console.log('b = ' + b);console.log(rest);}foo(1, 2, 3, 4, 5);// 结果:// a = 1// b = 2// Array [ 3, 4, 5 ]- rest参数只能写在最后,前面用…标识,从运行结果可知,传入的参数先绑定a、b,多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。
- 如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined)。
变量 的作用域
在JavaScript中,用
var
申明的变量实际上是有作用域的。(该作用域只包括函数,不包括快作用域)只有在作用域内部才可以使用。
1234567891011;function foo() {var temp =100;for (var i=0; i<100; i++) {//}i += 100; // 仍然可以引用变量i,因为var的局部只局限于函数内部。}temp=temp+2; // ReferenceError! 无法在函数体外引用变量x在es6之后,要想在块级作用域定义局部变量可以使用let关键字。
1234567function foo() {var temp =100;for (let i=0; i<100; i++) {//}i += 100; // SyntaxError}并且在es6之后,可以通过const关键字定义常量
123const PI = 3.14;PI = 3; // 某些浏览器不报错,但是无效果!PI; // 3.14
装饰器
通过js的装饰起可以动态改变函数的行为,增加我们需要的功能。
12345678910111213var count = 0;var oldParseInt = parseInt; // 保存原函数window.parseInt = function () {count += 1;return oldParseInt.apply(null, arguments); // 调用原函数};// 测试:parseInt('10');parseInt('20');parseInt('30');count; // 3这样我们每次调用parseInt()的时候都可以计数一次。
高阶函数
神马叫高阶函数?
JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
Map函数
由于
map()
方法定义在JavaScript的Array
中,我们调用Array
的map()
方法,传入我们自己的函数,就得到了一个新的Array
作为结果。相当于对Array中的每个值都进行一次我们指定的函数操作。12345function pow(x) {return x * x;}var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]Reduce函数
Array的reduce()把一个函数作用在这个Array的[x1, x2, x3…]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:
1234var arr = [1, 3, 5, 7, 9];arr.reduce(function (x, y) {return x + y;}); // 将array中的数据进行了累加操作Filter函数
通过Filter我们可以过滤我们需要的数组元素。和
map()
类似,Array
的filter()
也接收一个函数。和map()
不同的是,filter()
把传入的函数依次作用于每个元素,然后根据返回值是true
还是false
决定保留还是丢弃该元素。12345var arr = [1, 2, 4, 5, 6, 9, 10, 15];var r = arr.filter(function (x) {return x % 2 !== 0;});r; // 删除数组中的偶数,返回结果为[1, 5, 9, 15]去除数组中的重复元素:
12345r = arr.filter(function (element, index, self) {return self.indexOf(element) === index;});//这里的回调函数第一个参数代表数组中的某个元素,第二个代表下标,第三个代表数组本身。//去除重复元素依靠的是indexOf总是返回第一个元素的位置,后续的重复元素位置与indexOf返回的位置不相等,因此被filter滤掉了。排序函数
常规定,对于两个元素
x
和y
,如果认为x < y
,则返回-1
,如果认为x == y
,则返回0
,如果认为x > y
,则返回1
。所以自定义排序函数(sort会直接对数组内容进行修改):
12345678910var arr = [10, 20, 1, 2];arr.sort(function (x, y) {if (x < y) {return -1;}if (x > y) {return 1;}return 0;}); // [1, 2, 10, 20]
函数的闭包
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
举个栗子:正常的求和函数
123456function sum(arr) {return arr.reduce(function (x, y) {return x + y;});}sum([1, 2, 3, 4, 5]); // 15使用闭包的求和函数:
12345678910111213function lazy_sum(arr) {var sum = function () {return arr.reduce(function (x, y) {return x + y;});}return sum;}var f1 = lazy_sum([1, 2, 3, 4, 5]); // function sum()这里并不会立即执行函数f1(); // 15 调用函数的时候才会真正的执行计算。var f2 = lazy_sum([1, 2, 3, 4, 5]);f1 === f2; // false每次返回的都是不同的函数。我们在函数
lazy_sum
中又定义了函数sum
,并且,内部函数sum
可以引用外部函数lazy_sum
的参数和局部变量,当lazy_sum
返回函数sum
时,相关参数和变量都保存在返回的函数中,这种称为闭包(Closure)。匿名函数会有这种功能其主要原因是因为我们创建的匿名函数并没有执行,如果想要创建匿名函数并执行,可以使用以下语法:
1(function (x) { return x * x }) (3);
箭头函数
在ES6中新增了一种匿名函数标准,可以使用箭头语法。
12345x => x * x//作用相当于function (x) {return x * x;}如果包涵多个表示式:
12345678x => {if (x > 0) {return x * x;}else {return - x * x;}}包涵多个参数:
1234567891011121314// 两个参数:(x, y) => x * x + y * y// 无参数:() => 3.14// 可变参数:(x, y, ...rest) => {var i, sum = x + y;for (i=0; i<rest.length; i++) {sum += rest[i];}return sum;}