最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 史上最详细的this讲解

    正文概述 掘金(longhanghang)   2020-12-30   358

    this是什么

    this是一个指针,并且永远指向一个对象,对于非箭头函数来说,具体指向哪个对象是在运行时基于函数的执行环境动态绑定的。对于箭头函数来说,具体指向哪个对象是在箭头函数申明时的环境确定的。

    this的使用场景--非箭头函数

    this的常用使用场景无非就是下面几种

    1. 作为对象的方法调用
    2. 作为普通函数调用
    3. 作为构造函数调用
    4. 作为事件的回调函数调用
    5. Function.prototype.call和Function.prototype.apply调用

    1 作为对象的方法调用

    例子

    <script type = "text/javascript">
        // 作为对象的方法调用
        var userName = "wenyaoyao"
        var obj = {
            userName:"longhanghang",
            getName:function(){
                return this.userName;
            }
        }
        console.log(obj.getName());
        //输出 longhanghang
    </script>
    

    以上代码运行之后输出 longhanghang

    结论

    当函数作为对象方法被调用时,this指向该对象。

    衍生

    如果把对象里面的函数赋值给一个用var定义的全局变量,然后再在全局下面执行会产生什么样的火花呢?

    <script type = "text/javascript">
        // 作为对象的方法调用
        var userName = "wenyaoyao"
        var obj = {
            userName:"longhanghang",
            getName:function(){
                return this.userName;
            }
        }
        var getNameFun = obj.getName;
        console.log(getNameFun());
        //输出 wenyaoyao
    </script>
    

    以上代码运行之后输出 wenyaoyao

    刨析:因为此时getNameFun是在全局作用域下面执行的,所以this指向window。

    2 作为普通函数调用

    例子
    <script type="text/javascript">
      // 作为普通函数调用
      var userName = 'wenyaoyao';
      function getName() {
        console.log(this.userName);
      }
      getName();
      //输出 wenyaoyao
    </script>
    

    以上代码运行之后输出 wenyaoyao

    结论

    当函数直接被调用时this指向全局对象,游览器里面是window,nodejs里面是global

    衍生

    1 全局环境下使用var关键字定义的变量会直接挂靠到window上,如果是使用let和const呢?

    <script type="text/javascript">
      // 作为普通函数调用
      let userName = 'wenyaoyao';
      function getName() {
        console.log(this.userName);
      }
      getName();
      //输出 undefined
    </script>
    

    以上代码运行之后输出 undefined

    刨析: 可以发现let和const定义的变量并不会挂靠在window上,然后此时this指向的是window,所以打印的时候输出undefined。

    3 作为构造函数调用

    例子
    <script type="text/javascript">
      // 作为构造函数调用
    
      function Person() {
        this.userName = 'longhanghang';
        //return this;
      }
    
      let person1 = new Person();
      console.log(person1.userName);
    
      //输出 longhanghang
    </script>
    

    以上代码运行后输出longhanghang

    结论

    当使用new运算符调用函数时总是会返回一个对象,this指向这个对象。这也就是大名鼎鼎的构造函数。

    衍生

    1 函数内部怎么区分一个函数的调用类型呢?

    <script type="text/javascript">
      // 函数内判断调用类型
      let obj = {};
      function gudgeType() {
        if (this instanceof gudgeType) {
          console.log('构造函数调用');
        } else {
          console.log('其他方式调用');
        }
      }
    
      gudgeType();
      new gudgeType();
      // 输出其他方式调用
      // 构造函数调用
    </script>
    

    以上代码运行后依次输出 输出其他方式调用构造函数调用

    刨析: 我们可以巧妙的在函数内部使用instanceof对函数的调用方式进行判断。

    4 作为事件的回调函数使用

    例子
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>作为事件的回调函数使用</title>
      </head>
      <body>
          <div id = "test">点击我</div>
      </body>
      <script type="text/javascript">
        //作为事件的回调函数使用
        var testDom = document.querySelector("#test");
        test.onclick = function(e){
            console.log(this)
        }
      </script>
    </html>
    // 输出 <div id = "test">点击我</div>
    

    以上代码运行后输出 <div id = "test">点击我</div>

    结论

    作为事件的回调函数调用时,this指向绑定事件的dom对象。

    衍生

    如果在事件的回调函数里面咱们再定义一个函数,函数内部的this又会指向哪里呢?

    <!DOCTYPE html>
    <html lang="en">
     <head>
       <meta charset="UTF-8" />
       <meta name="viewport" content="width=device-width, initial-scale=1.0" />
       <title>作为事件的回调函数使用</title>
     </head>
     <body>
         <div id = "test">点击我</div>
     </body>
     <script type="text/javascript">
       //作为事件的回调函数使用
       var testDom = document.querySelector("#test");
       test.onclick = function(e){
           var text = function(){
               console.log(this);
           }
           text();
       }
     </script>
    </html>
    // 输出结果为window
    

    以上代码运行后输出 window

    刨析:此时的text是作为普通函数被调用的,所以指向windw。

    5. Function.prototype.call和Function.prototype.apply调用

    1 Function.prototype.call

    <script type="text/javascript">
     //Function.prototype.call()
    
     function getName(name, age) {
       console.log(this.name, name, age);
     }
     var obj = { name: 'longhanghang' };
     getName.call(obj,'wenyaoyao',11);
    
     //输出为 longhanghang wenyaoyao 11
    </script>
    

    1 Function.prototype.apply

    <script type="text/javascript">
    //Function.prototype.apply()
    
    function getName(name, age) {
      console.log(this.name, name, age);
    }
    var obj = { name: 'longhanghang' };
    getName.apply(obj,['wenyaoyao',11]);
    
    //输出为 longhanghang wenyaoyao 11
    </script>
    

    以上代码最终输出都为longhanghang wenyaoyao 11

    结论

    执行一个函数我们通常都是采用的后面加()的方式,例如有名为 getName 这样的一个函数,我们通常会 使用getName()进行调用,这样调用的方式this的指向会遵循我们前面说的四种情况,但是如果我们不这样调用,而使用callapply方法来调用时,那么this的指向取决于我们传的第一个参数,正如上面的示例,第一个参数我们传递的obj,那么函数内部的this指向的就是这个obj,后面的参数是函数执行时需要的参数,apply方法跟call方法作用差不多,只是后面的函数参数形式不同。

    衍生

    如果我们将函数作为对象的方法调用,然后再使用call或者apply时,会擦出怎么样的火花呢?

    <script type="text/javascript">
      //Function.prototype.apply()
    
      var obj1 = {
        name: 'longhanghang',
        getName: function () {
          console.log(this.name);
        },
      };
      var obj2 = { name: 'wenyaoyao' };
      obj1.getName.call(obj2);
    
      //输出为 wenyaoyao
    </script>
    

    以上代码输出为wenyaoyao

    刨析:当我们使用了call或者apply执行函数时,优先级会高于作为对象的方法调用

    this的使用场景--箭头函数

    1 作为对象的方法调用

    例子
    <script type="text/javascript">
      // 作为对象的方法调用
      var userName = 'wenyaoyao';
      var obj = {
        userName: 'longhanghang',
        getName: () => {
          return this.userName;
        },
      };
      console.log(obj.getName());
      //输出 wenyaoyao
    </script>
    

    以上代码运行之后输出 wenyaoyao

    结论

    this指向被申明时的环境,因为getName被申明时实在全局环境下的,所以这里的this指向window

    衍生1

    如果把对象里面的函数赋值给一个用var定义的全局变量,然后再在全局下面执行会产生什么样的火花呢?

    <script type="text/javascript">
      // 作为对象的方法调用
      var userName = 'wenyaoyao';
      var obj = {
        userName: 'longhanghang',
        getName: () => {
          return this.userName;
        },
      };
      var getNameFun = obj.getName;
      console.log(getNameFun());
      //输出 wenyaoyao
    </script>
    

    以上代码运行之后输出 wenyaoyao

    刨析:当箭头函数被申明的时候,this指向的是window

    衍生2

    如果箭头函数里面嵌套一个箭头函数呢?

    <script type="text/javascript">
      // 作为对象的方法调用
      var userName = 'wenyaoyao';
      var obj = {
        userName: 'longhanghang',
        getName: () => {
          var getFirstName = () => {
              return this.userName
          }
          return getFirstName;
        },
      };
      var getNameFun = obj.getName();
      console.log(getNameFun());
      //输出 wenyaoyao
    </script>
    

    以上代码输出结果为wenyaoyao

    刨析:当getFirstName申明的时候是在getName的这个作用域里面,this指向的就是getName指向的this,而getName在申明的时候this指向的是window,所以结论就是getName种的this也是指向window

    2 作为普通方法调用

    例子
    <script type = "text/javascript">
        let getName = () => {
            console.log(this)
        }
        getName();
    </script>
    //输出是window
    

    以上代码输出的结果是window

    结论

    this指向被申明时的环境,因为getName被申明时实在全局环境下的,所以这里的this指向window

    3 作为构造函数调用

    例子
    <script type = "text/javascript">
        var getName = () => {
            console.log(this)
        }
        var aa = new getName();
        //报错 Uncaught TypeError: getName is not a constructor
    </script>
    

    以上代码会报错 Uncaught TypeError: getName is not a constructor

    结论

    箭头函数不能作为构造函数调用。

    4 作为事件的回调函数调用

    例子
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>作为事件的回调函数使用</title>
      </head>
      <body>
          <div id = "test">点击我</div>
      </body>
      <script type="text/javascript">
        //作为事件的回调函数使用
        var testDom = document.querySelector("#test");
        test.onclick = () => {
            console.log(this)
        }
      </script>
      //输出为window
    </html>
    

    以上代码输出为window

    结论

    this指向被申明时的环境,因为回调函数在申明时的环境是window,所以this指向window

    5. Function.prototype.call和Function.prototype.apply调用

    例子
    <script type = "text/javascript">
        var getName = () => {
            console.log(this)
        }
        
        getName.call(Object)
        getName.apply(Object)
        // 输出为window
    </script>
    

    以上代码输出为 window

    结论

    Function.prototype.call和Function.prototype.apply无法改变箭头函数this的指向,this的指向还是在申明时就决定了。因为申明时的环境时window,所以this指向window而并不是指向构造函数Object

    箭头函数和非箭头函数的区别

    1. 箭头函数长得帅,非箭头函数长得漂亮
    2. 箭头函数都是匿名函数,非箭头函数可以是匿名函数也可以是具名函数
    3. 箭头函数不能使用new关键字,非箭头函数可以
    4. 箭头函数没有prototype属性,非箭头函数有
    5. 箭头函数不绑定arguments,非箭头函数要绑定
    6. 箭头函数无法使用callapply修改的this的指向,非箭头函数可以
    7. 箭头函数的this在申明时决定,非箭头函数的this在执行时确定

    自己实现call,apply,bind

    前面用了这么多call和apply,那么咱们来点干货,自己手写一下这几个函数

    1 call
       <script type="text/javascript">
      Function.prototype.selfCall = function () {
        var args = arguments; //获取到实际参数
        //类型判断
        if (Object.prototype.toString.call(this) !== '[object Function]') {
          throw new Error('必须使用函数调用该方法');
        }
        var realThis = Array.prototype.shift.call(args) || window; //截取出第一个参数也就是自定义this指向的这个对象
        var funName = Symbol('funName'); //定义一个Symbol类型的属性名
        realThis[funName] = this; //将方法赋值给对象的funName属性
        var res = realThis[funName](...args); // 将此方法作为传递进来的对象的方法调用,此时this已经指向我们传递进来的这个对象
        delete realThis[funName]; //删除这个对象上的临时属性
        return res;
      };
    
      function getName(aa) {
        console.log(this);
      }
      let obj = {};
      getName.selfCall(obj, 11);
    
      //输出obj这个对象
    </script>
    

    最终输出的是obj对象和11

    2 apply
    <script type="text/javascript">
      Function.prototype.selfCall = function () {
        //类型判断
        if (Object.prototype.toString.call(this) !== '[object Function]') {
          throw new Error('必须使用函数调用该方法');
        }
        var realThis = arguments[0] || window; //截取出第一个参数也就是自定义this指向的这个对象
        var args = arguments[1]; //取出剩下的参数作为函数运行的参数
        var funName = Symbol('funName'); //定义一个Symbol类型的属性名
        realThis[funName] = this; //将方法赋值给对象的funName属性
        var res = realThis[funName](...args); // 将此方法作为传递进来的对象的方法调用,此时this已经指向我们传递进来的这个对象
        delete realThis[funName]; //删除这个对象上的临时属性
        return res;
      };
    
      function getName(aa, bb) {
        console.log(this, aa, bb);
      }
      let obj = {};
      getName.selfCall(obj, [11, 22]);
    
      //输出obj这个对象和11
    </script>
    
    bind

    bindcall以及apply有一点区别就是函数调用bind后会返回一个新的this指向的函数,而使用callapply是改成this指向并执行

    <script type="text/javascript">
      Function.prototype.selfCall = function () {
        //类型判断
        if (Object.prototype.toString.call(this) !== '[object Function]') {
          throw new Error('必须使用函数调用该方法');
        }
        var args = arguments; //获取到参数
        var realThis = [].shift.call(args) || window; //截取出第一个参数也就是自定义this指向的这个对象
        var funName = Symbol('funName');//定义一个Symbol类型的属性名
        realThis[funName] = this;//将方法赋值给对象的funName属性
    
        //返回一个匿名函数,匿名函数中去执行我们要改变this指向的函数
        return function () {
          var diaoyongArgs = arguments;//获取到内层函数的参数
          let allArgs = [...args, ...diaoyongArgs];//合并参数
          let ret = realThis[funName](...allArgs);//将此方法作为传递进来的对象的方法调用,此时this已经指向我们传递进来的这个对象
          delete realThis[funName];//方法执行完后,删除这个对象上的临时属性
          return ret;
        };
      };
    
      function getName() {
        console.log(this, ...arguments);
      }
      let obj = {};
      getName.bind(obj, 11, 22)(33);
    
      //输出 {} 11 22 33
    </script>
    

    以上代码最终输出 {} 11 22 33

    总结

    史上最详细的this讲解


    起源地下载网 » 史上最详细的this讲解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元