JavaScript语言精粹学习笔记-函数(一)

函数包含一组语句,它们是javascript的基础模块单元,可以被执行或调用任意多次,用于代码复用、信息隐藏和组合调用。函数用于指定对象的行为。
4.1 函数对象:在javascript中函数就是对象,函数对象链接到Function.prototype(它本身链接到Object.prototype)原型上。
每个函数对象在创建时附有两个附加的隐藏属性:函数的上下文和实现函数行为的代码。
每个函数对象在创建时也随带有一个prototype属性。它的值是一个拥有constructor属性且其值即为该函数的对象。
函数就是对象,所以它可以被存储在变量、对象和数组中,函数可以被当做对象传递给其他函数,函数可以返回函数,并且函数可以有方法。

函数的与众不同之处在于它可以被调用。

4.2 函数字面量:
函数字面量包括四个部分:
(1)、第一部分是保留字Function
(2)、第二部分是函数名,它可以被省略。就是匿名函数。
(3)、第三部分是包含在圆括号之中的一组参数。用逗号分隔。
(4)、第四部分是包围在花括号中的一组语句,他们在函数被调用的时候执行。

//这样的函数声明会在任何赋值表达式之前被浏览器执行,
//所以可以在任意位置调用add。
document.writeln(add(1,3))//4
function add(a,b){
   return a+b;
 };
 //如果使用函数定义表达式定义函数,
 //在此时调用sum(1,2)会报错。
document.writeln(sum(1,3))//TypeError: sum is not a function 
var sum = function jia(a,b){
    return a+b;
 };
//在此种情况下,jia是不能使用的,实际使用函数的时候应该使用sum。
document.writeln(sum(1,3));//4
document.writeln(jia(1,3));//TypeError: jia is not a function 

函数字面量可以出现在任何允许表达式出现的位置。
函数可以创建在函数之中,一个内部函数除了可以访问他自己的变量和参数,还可以访问他被嵌套在其中的函数的变量和参数。
通过函数字面量创建的函数对象,包含一个指向外部上下文的链接。这就是闭包,也就是javascript的强大的根基。

4.3 函数的调用
在定义函数的时候,函数中的代码不会执行,只有当调用开始(使用())之后,才会执行。调用一个函数将停止当前函数的运行,将控制权和参数传递个要调用的函数。

javascript中有四种调用函数的模式:
一、方法调用模式。
二、函数调用模式。
三、构造器调用模式
四、apply调用模式(通过函数的call()和apply()方法调用)。

函数调用操作符是跟在任何能产生函数值的表达式之后的一对圆括号。圆括号内可包括零个或多个由逗号隔开的表达式,每个表达式产生一个数值,赋值给形参。
当实际参数个数与形式参数个数不符合时不会导致错误,多了忽略,少了剩余形参赋值undefined。参数传递时,不会校验参数类型。
除了声明和定义好的参数之外,每个函数都接收两个额外的参数this和arguments。参数this的值非常重要,它取决于调用函数模式。

4.3.1方法调用模式
当一个函数被保存为对象的一个属性时,我们称之为方法。
此时方法可以通过this方法可以访问所属对象的属性。

var myObject = {
    myValue: 0,
    increment: function (inc) {
        this.myValue += typeof inc === 'number' ? inc : 1;
    }
};

如果一个函数表达式通过存取表达式(一个.表达式或[subscript]下标表示法)进行调用,我们称之为方法调用。

myObject.increment();
document.writeln(myObject.myValue);    // 1

myObject.increment(2);
document.writeln(myObject.myValue);    // 3

4.3.2函数调用模式
当一个函数不是一个对象的属性时,它被当做函数。类似于

function minus(a, b) {
    return a + b;
};

当函数以此模式被调用时,this被绑定到全局对象。这本来没什么,但是当使用内部函数时,this对象还是绑定到全局对象,这就有问题了。不过这个问题很容易解决,如果给一个变量赋值为this,那么内部函数就可以通过这个变量就可以访问到this,我们可以叫他that。

var myObj={
    myValue:10
 }
var myValue = 19;
myObj.double = function () {
    // 使用that对象保存this,
    // 此时this和that都是指向myObj
    var that = this;    
    var helper = function () {
        // 此时this指向全局对象,
        // this.myValue=10;
        this.myValue += this.myValue;
        // 此时that指向myObj,
        // that.myObj=10
        that.myValue += that.myValue;
    };
    helper();    // Invoke helper as a function.
};
myObj.double();
document.writeln(myValue); //38
document.writeln(myObj.myValue);//20  

4.3.3构造器调用模式
javascript是基于原型继承的语言,这意味着对象可以从其他对象继承属性。
如果在一个函数前带上new来调用,那么将创建一个隐藏链接到该函数prototype成员的新对象,同时this将绑定到新对象上。

var Cstr = function(str){
    this.status = str;
}  
Cstr.prototype={
    status:"ceshi",
    getStatus:function(){
       return this.status;
    }
}
// 此时语句this.status = str;中,
// this指向的是对象my
var my = new Cstr("hello");
document.writeln(my.getStatus())//"hello"

new前缀也会改变return 的行为。通常在构造函数中一般不使用return返回,构造函数会显示的返回一个对象。但是如果使用了return语句,且这个return返回一个新对象,则构造函数返回新对象。
比如上面的代码

var Cstr2 = function(){
    return {name:"hehe"};
}  
Cstr2.prototype={
    status:"ceshi",
    getStatus:function(){
       return this.status;
    }
}
var my2 = new Cstr2();
document.writeln(my2.name);//"hehe"
document.writeln(my2.status);//undefined

如果return后面没有值,或者返回一个原始类型,则使用新对象作为调用的结果。

var Cstr3 = function(){
    return 2;
}  
Cstr3.prototype={
    status:"ceshi",
    getStatus:function(){
       return this.status;
    }
}
var my3 = new Cstr3();
document.writeln(my3.status);//"ceshi"

调用构造器函数时,没有加上new前缀,将会发生非常糟糕的事情,没有编译时警告,没有运行时警告。
不推荐使用这种形式的构造器。

4.3.4apply模式调用
javascript是一门函数式的面向对象编程语言,所以函数可以拥有方法。
而函数的apply方法,让我们构建一个参数数组,并用这个数组去掉用函数。它允许我们改变this的值。
apply接受两个参数,第一个参数给this,第二个就是参数数组 。
如果第一个参数为null,则为默认的this。

var sum = function(a,b){
return a+b;
}
var result = sum.apply(null,[2,3]);
document.writeln(result);//5
result = 12;
var obj1={
    result:10
}
var obj2={
    result:23
}
var double = function(){
    this.result += this.result;
}
double.apply(null,[]);         //使用默认this
document.writeln(result);      //24
document.writeln(obj1.result); //10
document.writeln(obj2.result); //23
double.apply(obj1,[]);         //此时double中this指向obj1
document.writeln(result);      //24
document.writeln(obj1.result); //20
document.writeln(obj2.result); //23
double.apply(obj2,[]);         //此时double中this指向obj2
document.writeln(result);      //24
document.writeln(obj1.result); //20
document.writeln(obj2.result); //56

转载请注明出处http://blog.csdn.net/laozhaishaozuo/article/details/45696807

猜你喜欢

转载自blog.csdn.net/laozhaishaozuo/article/details/45696807