• 函数调用模式


    这篇主要是记录下对一篇外文的阅读收获,原文链接

      函数调用模式其实以前也比较清楚,但是对于this这个东西总是吃不透,阅读完这篇文章后才豁然开朗。

      首先将函数的几种调用模式以及this的定义列出来,以便结合理解每种调用模式和this之间的关系。

      函数调用模式有以下几种:1)方法调用 2)函数调用 3)构造器调用 4)apply/call调用

      this的定义:this在js中是一个依赖于使用它的执行环境被解析的关键字,this的值是建立在当前函数被调用的上下文基础上的,取决于在哪里、怎么样调用函数。

      1)方法调用

    复制代码
    var obj = {
        value: 0,
        increment: function() {
            this.value+=1;
        }
    };
    
    obj.increment(); //Method invocation
    复制代码

      上面的代码中obj.increment();就是方法调用模式。此时increment函数的调用方式是"对象名.方法名",也就是在调用increment函数时显示的指向了它所属的对象,因此increment函数中的this就指向了obj对象。

      2)函数调用

    var value;
    function increment(){   this.value++; } increment();

      这是简单的函数调用,先定义一个函数,在通过圆括号调用。此时因为没有明确指定调用函数时的对象,因此increment函数中的this是指向了全局对象window。令人费解的是,这种情况在子函数中尽然也成立,请看下面代码:

    复制代码
    var value = 500; //Global variable
    var obj = {
        value: 0,
        increment: function() {
            this.value++;
    
            var innerFunction = function() {
                alert(this.value);
            }
    
            innerFunction(); //Function invocation pattern
        }
    }
    obj.increment(); //Method invocation pattern
    复制代码

      大家看看,obj.increment();调用完后,alert的值应该是多少呢?500,没错就是500。为什么???

      因为innerFunction()是函数调用模式,它里面的this指向的是全局对象window,因此this.value输出的应该是500。

      如何让它输出obj对象中的value值呢?修改成下面这样:

    复制代码
    var value = 500; //Global variable
    var obj = {
        value: 0,
        increment: function() {
            var that = this;
            that.value++;
    
            var innerFunction = function() {
                alert(that.value);
            }
    
            innerFunction(); //Function invocation pattern
        }
    }
    obj.increment();
    复制代码

      使用that缓存this对象,innerFunction中调用alert(that.value); 这样输出的结果就是1了。这样之所以可以输出1,是应为js中函数都是闭包(this works because functions in JavaScript are closures)。这句话很费解,解释下:广义上讲,js中的所有子函数都是闭包。我们一般的函数是定义在全局对象中的,因此一般定义的函数其实是全局对象的子函数,因此也是个闭包。这可以解释原文中的那句话,但是最好不要这样理解闭包。

      闭包的定义:当你在内嵌函数中使用外部函数作用域内的变量时,就是使用了闭包。用一个常用的类比来解释闭包和类(Class)的关系:类是带函数的数据,闭包是带数据的函数。

      根据上面的对闭包的定义,第二段代码中子函数innerFunction调用了父函数的变量that.value,因此形成了闭包。通过函数调用模式调用innerFunction时,它会在定义它的上下文环境中找that.value这个变量,首先找到的就是obj对象中的value值,因此输出是1.

      3)构造器调用

      这种调用比较好理解,和面向对象语言中的调用方式基本相同。

    复制代码
    function obj(){
          this.arr=[1,2];  
    }
    
    obj.prototype={
          push:function(a){
             this.arr.push(a);
         },
          length:function(){
            alert(this.arr.length);
        }
    }
    var o1=new obj();
    o1.push(2);
    复制代码

      需要注意一点是,之所以将arr放在函数体(构造器)obj中定义,是因为构造器中定义的对象是属于每个实例的,而原型prototype中定义的对象或者方法是所有实例共享的。

      4)apply/call调用

      这种调用方式也比较简单,两种方法只是传参不一样,其他都一样。作用都是显示的改变调用函数运行的上下文环境。

    复制代码
    var add = function(num1, num2) {
            return num1+num2;
    }
    
    array = [3,4];
    add.apply(null,array); //7
    复制代码

      也是需要注意一点,apply第一个参数传递为null,并不是说将add函数的运行上下文环境设置为null,而是设置为全局对象window。

  • 相关阅读:
    php 0916
    心里话
    LibreOJ 6559 小奇采药
    计蒜客 T1658 热浪
    计蒜客 T2021 飞扬的小鸟
    POJ 1276 Cash Machine
    SCU 3366 Watering Hole
    POJ 3268 Silver Cow Party
    luogu P4568 [JLOI2011]飞行路线
    POJ 1179 Polygon
  • 原文地址:https://www.cnblogs.com/yzadd/p/6495226.html
Copyright © 2020-2023  润新知