函数进阶
函数进阶
函数“提升”
在之前的教程中我们已经了解了 "hoisting(提升)"。提升是 JavaScript 默认将当前作用域提升到前面去的行为。提升应用在变量的声明与函数的声明。因此,函数与变量一样,可以在声明之前调用:
myFunction(5);
function myFunction(y) {
return y * y;
}
使用表达式定义函数时无法提升。
ES6箭头函数
ES6 新增了箭头函数。箭头函数等价于其他语言的lambda表达式。
箭头函数表达式的语法比普通函数表达式更简洁。
(参数1, 参数2, …, 参数N) => { 函数声明; }
(参数1, 参数2, …, 参数N) => 表达式;
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }//表达式只有一个
当只有一个参数时,圆括号是可选的:
(单一参数) => {函数声明}
单一参数 => {函数声明}
没有参数的函数应该写成一对圆括号:
() => {函数声明}
例子:
var x = function(x, y) {
return x * y;
}// ES5
const x = (x, y) => x * y;// ES6
var p=x(1,2);//调用方式
嵌套函数
嵌套函数:在函数中声明的函数就是嵌套函数,嵌套函数只能在当前函数中可以访问,在当前函数外无法访问。
function fu() {
function zi() {
console.log("我是儿子")
}
zi();
}
fu();
匿名函数
匿名函数:没有名字的函数就是匿名函数,它可以让一个变量来接收,也就是用 “函数表达式” 方式创建和接收。
var fun = function () {
alert("我是一个匿名函数");
}
fun();
立即执行函数
立即执行函数:函数定义完,立即被调用,这种函数叫做立即执行函数,立即执行函数往往只会执行一次。同样的,箭头函数也有如此效果。
(function () {
alert("我是一个匿名函数");
})();
函数返回类型
函数做值
值类型的返回值让函数可以当成值来调用
function myFunction(a, b) {
return a * b;
}
var x = myFunction(4, 3);
函数为函数
函数类型的返回值可以让函数当成新函数来调用。
在C语言中,只能使用函数指针来形成类似的操作。
var add = function(x){
return function(y){
return x + y
}
}
//ES6
var add = x => (y => x + y);
//调用
var add2 = add(2);
var add200 = add(200);
add2(2); // =>4
add200(50); // =>250
函数是对象
作为对象的函数,自然有它的属性和方法可供调用。JavaScript 函数有个内置的对象 arguments 对象。
argument 对象包含了函数调用的参数数组。
arguments.length 属性返回函数调用过程接收到的参数个数
//通过这种方式你可以很方便的找到最大的一个参数的值:
x = findMax(1, 123, 500, 115, 44, 88);
function findMax() {
var i, max = arguments[0];
if(arguments.length < 2) return max;
for (i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
call()和apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。
function myFunction(a, b) {
return a * b;
}
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray); // 返回 20
function myFunction(a, b) {
return a * b;
}
myObject = myFunction.call(myObject, 10, 2); // 返回 20
两个方法都使用了对象本身作为第一个参数。 两者的区别在于第二个参数: apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。
在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。
在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。
函数闭包
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。直观的说就是形成一个不销毁的栈环境。
计数器困境:
var counter = 0;
function add() {
return counter += 1;
}
add();
add();
add();
// 计数器现在为 3
//但问题来了,页面上的任何脚本都能改变计数器,即便没有调用 add() 函数。
/*——————————————————————————————————*/
function add() {
var counter = 0;
return counter += 1;
}
add();
add();
add();
// 如果我在函数内声明计数器,如果没有调用函数将无法修改计数器的值:
//但是 现在 本意是想输出 3, 但事与愿违,输出的都是 1 !
/*——————————————————————————————————*/
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}
//这样可以解决函数调用的问题,但是,不能在外界被调用
/*——————————————————————————————————*/
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
//此时由于函数自调用,本身add=function () {return counter += 1;}
//调用add()计数器+1,而计数器受匿名函数的作用域保护,只能通过 add 方法修改,也只能通过add()访问到值
// 计数器为 3
在ES6中,闭包书写为:
var add=(()=>{var counter=0; return ()=>counter+=1;})()
默认参数
ES5 中如果函数在调用时未提供隐式参数,参数会默认设置为: undefined
有时这是可以接受的,但是建议最好为参数设置一个默认值:
function myFunction(x, y) {
if (y === undefined) {
y = 0;
}
}
//或者
function myFunction(x, y) {
y = y || 0;
}
如果y已经定义 , y || 返回 y, 因为 y 是 true, 否则返回 0, 因为 undefined 为 false。
如果函数调用时设置了过多的参数,参数将无法被引用,因为无法找到对应的参数名。 只能使用 arguments 对象来调用。
ES6 函数可以自带参数
ES6 支持函数带有默认参数,就可以省略了判断 undefined 和 || 的操作:
function myFunction(x, y = 10) {
// y is 10 if not passed or undefined
return x + y;
}
myFunction(0, 2) // 输出 2
myFunction(5); // 输出 15, y 参数的默认值
函数式编程思维
函数式编程与命令式编程最大的不同其实在于:函数式编程关心数据的映射,命令式编程关心解决问题的步骤。即函数式编程关心类型(代数结构)之间的关系,命令式编程关心解决问题的步骤。
这里的映射就是数学上「函数」的概念——一种东西和另一种东西之间的对应关系。这也是为什么「函数式编程」叫做「函数」式编程。函数式编程中,函数才是一等公民,一切都是函数。(与一切都是对象相对应)。一定意义上,函数式对应lambda演算而命令式对应图灵机。而lambda演算则有如下规则:
- 变量
- 函数
- 替代(并非赋值)
函数式编程有以下特点:
- 变量不可变:闭包。
- 无法实现循环:依赖递归(循环是递推)
- 高阶函数:返回值为函数
函数的柯里化
函数柯里化(curry)的定义很简单:传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。例如:
var add = x => (y => x + y);
var add2 = add(2);
var add200 = add(200);
add2(2); // =>4
add200(50); // =>250
将加法变成了两个执行函数的步骤。
函数组合
//两个函数的组合
var compose = function(f, g) {
return function(x) {
return f(g(x));
};
};
//ES6
var compose = (f, g) => (x => f(g(x)));
var add1 = x => x + 1;
var mul5 = x => x * 5;
compose(mul5, add1)(2);
// =>15
参考资料: