Vue中调用document.addEventListener添加事件回调函数时,在回调函数中调用this对象时,通过调试发现,this指向的是之前调用document.addEventListenerVue对象或者Vue组件示例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>
</head>
<body>
<div id="app">
    <div style="width: 100px;height: 100px;background-color: #4CAF50" @mousedown="mouseDown($event)"></div>
</div>

</body>
<script>
    new Vue({
        el: "#app",
        methods: {
            mouseDown(event) {
                document.addEventListener("mousemove", this.onmousemove);
            },
            onmousemove(event) {
                console.log(this);

            }
        }
    });
</script>
</html>

通过调试打印的日志:
在这里插入图片描述
但是如果将onmousemove写在全局作用域呢?示例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>
</head>
<body>
<div id="app">
    <div style="width: 100px;height: 100px;background-color: #4CAF50" @mousedown="mouseDown($event)"></div>
</div>

</body>
<script>
    function  onmousemove(event) {
        console.log(this);

    }
    new Vue({
        el: "#app",
        methods: {
            mouseDown(event) {
                document.addEventListener("mousemove", onmousemove);
            },
        }
    });
</script>
</html>

打印结果如下:
在这里插入图片描述
发现this指向变成了doucument,即调用addEventListener的对象。
为什么addEventListener绑定的回调方法是Vue对象内部的methods方法时,this指向的对象就是当前Vue对象呢,通过查看源码发现Vue在初始化methods对象时,会将methods里面的每个函数绑定到当前的Vue对象,代码如下:

function initMethods (vm, methods) {
    var props = vm.$options.props;
    for (var key in methods) {
      {
        if (typeof methods[key] !== 'function') {
          warn(
              "Method \"" + key + "\" has type \"" + (typeof methods[key]) + "\" in the component definition. " +
              "Did you reference the function correctly?",
              vm
          );
        }
        if (props && hasOwn(props, key)) {
          warn(
              ("Method \"" + key + "\" has already been defined as a prop."),
              vm
          );
        }
        if ((key in vm) && isReserved(key)) {
          warn(
              "Method \"" + key + "\" conflicts with an existing Vue instance method. " +
              "Avoid defining component methods that start with _ or $."
          );
        }
      }
      vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);
    }
  }

上面最后一行的代码中调用的bind函数就是实现此功能的,代码如下:

var bind = Function.prototype.bind
      ? nativeBind
      : polyfillBind;

如果当前在Functionprototype上存在bind属性,也就是函数支持bind方法绑定新的作用域,就直接调用bind将当前vue对象绑定到函数的作用域上,如果不支持就用applycall来代替实现,通过bind创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind 的第一个参数,即传递的Vue对象,其中nativeBindpolyfillBind代码如下:

function nativeBind(fn, ctx) {
        return fn.bind(ctx)
}
function polyfillBind(fn, ctx) {
        function boundFn(a) {
            var l = arguments.length;
            return l
                ? l > 1
                    ? fn.apply(ctx, arguments)
                    : fn.call(ctx, a)
                : fn.call(ctx)
        }

        boundFn._length = fn.length;
        return boundFn
}

上面的效果和下面一样:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>
</head>
<body>
<div id="app">
    <div style="width: 100px;height: 100px;background-color: #4CAF50" @mousedown="mouseDown($event)"></div>
</div>

</body>
<script>
    function onmousemove(event) {
        console.log(this);

    }

    let v=new Vue({
        el: "#app",
        methods: {
            mouseDown(event) {
                document.addEventListener("mousemove", onmousemove.bind(v));
            },
        }
    });


</script>
</html>

在这里插入图片描述

Logo

前往低代码交流专区

更多推荐