this 指向问题

JavaScript 的 this 原理      参考:http://www.ruanyifeng.com/blog/2018/06/javascript-this.html  或
                https://juejin.cn/post/7144332319279546404?share_token=6df617ba-1243-416d-a5c7-c7ad8b797035 

this 指向

  • 全局作用域下的this指向window
  • 如果给元素的事件行为绑定函数,那么函数中的this指向当前被绑定的那个元素
  • 函数中的this,要看函数执行前有没有..的话,点前面是谁,this就指向谁,如果没有点,指向window
  • 自执行函数中的this永远指向window
  • 定时器中函数的this指向window
  • 构造函数中的this指向当前的实例
  • call、apply、bind可以改变函数的this指向
  • 箭头函数中没有this,如果输出this,就会输出箭头函数定义时所在的作用域中的this。
    注意:箭头函数 中 的 this,有可能是需要运行时才能决定。https://www.jianshu.com/p/985a0ee0bbd7
              箭头函数中的this  虽然是 定义时 所在的 作用域的this,但是 如果 this 定义时所在的作用域 的 this 是运行时改变决定,则 箭头函数的 this 也就同样 需要运行时决定。
    function foo(){
      setTimeout(() => {
        console.log("id:", this.id)
      }, 100);
    }
    
    foo.call({ id:42 });

    如上,箭头函数所在 作用域 this 是可以改变的。那么对应 箭头函数 的this 也会改变。

   总结:

    • 一个对象是不会产生新的作用域的,所以对象下属性直接使用this,这个this是对象外的作用域下的this。如下
      var catName = "ketty"
      var obj = {
        catName: "obj 的 ketty",
        newCat: this.catName    // ketty,这里的 this就是obj外面环境的this,obj不是函数 本身 无法产生 作用域,也就不会改变this的值。
      }

      这点理解在 vue 中是很重要的,因为vue中常用this指向当前组件实例。没理解透,容易出错。

    • 能产生作用域的环境,才有可能内部隐式创建新的this;产生新作用域环境的,不一定会隐式创建this变量,如 箭头函数。

 闭包函数(返回函数内)的this指向外围作用域的this(一般是Windows,当然bind等可以改变外网环境的this)

  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());  //the Window。  因为返回的闭包函数,再执行时 就变成一个普通函数的执行了。所以this 就是window

 

以下除了匿名函数

1、对象调用函数的时候,函数里面会有一个隐式的赋值操作   var  this = 调用的对象 (没有调用对象,即执行函数 fun(),其实就是window调用,结果还是window),其它的如何情况都不会存在隐式的操作  this 的值。

//对象方法调用
var person = {
    run: function () {console.log(this)}
}
person.run() // person

  https://blog.csdn.net/qq_33988065/article/details/68957806

2、(这个太平常了,不用特别去套理论,构造函数就是这样用的)new一个对象时,同样可以看做是对象调用构造函数(事实上构造函数确实执行了),构造函数里面隐式的执行了   var  this = 调用的对象 。

3、对象调用函数的时候,函数内才会出现 this改变的情况,即隐式的出现  var  this = 调用的对象。其它情况 this 都不操作,里面的this 都是作用域链上游的  this值;

  函数调用函数,不会出现var this重新赋值的情况,如下面的多层函数调用。只有第一个函数是window对象调用  fun()函数,fun函数先进行隐式操作 var this = window,

  fun() 里面的函数没有对象调用,只是函数调用函数(执行函数),所以fun2函数的 this 根据作用域链就近原则,最近的this,就是fun函数里面的this = window。

    function fun2() {
        console.log(this);
    }
    function fun1(fun2) {
        fun2();
    }
    function fun(fun1) {
        fun1(fun2);
    }
    
    fun(fun1);

 this的指向在 调用(即运行) 的时候确定,全局下的函数中的this不一定指向的是window,看谁在调用它。所以所以引起混淆。(这点和变量的作用域,变量的作用域是 声明的时候确定的,而不是运行时)

如:下面init函数没有人调用,通过赋值,把init函数赋值给了obj.fun函数,最后是obj在调用fun函数,init函数并没有直接执行。

    function init() {
        console.log(this.name);
    }

    function test(){
        var obj= {};
        obj.name = "中国"
        obj.fun = window.init;
        obj.fun();
    }
    test();  //  中国    // 这里this指向obj

感悟:vue.js中出现这种情况,分析出上面的这种的this指向。所以在实际项目中框架(或库、插件)中的参数调用外部的函数,外部函数千万不要带this

    因为我们不知道框架内是怎么实现的,这个this被指向哪个对象了。如果内部的this指向很明确就没关系了,如

下面的this很明确的指向obj2,init 函数到了其它函数里面,不管怎么变,init内的结构不会变,this指向就不会改变。
  function init() {
        var obj2 = {
            name : "小李",
            fun2 : function () {
                console.log(this.name);
            }
        }
        obj2.fun2();
    }
View Code

 

总结(个人):   this指向问题可以把他看做是一个变量,利用js作用域链就可以很好确定 this指向谁了。


箭头函数的this指向

1、剪头函数this的指向是定义时this的指向,而不是调用时this的指向。(这个和 es5 的this指向是不一样的

 

手动绑定 this 的指向:https://www.jianshu.com/p/87e3f586f35f

1、this 都是在函数内出现(全局上不需要this,就是window),函数本身就是一个对象。所有的函数的原型上都有,绑定this的方法。

  a、call, apply (是改变this指向,立即执行的。)

  b、bind  (返回了一个新的函数,这是创建了一个新函数,并没有改变原来的函数)

  总结:可以这样理解,call, apply绑定的函数,在执行时,先执行了 this = 绑定的object。而 bind 函数是创建了一个新的函数,这个新的函数第一行就是 this = 绑定的object。两者都不会导致原函数的变化。

 


 碰到的不合理问题:

  • 自定义检验规则 箭头函数 中使用的this指向了第一个参数?https://www.jianshu.com/p/148f3f324a17(这里用的是普通函数,是可能的)
    情景:在杭州银行虚拟机中开发,使用 ant-design-vue 的 from 表单的自定义 校验,用的是 箭头函数。箭头函数 里面的 this 按理来说 应该是 vue组件对象,但 实际上却是 第一个参数 rule 对象。简化 如下代码
    export default {
      data() {
        let validatePass2 = (rule, value, callback) => {
            console.log(this)
            callback();
        };
        return {
          rules: {
            checkPass: [{ validator: validatePass2, trigger: 'change' }]
          }
        };
      },

    网上找了很多资料,也没有找到原因。
    最后猜测 可能的 结果是:webpack 编译  .vue 文件 后导致的问题,编译后的代码并不是完全符合 源码的逻辑。webpack 编译后就是一个黑盒子,我们不知道具体处理了什么。
            可能1:源码中 这个箭头函数 定义的 地方 可能data()里,但是webpack编译后,定义的位置可能发生改变了;
            可能2:源码中 箭头函数 可能 被编译成 普通函数(语言降级了)。

     

 

posted @ 2018-06-08 13:46  吴飞ff  阅读(428)  评论(0编辑  收藏  举报