JavaScript的函数和作用域闭包

1. 函数

1.1 定义函数

function add(x, y){
    return x + y;
}

上述函数定义如下:

  • 关键字function指出这是一个函数定义;
  • add是函数的名称;
  • (x, y)括号内列出函数的参数,多个参数以,分隔;
  • {}之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句。

函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。
如果没有return语句,函数执行完毕后也会返回结果,只是结果为undefined

JavaScript的函数也是一个对象,函数名可以视为指向该函数的变量。因此,函数也可以像下面这样定义。

var add = function (x, y){
	return x + y;
}

这种情况下,function (x, y){}是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量add,所以,通过变量add就可以调用该函数。

1.2 调用函数

调用函数时,按顺序传入参数即可。

add(1, 2); // 3

关键字arguments只在函数内部起作用。我们通过arguments可以获得调用者传入的所有参数。事实上,arguments最常用于判断传入参数的个数。

function add(x, y){
	for(let i = 0; i < arguments.length; i++){
		console.log(arguments[i]);
	}
    return x + y;
}

console.log(add(1,5)); // 1 5 6

2. 作用域

2.1 概述

在JavaScript中,用var声明的变量是有作用域的。
如果一个变量在函数体内声明,则该变量的作用域是整个函数体。
如果两个不同的函数各自声明了一个同名变量,那么这两个变量是相互独立的,互不影响。
由于JavaScript的函数可以嵌套,内部函数可以访问外部函数定义的变量,反过来则不行。
JavaScript的函数在查找变量时从自身函数定义开始,从"内"向"外"查找。
如果内部函数定义了与外部函数重名的变量,则内部函数的变量将"屏蔽"外部函数的变量。

2.2 变量提升

JavaScript会把变量声明提升到顶部。

function foo(){
	console.log(x); // 不报错
	var x = 'Hello';
}

// 等同于

function foo(){
	var x;
	console.log(x);
	x = 'Hello';
}

3. 方法

在一个对象中绑定函数,称为这个对象的方法。

var me = {
	name: '张三',
	birth: 1994,
	age: function(){
		var y = new Date().getFullYear();
		return y - this.birth;
	}
};

console.log(me.age()); // 25

如果以对象的方法形式调用me.age(),函数age()this指被调用的对象me

3.1 applycallbind

var obj = {birth: 1990};

console.log(me.age.call(obj)); // 29
console.log(me.age.apply(obj)); // 29
let bind = me.age.bind(obj);
console.log(bind()); // 29

applycallbind这三个函数的作用是改变函数执行时的上下文,即改变函数运行时的this的指向。

  • applycall改变了函数的this上下文后便执行该函数,bind则返回改变了上下文后的一个函数。
  • applycall的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。
let arr1 = [1, 2, 10, 4];
//例子:求数组中的最值
console.log(Math.max.call(null, 1, 2, 10, 4)); // 10
console.log(Math.max.call(null, arr1)); // NaN
console.log(Math.max.apply(null, arr1)); // 10

5. 高阶函数

5.1 概述

JavaScript的函数接收变量作为参数,而函数名可以视为指向该函数的变量。因此,一个函数可以接收另一个函数作为参数。这种函数被称为高阶函数

function add(x, y, f) {
    return f(x) + f(y);
}

console.log(add(-5, 6, Math.abs)); // 11

内置对象Array提供了一些高阶函数:

  • map
  • reduce
  • filter
  • sort
  • every
  • find
  • findIndex
  • forEach

相关阅读JavaScript Array 对象

5.2 闭包

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

/*		
function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}
*/
function lazy_sum(arr) {
    return function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    };
}

var f = lazy_sum([1, 2, 3, 4, 5]);
console.log(f()); // 15

lazy_sum函数中定义了函数sumsum函数可以访问lazy_sum函数的变量arr(内部函数可以访问外部函数,参数可以看作函数中声明的变量)。当一个函数返回了一个函数后,相关的变量都保存在返回的函数中。
返回的sum函数依然保持对lazy_sum函数的作用域的引用,这个引用就叫作闭包
注意:返回的函数没有立即执行,而是直到调用了f()才执行。
使用闭包时要牢记一点是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

再看另一个例子。

var buildMultiplier = function(x) {
    return function(y) {
        return x * y;
    }
}

var double = buildMultiplier(2);
var triple = buildMultiplier(3);

console.log(double(3)); // 6
console.log(triple(3)); // 9

参考

posted @ 2019-08-23 17:05  gzhjj  阅读(184)  评论(0编辑  收藏  举报