资讯专栏INFORMATION COLUMN

js函数:参数、作用域、匿名函数总结

BakerJ / 1227人阅读

摘要:在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。当在函数内部重写时,这个变量的引用就是一个局部变量了,这个局部变量在函数执行完毕后立即销毁。

前言:我入门学的 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--------------------------------------------------------

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79711.html

相关文章

  • 由两道题扩展的对作用,作用链,闭包,立即执行函数,匿名函数的认识总结

    摘要:前言最近在学前几天看到两道题刚开始看懵懵懂懂这几天通过各种查资料慢慢的理解顿悟了对匿名函数闭包立即执行函数的理解也更深了一点在此分享给大家我的理解与总结希望能帮助大家理解因为这篇文章是我用心总结的查阅了很多的资料所以总结的比较细篇幅较长如果 前言 最近在学JS,前几天看到两道题,刚开始看懵懵懂懂,这几天通过各种查资料,慢慢的理解,顿悟了,对匿名函数,闭包,立即执行函数的理解也更深了一点...

    piapia 评论0 收藏0
  • [ JS 进阶 ] 闭包,作用链,垃圾回收,内存泄露

    摘要:执行返回的内部函数,依然能访问变量输出闭包中的作用域链理解作用域链对理解闭包也很有帮助。早期的版本里采用是计数的垃圾回收机制,闭包导致内存泄露的一个原因就是这个算法的一个缺陷。 关于闭包,我翻了几遍书,看了几遍视频,查了一些资料,可是还是迷迷糊糊的,干脆自己动手来个总结吧 !欢迎指正... (~ o ~)~zZ 1. 什么是闭包? 来看一些关于闭包的定义: 闭包是指有权...

    clasnake 评论0 收藏0
  • JavaScript中的闭包

    摘要:闭包引起的内存泄漏总结从理论的角度将由于作用域链的特性中所有函数都是闭包但是从应用的角度来说只有当函数以返回值返回或者当函数以参数形式使用或者当函数中自由变量在函数外被引用时才能成为明确意义上的闭包。 文章同步到github js的闭包概念几乎是任何面试官都会问的问题,最近把闭包这块的概念梳理了一下,记录成以下文章。 什么是闭包 我先列出一些官方及经典书籍等书中给出的概念,这些概念虽然...

    HmyBmny 评论0 收藏0
  • js 闭包的使用技巧

    摘要:闭包的学术定义先来参考下各大权威对闭包的学术定义百科闭包,又称词法闭包或函数闭包,是引用了自由变量的函数。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。 前言 上一章讲解了闭包的底层实现细节,我想大家对闭包的概念应该也有了个大概印象,但是真要用简短的几句话来说清楚,这还真不是件容易的事。这里我们就来总结提炼下闭包的概念,以应付那些非专人士的心血来潮。 闭包的学术...

    dendoink 评论0 收藏0
  • js闭包的理解

    摘要:一般来讲,函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域,但是闭包的情况有所不同理解闭包的前提先理解另外两个内容作用域链垃圾回收作用域链当代码在执行过程中,会创建变量对象的一个作用域链。 闭包是javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包来实现。个人的理解是:函数中嵌套函数。 闭包的定义及其优缺点 闭包是指有权访问另一个函数作用域中的变量的...

    EasonTyler 评论0 收藏0

发表评论

0条评论

BakerJ

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<