[JSTDG]JavaScript函数

JavaScript语法: 函数

  • 上下文:函数总有上下文,可以通过this关键字引用其上下文;与java不同,函数的上下文未必是对象.如果函数挂栽于一个对象,则该函数被叫做对象的方法,该对象成为该函数本次调用的上下文.
  • 构造函数:初始化一个新创建的对象.
  • 函数=对象:函数有自己的属性,甚至有自己的方法.
  • 嵌套定义:函数中可以定义函数,子函数可以访问所处作用域中的任何变量.
  • 闭包:嵌套定义的副产品,给JavaScript带来了非常强劲的变成能力.

function关键字

函数定义表达式

  • 只出现在它作为一个大表达式的一部分的时候
  • 可以子表达式的方式出现在JavaScript代码的任何地方
  • 函数名是可选的
  • 定义后可立即调用: var tensquared = (function(x) {return x * x;} (10));

函数声明语句

  • 被提前到外部脚本或外部函数作用域的顶部
  • 并非真正的语句: 不能出现在循环,条件,try/cache/finally以及with语句中

  • 函数名称是函数内部的一个局部变量

  • 无return语句的函数返回undefined

调用一个JavaScript函数

任何一种函数,都严格遵守以下调用原则.
作为一个例子,嵌套在内部的函数并不会因为其外层函数是方法而变成方法.

作为函数

被调用的函数不是任何对象的属性.

  • 调用上下文是全局对象,如浏览器JavaScript中的window
    全局函数是作为全局对象属性出现的,所以这样规定调用上下文是自然的.
    第二,全局对象没有名字,标准的引用方式是使用this关键字.window是浏览器全局对象的一个属性,它包含对自身的引用.
  • 在严格模式下,调用上下文是undefined
  • 以函数形式调用的函数通常不使用this关键字

作为方法

函数是一个对象的属性或者数组中的一个元素.

  • this关键字引用其隶属对象

作为构造函数

当函数或方法调用之前带有new关键字时,函数成为构造函数.

  • 若构造函数没有形参,调用该构造函数时可省略小括号
  • 构造函数会创建新的对象,新对象继承自构造函数的prototype属性
  • 构造函数以新对象为其上下文
  • 除非显示返回一个对象,否则构造函数总是返回新对象

间接调用

通过调用函数的call()和apply()方法,可以间接调用函数.

  • 这两个方法可以显示指定this的值
  • call()方法使用它自由的实参列表,apply()方法则要求以数组形式传入实参.

实参和形参

JavaScript不对传入的参数做类型检查,也不检查传入参数的个数.

  • 个数匹配:形参被依次赋与实参的值,多余的形参被赋值为undefined,多余的实参不能通过形参直接访问
  • 实参对象:arguments这个类数组对象保存了全部实参
  • 访问调用栈:callee引用当前正在执行的函数,caller引用调用callee的函数
  • trick:将对象属性用作实参,这样你就不必记住实参的顺序

命名空间

JavaScript没有块级作用域.
也就是说,JavaScript变量的作用域只有两种:全局的/函数内部的.因此,为了让一个变量的作用域局限于一个代码块内,我们将这个代码块用匿名函数包围起来:

(function(){
    //statements
}());

闭包

作用域链

调用对象/声明上下文对象
局部变量是某个神秘对象的属性,该对象被称为调用对象.调用对象是一种对我们不可见的内部实现,然而对我们的编程非常重要.

于是:
- 变量总是对象的属性
- 对象总是另一个对象的属性

这样,按照隶属关系,JavaScript中的全局对象和所有调用对象最终构成了一颗树,就叫作用域树好了.

作用域链
在JavaScript的作用域树上,从某一节点开始向根结点上溯,这条路径上的所有对象(调用对象/全局对象)构成作用域链.

变量解析:当JavaScript需要查找变量x的值的时候,它会从作用域链中的第一个对象开始查找,如果此对象有个名为x的属性,则会直接引用这个属性的值,否则,JavaScript会向上查找链中的下一个对象.如果作用域链上不存在x,则抛出EeferenceError.

定义一个函数时,新函数保存其创建者的作用域链.当新函数被调用时,一个新的调用对象被创建并连接到已保存的作用域链上.

你理解上面这两段话吗?

/*
 * <javascript权威指南> P183
 */
var scope = "global";
function checkscope() {
    var scope = "local";
    function f() {return scope;}
    return f;
}
checkscope()(); //"local"

最后,作用域链的重叠部分是共享的: 如果一段代码修改了其作用域链上的某个调用对象的属性(也就是一个变量),那么这种改变对所有引用该调用对象的代码可见.

闭包

函数的定义后,它的作用域链随之”定型“,并永远与这个函数关联在一起.于是,函数的每个调用对象都会连接至已保存的作用域链的末端,因此我也说:函数位于这条作用域链的末端.

与之不同的是,函数的this并没有在定义时”定型”.函数this的值随着调用栈的变化而变化.

这样,我们就可以定义闭包:

闭包
位于作用域链末端的函数

这样,JavaScript中的每个函数都是一个闭包.但是,通常,闭包指的是嵌套在函数内部的函数.

函数是个对象

  • 函数名只不过是个引用了函数对象的普通变量而已
  • 函数拥有属性,甚至拥有方法和一个构造器Function()

下面是函数的属性,方法和构造函数

length
函数形参的个数
prototype
对一个对象的引用,这个对象叫做”原型对象”, 每个函数都引用不同的原型对象, 此外,当函数用作构造函数的时候,新对象会从原型对象上继承属性
call()和apply()
间接调用函数.第一个参数是调用上下文,剩下的参数传递到原函数

call()函数一个一个地传递参数,apply()函数则使用数组传递参数:

f.call(obj,1,2);
f.apply(obj,[1,2]);
bind()
将函数绑定到一个对象.

bind()函数可以这样模拟:

/*
 * JSTDG P191
 */
if (!Function.prototype.bind) {
    Function.prototype.bind = function(o, /* args */) {
        var self = this, boundArgs = arguments;
        return function() {
            var args = [], i;
            /*
             * 将原函数的调用参数传入args
             */
            for(i = 1; i < boundArgs.length; ++i) args.push(boundArgs[i]);
            /*
             * 新函数的调用参数将覆盖原函数的调用参数
             */
            for(i = 0; i < arguments.length; i++) args.push(arguments[i]);
            return self.apply(o, args);
        }
    }
}   

这样,下面的代码就好理解了:

/*
 * JSTDG P191
 */
var sum = function(x,y) {return x + y};
var succ = sum.bind(null, 1);
succ(2) // 3

function f(y,z) {return this.x + y + z};
var g = f.bind({x:1}, 2);
g(3) // 6

但是,真正的bind()方法有一些特性是上述代码无法模拟的: JSTDG P192

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值