3

前言:我入门学的 java这种强类型语言,刚开始学js第一感觉是挺简单,后来发现还是too young。所以,本次就把作用域、匿名函数做一个完整总结,黑喂狗~~~

-------------------分割线----------------------

1.函数

返回值/参数

返回值:::::::::::::::::::::::::
js的函数在定义时不必指定返回值,而且任何函数都可以通过return随时返回值。
函数在执行完return之后停止并立即退出,因此return后面的代码永远不会执行。

    function sum(num1,num2){
        return num1 + num2;
        alert("Hello World");  //这段代码永远不会执行
    }

参数:::::::::::::::
js函数不介意传递进来多少个参数,也不在乎传递进来的是什么参数类型。
参数在内部是由一个数组来表示的,函数接收的永远都是这个数组,函数体内部可以通过arguments对象来访问这个参数数组,从而可以获取传递给函数的每一个参数。

如下代码:

    function howManyArgs(){
        alert(arguments.length);
    }
    
    howManyArgs("String",45);  //2
    howManyArgs();             //0
    howManyArgs(12);           //1

从上面的代码可以看出来,定义函数时没有给函数指定参数名字,而调用时依然可以传递进去任何数量和类型的参数,因此在js函数中:命名的参数只是提供便利,但不是必须的。理解这点很重要。

不完美的重载:::::::::::::::::
既然可以用arguments.length判断传入参数的个数,那么js函数也可以实现不完美的重载。
如下代码:

    function doAdd(){
        if(arguments.length == 1){
            alert(arguments[0] + 10);
        }
        if(arguments.length == 2){
            alert(arguments[0] + arguments[1]);
        }
    }
    
    doAdd(10);   //20
    doAdd(30,20);    //50

arguments[i]和对应命名参数的关系:::::::::::::::::
看如下代码:

    function doAdd(num1,num2){
        arguments[1] = 10;          //重写第二个参数
        alert(arguments[0] + num2);
    }

arguments对象中的值会自动反映到对应的命名参数,但是读取这两个值不会访问相同的内存空间,
修改命名参数并不会改变arguments中对应的值
arguments对象长度是由调用函数时传入的参数个数决定的,不是由定义函数时的命名参数的个数决定的。

2.传递参数

js中所有的函数都是按值传递的。
在向参数传递基本类型时,被传递的值会被复制给一个局部变量。
在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。

来看几个例子:

    function addTen(num){
        num += 10;
        return num;
    }
    var count = 20;
    var result = addTen(count);
    alert(count);       //20,没有变化
    alert(result);      //30

count传递给参数num,函数内部num做了修改,但是没有反映到count上。

    function setName(obj){
        obj.name = "Jack";
    }
    var person = new Object();
    setName(person);
    alert(person.name);     //Jack

这个例子很容易让人觉得,引用类型做为函数参数传递是按引用传递的,因为局部的修改:obj.name = "Jack",反映在了全局的作用域上:person.name,事实上并不是如此。
事实上:我们创建了一个对象,并把它保存在person变量中,然后把person当做参数传递到setName()函数中复制给了obj,在这个函数内部obj和person引用的是同一个对象。

再看一个例子:

    function setName(obj){
        obj.name = "Jack";
        obj = new Object();
        obj.name = "Rose";
    }
    var person = new Object();
    setName(person);
    alert(person.name);       //Jack
    

这个例子中在setName()函数中,为obj重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name属性。如果person传递给函数setName()之后是按引用传递的,那么对obj.name的修改就会反映到person.name上,事实上person.name依然是Jack。
当在函数内部重写obj时,这个变量的引用就是一个局部变量了,这个局部变量在函数执行完毕后立即销毁。

3.执行环境及作用域

定义什么的太多了就不写了,直接上代码

    var color = "blue";
    function changeColor(){
        var anotherColor = "red";
        function swapColors(){
            var tempColor = anotherColor;
            anotherColor = color;
            color = tempColor;
            //这里可以访问 color,anotherColor 和tempColor
        }
        
        swapColor();
        //这里可以访问color, anotherColor
    }
    
    changeColor();  //这里只能访问color

上面代码中有三个执行环境:全局环境、changeColor()的局部、和swapColors()的局部环境。
全局环境中有一个color变量和changeColor()函数。
changeColor()的局部环境中有一个名为anotherColor的变量和一个swapColors()的函数,但是它也可以访问全局环境中的变量color
swapColors()局部环境中有一个变量tempColor,只能在这个环境中访问到。

无论是全局环境还是changeColor()的局部环境都无权访问tempColor,在swapColors()内部可以访问其他两个环境中的所有变量,因为那两个环境是它的父执行环境。

总结:内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数,每个环境内部没有这个变量或者函数时,都可以向上搜索变量和函数名,直到找到为止。

没有块级作用域

java等语言话括号括起来的代码块都有自己的作用域,但是js中总有例外,看下面代码:

    if(true){
        var color = "blue";
    }
    alert(color);    //blue

这里是在if语句中定义了一个变量color,和作用域函数里面定义变量的例子不同,在if语句中的变量声明将添加到当前的执行环境中,看下面for循环的例子:

    for(var i = 0; i<10; i++){
        doSomething(i);
    }
    alert(i);   //10

对于js,由for语句创建的变量i即使在for循环执行结束后,也依旧会存在于循环外部的执行环境。

4.匿名函数

看下面例子:

      window.onload = 
           function(){
               alert("Hello World");
           };
           
       var person = {
           callName: function(){
               alert("My name is Jack");
           }
       };
       person.callName();  
           
       setTimeout(
           function(){alert("Hello World");},
           500);

1.首先,为load事件创建了一个函数做为事件处理程序,不会直接调用这个函数,而是在页面加载时自动调用,所以没必要为这个函数命名,像这样:

    function sayHello(){alert("Hello World");};
    window.onload = sayHello;

2.声明了一个匿名函数,做为person对象的一个属性callName,可以通过该属性来调用这个方法:
如:person.callName();

3.将匿名函数做为回调函数传递给另外一个函数,代码中将匿名函数做为一个参数传递给window对象的setTimeout()方法,该方法将在半秒后被调用。

-------------------------------------------------------end--------------------------------------------------------


yangdepp
446 声望21 粉丝

只有那些疯狂的人才能引起我的兴趣,那些人疯狂地生活,疯狂地表达,同时对一切事物心怀渴望,他们毫无倦意,不屑于陈词滥调,只是燃烧,燃烧,燃烧。