日期:2014-05-16  浏览次数:20355 次

JavaScript: The Good Parts 读书笔记(二)

二.函数

  • JS 中函数亦是对象
    ????? 使用字面变量表面法产生的对象被链接到Object.prototype.而函数对象将会链接到 Function.prototype.(该原型对象本身也是连接到 Object.prototype).
    ????? 每个函数在创建时将会附有个隐藏属性: 函数的执行上下文(function context) 和 实现函数行为的代码(!).
    每个函数对象在创建时也随带有一个 prototype 属性,它的值是一个拥有constructor 属性的对象. 该constructor属性的值既是函数本身(!). 这和隐藏连接到的 Function.prototype 完全不同!!
    ????? 因为函数是对象,所以它们可以像任何其他的值一样被使用。函数可以存放在变量,对象和数组中。也可被看做参数传递给其他函数,函数自身也可返回另一个函数。最后,还是因为函数是对象,它也可以拥有属性(方法). 或许函数与普通对象最大的不同之处在于,它可以被调用(通过'(args)'进行).?
    ????? 函数可以通过字面表达来创建:
    var add = function(a,b){
    	return a + b;
    };
    ?
  • 函数的声明:
    ? 函数字面表达包括四个部分:
    ???? 第一个部分是保留字 function.
    ???? 第二个部分是函数名,该名称可以被忽略. 如果名称被忽略,该函数将成为一个匿名函数. 函数可以用它的名称来递归调用自己。此名称也可以被调试器和开发工具识别.
    ???? 第三部分是包围在圆括号中的一组参数。其中每个参数用逗号分隔。这些名称将被定为函数中的变量(!),这些变量不像普通变量一样被初始化为undefined. 而是在该函数被调用时初始化为实际调用的参数值.
    ???? 第四部分是包括在{}内的函数体,函数体内的语句在调用时执行.

    ?? 调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数。 除了声明时定义的形参, 每个函数接受两个附加的参数: thisarguments. 参数this的取值取决于被调用的模式.在JS中一共有四种调用模式: 方法调用模式,函数调用模式构造器调用模式Apply/Call调用模式.这些模式在如何初始化关键参数this 上存在着差异.

    ?? 函数调用时,使用圆括号来表达,圆括号中的可以包含用逗号分隔的表达式,每个表达式产生一个参数值。当实际参数的个数与形式参数的格式不匹配时并不会导致运行时错误,如果实际参数值过多了,超出的参数值将会被忽略(!). 如果实际参数值过少,缺少的值将会被替换为 undefined.

  • 方法调用模式
    ??? 当一个函数被保存为对象的一个属性时,我们称它为一个 方法. 当一个方法被调用时,this被绑定到该对象上。如果一个调用表达式包含一个属性存取表达式(即.或[]导航),那么他被看做一个方法调用模式.
    var myObject = {
    	value : 0,
    	increment : function(inc){
    		// 方法this可以访问对象的属性。 this到对象的绑定发生在调用时。
    		// 这种延迟绑定使得函数可以对this进行高度复用.
    		// 通过this可以访问所属对象上下文的方法称为公共方法(!).
    		this.value += typeof inc === "number" ? inc : 1;
    	}
    };
    
    myObject.increment(); // 使用属性存取表达式调用,属于方法调用模式.
    document.writeln(myObject.value);
    myObject.increment(2);
    document.writeln(myObject.value);
    
    ?
  • 函数调用模式
    ?? 当一个函数并非一个对象的属性时,那么它被当做一个函数来调用:
    var sum = add(3,4); // 7
    ?? 当函数以此模式调用时,this被绑定到全局对象。这是JS语言设计上的一个错误。在正确设计时,当函数中的内部函数被调用时,this应该仍然绑定到外部函数的this变量.这个设计错误的后果是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值, 所以不能共享该方法对原对象的访问权。幸运的是,有一个很容 易的解决方案。通过定义一个临时变量.
    myObject.double = function(){
    	var that = this; // 保存外部函数的上下文.
    
    	var helper = function(){
    		that.value = add(that.value,that.value);
    	};
    
    	helper(); // 函数方式调用helper. 此时helper内部的context 是 window. 所以需要定义 that.
    };
    
    myObject.double();
    document.writeln(myObject.value);
    ?
  • 构造器调用模式
    ?? Javascript 是一门基于原型继承的语言。 这意味着对象可以直接从其他对象继承属性。如果在一个函数前面带上 new 来调用,那么将创建一个隐示链接到该函数的prototype成员上的新对象,同时 this 将会被绑定到这个新对象上。
    // 按照约定,对于构造器变量. 它们需要以开头大写的格式来命名。
    var Quo = function(string){
    	this.status = string;
    };
    // 为所有Quo 的实例提供一个get_status方法.
    Quo.prototype.get_status = function(){
    	return this.status;
    };
    var myQuo = new Quo("confused");
    document.writeln(myQuo.get_status()); // 令人困惑的结果.
    
    ?
  • Apply/Call 调用模式
    ? 因为JavaScript 是一门函数式的OO语言,所以函数自身也可以拥有方法.
    var args = [3,4];
    var sum = add.apply(null,args); // context 为null 时表示将context 绑定到 window.
    document.writeln(sum);
    
    var statusObject = {status : "A-OK"};
    // 使用statusObject 作为函数上下文去调用 get_status方法.
    var status = Quo.prototype.get_status.apply(statusObject);
    document.writeln(status);
    
    ? ?apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call调用的参数传入(从第二个参数开始)。如 func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入.

  • 函数的参数
    ?? 函数被调用时,另外一个传入的默认参数为 arguments 数组,通过它函数可以访问所以它被调用时传递给它的参数列表。因为语言设计的错误,arguments 并不是一个真正的数组(!), 他只是一个类似数字的对象,拥有一个 length属性,但他缺少所有的数组方法(!).
    var sum = function(){