• javascript中this对象之 —— 意乱情迷


      javascript函数中的this对象和其他语言比较起来很很大不同,甚至在严格模式和非严格模式下都有不同。

      大多数情况下,this对象是有函数的调用对象决定。在任务执行过程中,this对象不能被修改。ECMAScript 5引入了bind方法来设置调用中的this对象,实际上就是传递上下文,下次有时间,可以具体讨论下bind方法,很多框架都已经实现了,也就是说早就有了实际标准 —— 先上车后买票。

      就这么些颇为空洞,下面就使用几个例子

    function fn(){
        return this;
    }
    
    alert(fn() === window);  //true

      这个例子,结果和原因都比较明显,fn()相当于全局对象window在调用,等同于window.fn(),因而this对象指向window对象。

    var x = 0;
    function fn(){
        alert(this.x);
    }
    var obj = {
        x: 1,
        f: fn,
    }
    obj.f();  //1

     这个例子注意,obj对象的f属性指向fn函数体,所以最后的obj.f()调用,就是对象obj在调用函数,因此套用规则,this.x显示1。

    var x = 0;
    function test(){
        return function(){
            console.log(this.x);
        };
    }
    var o = {x:1};
    o.f = test;
    o.f()();  //0

      那再看这个例子,最终显示结果为0,这是为何呢?通俗点讲,对象o的属性f是test函数的引用,o.f()得到的结果是function(){console.log(this.x);},单独执行这个函数,this对象当然指向全局对象window,因而结果为0。这里就简单地理解this对象,不要去看太复杂的例子,记住:万变不离其宗,this对象指向调用包裹它本身的函数的对象。

      另外还需要注意的是,使用一个变量保存this对象,可以改变上下文。柯里化的函数绑定是比较典型的传递上下文技术。在这里就不多讲,下次跟函数绑定一起谈谈。

      最后总结一下,this的处理机制分为五种不同的情况:

      ①、全局范围,指向全局对象;

      ②、函数直接调用,同样指向全局对象,例如:func();    -- ES5严格模式下,会是undefined,因为不存在全局变量

      ③、方法调用,指向调用方法的对象,例如:fn.func(); this指向fn对象;

      ④、调用构造函数,例如:new func();这里this指向新创建的对象;

      ⑤、传递上下文,例如:apply(context, args)或者call(context, arg1, arg2...),this指向context

      还有一种特别容易误解地方,内部函数,即声明在另外一个函数体内的函数,如下所示。

    Fn.method = function() {
        function func() {
            // this 将会被设置为全局对象
        }
        func();
    }

      大多数初学者很容易误解,代码当中的this指向Fn对象,其实指向window全局对象;若真需要使用Fn对象,可以在method方法内创建一个变量来引用Fn对象,通常情况下,开发人员习惯使用that或者self替换,约定俗成。

    Fn.method = function() {
        var self= this;
        function func() {
            // 使用 self来指向 Fn 对象
        }
        func();
    }

      通过上面的通篇描述,相信大家都能很好地理解this对象的工作机制了。若大家觉得都掌握了要点,那么在此基础上可以适当地拓展下。若到此还是云里雾里的,推荐你详读下这篇文章:http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/
      

      函数的执行上下文(context)

      一个函数被调用,或者直接执行,都会创建一个执行上下文(execution context),函数所有的行为都发生在此执行上下文中。要构建执行上下文,javascript首先会创建arguments变量,包含调用函数时传入的参数集合。接着会创建作用域链(scope chain),然后初始化变量,包括函数的形参(parameter)。如果函数内部有函数,则接着初始化这些内部函数;如果没有内部函数,则继续将局部变量初始为undefined,真正地赋值操作在执行上下文创建完成之后,函数执行之时才会赋值。最后给this对象赋值。

      执行上下文创建完成之后,函数会逐行执行,所需的变量都会从已经构建好的执行上下文中读取。这也是创建上下文的目的和意义之一。

      函数绑定

      另一个能改变this对象的知识扩展点,函数绑定:指创建一个函数,在特定的上下文中指定参数调用另一个函数。该技巧经常跟回调函数和事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行上下文。

    var x = 9; 
    var obj= {
      x: 81,
      getX: function(y) {console.log('x: ' + this.x + ', y: ' + y);}
    };
    obj.getX(); //结果为x: 81, y: undefined
    var fn = obj.getX;
    fn();  //结果为x: 9, y: undefined

      上述结果的原因相信大家都知道,fn()执行时,并没有保存obj上下文,实际上它是在全局上下文中执行,因而得到的结果为9。但是,我们可以通过函数绑定技巧来传递上下文改变这样的结果,以达到我们的预期。

    function bind(func, context){
        var args = Array.prototype.slice.call(arguments, 2);  //两次调用,args都为空,因为bind(obj.getX, obj),并未传递第三个参数
        return function(){
            var innerArgs = Array.prototype.slice.call(arguments);  //fn(88)innerArgs为88,fn()为空
            var finalArgs = args.concat(innerArgs);
            return func.apply(context, finalArgs);
        };
    }
    var fn = bind(obj.getX, obj);  //传递了函数和上下文
    fn();    //结果为x: 81, y: undefined
    fn(88);  //结果为x: 81, y: 88

      bind函数使用了两种技巧:①、函数绑定;②、参数柯里化。bind函数传递了两个参数,一个为被调用的函数,一个为期望的上下文。接下来看看函数体做了哪些事情?整体上看去bind函数用到了闭包,返回了一个函数,并且在返回的函数体内访问了外面的变量args。变量args是用来保存了bind函数传递进来的实参集合,并且从第三位开始获取,去掉func、context的实际参数值;返回的函数体内,innerArgs用来保存返回函数的实际参数,也就是例子中的fn()的参数;接着将args参数和调用的innerArgs拼接成一个新数组,最后返回func在context上下文中执行的结果。

      绑定函数能提供强大的动态函数创建功能,但不能滥用,毕竟每个函数都会带来额外的开销。

  • 相关阅读:
    第二次作业
    第一次作业
    2019春总结作业
    2019春第四次课程设计报告
    2019春第三次课程设计报告
    2019春第二次课程设计报告
    2019春第一次课程设计报告
    第十二周作业
    2019第十一周作业
    2019第十周作业
  • 原文地址:https://www.cnblogs.com/moltboy/p/3043835.html
Copyright © 2020-2023  润新知