美文网首页
函数调用模式的区别-this

函数调用模式的区别-this

作者: YM雨蒙 | 来源:发表于2017-09-11 21:16 被阅读20次

    前言

    学习js也快有一段时间了,一直不怎么想碰this,也碰到别人写的一些关于this,一直懒得看,但是最近在学习DOM事件,写代码中经常看见this,有时候理解的不太透彻,所以决定把它研究一下,当然了,现在写的this也只是我现在的水平和参考一下大神的文章,自己结合起来,写给自己的

    正文:

    引自 MDN this

    • 与其他语言相比,函数的 this关键字在JavaScript中的表现略有不同,此外,在严格模式非严格模式之间也会有一些差别。
    • 在绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。ES5引入了bind方法来设置函数的this值,而不用考虑函数如何被调用的,ES2015引入了支持this词法解析的箭头函数(它在闭合的执行上下文内设置this
      的值)。

    什么是this?

    this是在执行上下文创建时确定的一个在执行过程中不可更改的变量。

    • 所谓执行上下文,就是JavaScript引擎在执行一段代码之前将代码内部会用到的一些变量、函数、this提前声明然后保存在变量对象中的过程。这个’代码片段’包括:全局代码(script标签内部的代码)、函数内部代码eval内部代码。而我们所熟知的作用域链也会在保存在这里,以一个类数组的形式存储在对应函数的[[Scopes]]属性中。

    正文说:严格模式与飞非严格模式会有区别,我们来看一下:

    看一下严格模式下

    var a = 1;
    function fun() {
       'use strict';
        var a = 2;
        return this.a;
    }
    fun();  //报错: Cannot read property 'a' of undefined
    

    非严格模式下:

    var a = 1;
    function fun() {
        var a = 2;
        return this.a;
    }
    fun();  // 输出 1
    

    总结: 当函数独立调用的时候,在严格模式下它的this指向undefined,在非严格模式下,当this指向undefined的时候,自动指向全局对象(浏览器中就是window)


    正文说:绝大多数情况下,函数的调用方式决定了this的值

    我们来看看函数有几种调用方式

    • 函数调用模式: 函数名()
    • 方法调用模式
    • 构造函数调用模式 new Function(...)
    • apply(call)调用模式

    函数调用模式

    //例子:
    function add(i,j){
      console.log(this);  //Window
      // console.log(arguments);
      var sum = i+j;
      console.log(sum);
      (function(){
        console.log(this);  //Window
      })()
      return sum;
    }
    add(1,2);
    

    从上面的例子我们看到this两次返回的都是window全局对象,匿名函数也会产生本地作用域,打印也是window按理说第一次打印应该是undefined,但是浏览器里有一条规则:

    如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)

    总结:函数调用模式this指向window的全局对象

    方法调用模式

    //例子:
    var myNumber = {
      value: 1,
      add: function(i){
        console.log(this);  //Object{value: 1, add: ƒ}
        this.value += i;
      }
    }
    myNumber.add(1);
    

    函数实现改变value的值加1,add被调用时,this的指向是Object对象

    总结:方法调用模式this指向调用者

    构造函数调用模式

    //例子:
    function Car(type,color){
      this.type = type;
      this.color = color;
      this.status = "stop";
      this.light = "off";
      console.log(this);  //打印结果是我们创建的对象
    }
    Car.prototype.start = function(){
      this.status = "driving";
      this.light = "on";
      console.log(this.type + " is " + this.status);
    }
    Car.prototype.stop = function(){
      this.status = "stop";
      this.light = "off";
      console.log(this.type + " is " + this.status);
    }
    var benz = new Car("benz", "black");  //Car {type: "benz", color: "black", status: "stop", light: "off"}
    

    如果函数作为构造函数用,那么其中的this就代表它即将new出来的对象。为啥呢?new做了啥呢?new做了下面这些事:

    • 创建一个临时对象
    • 给临时对象绑定原型
    • 给临时对象对应属性赋值
    • 将临时对象return

    也就是说new其实就是个语法糖,this之所以指向临时对象还是没逃脱上面说的几种情况。
    总结:构造函数调用模式this指向被构造的对象

    apply(call)调用模式

    • this指向第一个参数

    对于这一个,由于自己还没学到,先借鉴MDN上的,后面自己再总结一下

    如果要想把this的值从一个context传到另一个,就要用call,或者apply
    方法。

    // 一个对象可以作为call和apply的第一个参数,并且this会被绑定到这个对象。
    var obj = {a: 'Custom'};
    
    // 这个属性是在global对象定义的。
    var a = 'Global';
    
    function whatsThis(arg) {
      return this.a;  // this的值取决于函数的调用方式
    }
    
    whatsThis();          // 直接调用,      返回'Global'
    whatsThis.call(obj);  // 通过call调用,  返回'Custom'
    whatsThis.apply(obj); // 通过apply调用 ,返回'Custom'
    

    当一个函数的函数体中使用了this关键字时,通过call()方法和apply()方法调用,this的值可以绑定到一个指定的对象上。call()和apply()的所有函数都继承自Function.prototype 。

    function add(c, d) {
      return this.a + this.b + c + d;
    }
    
    var o = {a: 1, b: 3};
    
    // 第一个参数是作为‘this’使用的对象
    // 后续参数作为参数传递给函数调用
    add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
    
    // 第一个参数也是作为‘this’使用的对象
    // 第二个参数是一个数组,数组里的元素用作函数调用中的参数
    add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
    

    使用 call 和 apply 函数的时候要注意,如果传递的 this 值不是一个对象,JavaScript 将会尝试使用内部 ToObject 操作将其转换为对象。因此,如果传递的值是一个原始值比如 7 或 'foo' ,那么就会使用相关构造函数将它转换为对象,所以原始值 7 通过new Number(7)被转换为对象,而字符串'foo'使用 new String('foo') 转化为对象,例如:

    function bar() {
      console.log(Object.prototype.toString.call(this));
    }
    
    //原始值 7 被隐式转换为对象
    bar.call(7); // [object Number]
    

    参考资料

    相关文章

      网友评论

          本文标题:函数调用模式的区别-this

          本文链接:https://www.haomeiwen.com/subject/rvnfsxtx.html