【读书笔记】细读《JavaScript权威指南》(第八章: 函数)

8.1 函数定义

  • 函数表达式
var f = function fact (x) {
            if (x<=1) return 1;
            else return x * fact(x-1);
        }

如果一个函数定义表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称,或说函数名称将成为函数内部的一个局部变量(通常应用于递归函数)。


  • 函数语句

函数声明语句必须被提前到外部脚本或外部函数作用域的顶部。这也就意味这它们不能出现在循环、条件判断、with等语句块中。这也是es规范没有将函数声明语句归类为语句的原因。

8.2 函数调用

8.2.1 普通的函数调用

var a = f(x);

单独调用函数,计算返回值。普通的函数调用一般不使用this值。在非严格模式下,this指向全局对象;在严格模式下this值为undefined

8.2.2 方法调用

方法是指作为对象属性存在的函数。 与普通的函数调用的唯一区别就是调用上下文(this)指向调用该方法的对象。

方法链(“链式调用”风格的编程)

当方法的返回值是一个对象,这个对象还可以再调用它的方法。这种方法调用序列中(通常称为“链”或“级联”)每次的调用结果都是另外一个表达式的组成部分。

var o = {
    m: function () {
        var self = this;
        console.log(this === o);  // true
        f();

        function f () {
            console.log(this === o);  // false
            console.log(self === o);  // true
        }
    }
}

一个常见的误区就是误认为嵌套函数时this会指向外层函数的上下文。实际上只要记住普通的函数调用里的上下文只有两种可能指向。详见8.2.1小节。

8.2.3 构造函数调用

构造函数调用创建一个新的空对象,这个对象继承来自构造函数的prototype属性,构造函数试图初始化这个新创建的对象,并将其用做调用上下文。

var o = new Object();
var o = new Object;

如果构造函数没有形参,圆括号是可以省略的。

8.2.4 间接调用

任何函数都可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。call与apply都可以用来间接调用函数,两个方法都允许显式指定this。

8.3 函数的实参与形参

JavaScript的函数调用既不检查参数类型,也不检查传入参数的个数。

8.3.1 可选形参

当调用函数时传入的实参比函数声明时指定的形参个数要少,剩下的形参都将设置为undefined值。

function getPropertyNames (o, /* optional */ a) {
    a = a || [];
    for (var property in o) a.push(property);
    return a;
}

可选实参要放在参数表后面,注释也是有必要的。

8.3.2 可变长的实参列表: 实参对象

函数体内可用标识符arguments访问实参对象。实参对象是一个类数组对象。

function max (x, y, z) {
    let max = -Infinity;
    for (let i = 0; i < argument.length; i++) {
        if (max < argument[i]) max = argument[i];
    }
    return max;
}

callee和caller属性

实参对象还定义了两个属性。在非严格模式下,es规定callee指代当前正在执行的函数。caller是非标准的,但被大多数浏览器所实现,它指代调用当前正在执行的函数的函数。

var factorial = function (x) {
    if (x <= 1) return 1;
    return x * argument.callee(x-1); // 在匿名函数中递归调用自身
}

8.3.3 将对象属性用做实参

当函数包含超过三个形参时,记住实参的正确顺序就有点困难了。一个解决方案是通过键值对的形式来传入参数,这样参数的顺序就无关紧要了。

8.4 作为值的函数

自定义函数属性

当函数需要一个“静态”变量来在调用时保持某个值不变,最方便的方式就是给函数定义属性,而不是定义全局变量。

function uniqueInteger () {
    return uniqueInteger.counter++;
}

8.5 作为命名空间的函数

定义一个函数用做临时的命名空间,在这个命名空间里定义的变量都不会污染到全局命名空间。

(function() {}());

该函数被称为立即执行函数,最外层的圆括号是必须的。如果不加,解释器会识图将function解析为函数声明语句,加了以后再会正确解析为函数定义表达式。

8.6 闭包

闭包的定义:

函数对象可以通过作用域链相互关联起来,函数体内部的变量可以保存在函数作用域中(看起来是函数将变量“包裹”了起来)。

闭包的主要特性:

捕捉到局部变量和参数,并一直保存下来。

var scope = 'global scope';
function checkscope () {
    var scope = 'local scope';
    function f () { return scope; }
    return f;
}
checkscope()(); // 'local scope'

上面的代码就是使用闭包的一个基本形式,即在函数体外调用其嵌套子函数(前提是该子函数被函数返回)。
从作用域链上来看,调用 f 时闭包所指向的作用域链和定义 f 时的作用域链不同,简单地表示一下,前者是【f 的局部变量保存对象 - 全局对象】,而后者是【f 的局部变量保存对象 - checkscope 的局部变量保存对象 - 全局对象】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值