函数 | callee属性和caller属性

回顾

  • arguments对象是一个类数组对象,包含调用函数时传入的所有参数,可以使用中括号语法访问其中的元素

  • arguments可以用length属性来检查传入参数的个数,主要length由实参决定,而不是形参(这里需要区分的是,函数也有length属性,函数的length表示函数需要传入形参的个数)

  • arguments的值始终会与对应的命名参数同步

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function sum(num1, num2) {
    // 此处arguments如下
    /*
    arguments = {
    0: 1,
    1: 2,
    length: 2
    }
    */
    arguments[0] = 10;
    // 此处arguments如下
    /*
    arguments = {
    0: 10,
    1: 2,
    length: 2
    }
    */
    console.log(arguments[0] + num2);
    };

    sum(1,2); // 所以打印结果为10+2 = 12

callee属性:arguments的属性

arguments的callee属性,是一个指向arguments对象所在函数的指针

callee作用:让函数逻辑与函数名解耦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 经典阶乘例子
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * fractorial(num - 1); // 这里需要递归factorial,说明这个函数和factorial是紧密耦合的,修改了函数名函数就会有问题
};
}

// 使用callee可以让函数逻辑与函数名解耦
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1); // 这样的话无论之后函数叫什么都没什么问题了
};
}

小缺陷: 严格模式下不能访问arguments.callee

可以使用命名函数表达式进行优化

1
2
3
4
5
6
7
const factorial = (function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1)
}
})

caller属性:函数对象的属性

caller属性引用的是调用当前函数的函数,或者如果是在全局作用域中调用的则是null

1
2
3
4
5
6
7
8
9
function inner() {
console.log(inner.caller);
}

function outer() {
inner();
}

outer(); // function outer() { inner()}

如果要降低耦合度,可以使用arguments.callee属性

1
2
3
4
5
6
7
function outer() {
inner();
}

function inner() {
console.log(arguments.callee.caller); // arguments.callee指向inner函数
}

在严格模式下访问arguments.callee会报错,在ES5中也定义了arguments.caller,严格模式下访问会报错,非严格模式下为undefined,在严格模式下,不能给函数的caller属性赋值