• Javascript中函数调用和this的关系


    例子先行:
    var myObject={
        foo:"bar",
        func:function(){
            var self=this;
            console.log("outerfunc:this.foo="+this.foo);
            console.log("outerfunc:self.foo="+self.foo);
            (function(){
                console.log("innerfunc:this.foo="+this.foo);
                console.log("innerfunc:self.foo="+self.foo);
            }());
        }
    }
    myObject.func();
    

    输出结果为:

    对于上面的结果,第一个和第二个我是不意外的,第三和第四个竟然不知道为什么,虽然之前总结了作用域和闭包,但是关于this的问题还是搞不清楚,所以准备写一篇总结来强化一下自己的这些基本概念。

     

    一、函数调用的形式

    就像孔乙己回字有四种写法,javascript中的函数调用同样也有四种方式,分别如下:

    1.作为一个函数进行的调用

    2.作为一个对象的方法进行的调用

    3.作为构造器进行的调用

    4.通过apply()call()函数进行的调用

     

    在进行C#或者java编程的时候,我们都知道如果函数在声明的时候有定义了参数,那么函数再被调用的时候,也要相应的传入参数,否则不能正确使用,javascript和上面一些高级语言不一样的是,它的参数数目可以和声明时候不一样,参数不够undefined代替,参数多了那么就会截断。

     

    有趣的是:javascript中所有的函数调用都会传递两个隐式参数:argumentsthis

    arguments 参数不是我想要详细说的,所以这个就不展开学习了,它的特点是有length属性、能够被遍历、有点像数组,但是却不是数组。

    this 参数也挺有意思,在四种调用中,传入的this还不一样

     

    二、函数调用过程中传入的this

    Javascript中的thisC#java中的还是有区别的,C#this代表的意义可能是实例本身,而javascript只用函数作为方法的时候才和这个代表的意义差不多,其他三种可能会不一样,javascript中的this依赖于函数的调用,而C#等则是依赖于函数的声明,this也称为函数的上下文

    1)作为函数进行调用

    function sum(){
    alert(this);
    return1+2+3;
    }
    
    alert(sum());

    结果:

    上面的代码就是最常用函数调用方式,这种方式就是作为函数进行的调用,从上图还可以看到函数内部的this参数就是全局的对象window对象。

    2)作为对象的方法进行调用

    var obj={
      name:"大橙子"
    };
    
    function sum(){
      alert(this);
      alert(this.name);
      return 1+2+3;
    
    }
    
    obj.func=sum;
    obj.func();

    结果:

    和函数方式调用不同的是,这种调用方式传递的this参数就是这个对象本身,这就和C#当中一个方法所属的对象在该方法体内部可以用this形式进行引用差不多。

    上面的代码也说明了这点,正因为thissum中被当作了obj来使用,才能打印出name这个属性

     

    3)作为构造器进行调用

    对于构造器,要先理解javascript中的new操作是干嘛的.之前总结一篇关于原型的文章,里面的配图也提到了constructor这个东西,但是没有深入研究,正好这次把这部分补全。

    先看如下的例子:

    var person = function(){

    this.name = "大橙子";

    this.age = 26;

    this.say = function(){

    return "Hello!";

    }

    }

    var p = new person();

    console.log(p.name);

    console.log(p.age);

    console.log(p.say());

    结果:

    [Web浏览器] "大橙子"       

    [Web浏览器] "26"       

    [Web浏览器] "Hello!"   

     

    上面是一个简单的使用构造器进行实例化对象的例子,就像上次原型的文章中所画的图一样,这个过程也可以这样做:

    这个过程就是用new创建一个实例的过程,new的过程是这样的:

    1)新建一个对象p=new Object();

    2)设置原型链p.__proto__=person.prototype;

    3)让person中的this指向p,执行person的函数体。

    4)判断person的返回值类型:

    如果是值类型,就丢弃它,还是返回p

    如果是引用类型,就返回这个引用类型的对象,替换掉p

     

    对于(3)和(4)不是很好理解:

    3)的理解是:就像例子中的代码,我虽然在person中写了this,但是不调用person,我就不知道this是谁,所以new的第三步帮我做了这个,让this指向了p,执行此时执行person函数体的时候,就相当于使用p这个对象,this.name就好比p.name……

    4)理解:

      我这个例子的当中,person函数体内没有返回值,所以返回的是undefinedundefined是值类型,所以就舍弃了,返回p

      如果返回值写成了 return this因为第三步thisp的引用,所以这样写也是返回的p

      如果是其他的引用类型,就用其他来代替p返回。

    上面的过程也就是使用构造器的方式来调用函数。

     

    4)使用apply()call()来进行调用

    上面的三种方式在进行使用的时候,可以说他们的this都是被固定化了的,window对象、调用对象,或者新创建的对象实例,但是如果想要自由指定函数的上下文,就要使用apply()或者call()函数。

    例如:

    function sum(){

    var result = 0;

    for(var n=0;n<arguments.length;n++){

    result += arguments[n];

    }

    this.result = result;

    }

    var obj1 ={};

    var obj2 ={};

    sum.apply(obj1,[1,2,3]);

    sum.call(obj2,4,5,6);

    console.log(obj1.result);

    console.log(obj2.result);

    结果:

    [Web浏览器] "6"        

    [Web浏览器] "15"       

     

    这个例子当作我们可以看到,作为sum函数的第一个参数的obj1obj2,分别被当成了sum函数内的this上下文。

    apply()call()的区别在于,一个接受参数的数组,另一个是分离开的。

    以后可以使用这样的方式,指定this上下文,比较灵活。

     

    再看new操作
    var  p = new person( );
    就相当于:
    var p  = {};
    person.apply(p);
    p.__proto__ = person.prototype;

     

     

    总结:函数的调用以及它和this上下文的关系,可以简单描述为如下:

    作为函数调用,this相当于window

    作为对象的方法,this相当于对象

    作为构造器调用,this相当于实例化的对象

    apply()call()调用,this可以进行指定。

     

    回头再看看上面的例子:

    调用函数方式是作为对象的方法,所以第一个和第二个输出的myObjectfoo,也就是bar

    第三个

    (function(){
    console.log("innerfunc:this.foo="+this.foo);
    console.log("innerfunc:self.foo="+self.foo);
    }());

    关于这个为什么输出的是undefined,原因是this在这里是window对象的引用,为什么可以看看下面的连接

    https://www.zhihu.com/question/21958425

    第四个:

    上面是一个自调用函数,我的理解是首先javascript的作用域是函数级别的,所以上面的自调用函数和外层function不是一个作用域,但是他们在一条链上,所以第四个self.foo的时候,因为自身的作用域内没有self这个对象,就向上找,上层有,于是就输出上层的self.foo也就是bar。

  • 相关阅读:
    高精度减法
    HDU 4460 Friend Chains
    POJ 2386 Lake Counting
    POJ 1852 Ants
    HDU 1240 Asteroids!
    SQL注入之Sqli-labs系列第三十六关(基于宽字符逃逸GET注入)和三十七关(基于宽字节逃逸的POST注入)
    SQL注入之Sqli-labs系列第三十四关(基于宽字符逃逸POST注入)和三十五关
    SQL注入之Sqli-labs系列第三十三关(基于宽字符逃逸注入)
    SQL注入之Sqli-labs系列第三十关(基于WAF防护的双引号报错注入)和三十一关
    墨者-uWSGI 漏洞复现(CVE-2018-7490)
  • 原文地址:https://www.cnblogs.com/dcz2015/p/5428043.html
Copyright © 2020-2023  润新知