起源

通过 Class 实现代码

class Demo {
  constructor() {
    this.num = 1
    this.init()
  }

  resize() {
    alert(this.num)
  }

  init() {
    window.addEventListener('resize', this.resize)
  }
}

new Demo()

代码执行后,缩放浏览器,此时弹窗显示 undefined

符合预期!!

通过 Vue 实现的代码

import Vue from 'vue'

new Vue({
  template: '<div></div>',
  data: {
    num: 1
  },
  methods: {
    resize() {
      alert(this.num)
    }
  },
  mounted() {
    window.addEventListener('resize', this.resize)
  }
}).$mount('#app')

缩放浏览器,此时弹框显示 1

不符合预期!!

代码分析

按常理,绑定事件 this.resize 后,将会丢失 this 所指向的上下文,所以第一个代码执行的结果是 undefined

因此猜想,在 Vue 的实现版本中,绑定是一定不是定义在 methods 下的 resize 方法。

源码分析

src/core/instance/state.js#L258

function initMethods (vm: Component, methods: Object) {
  const props = vm.$options.props
  for (const key in methods) {
    // ...
    vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
  }
}

可以看到在 Vue 实例上绑定的方法,都是被 bind 处理过的。

src/shared/util.js#L203

function polyfillBind (fn: Function, ctx: Object): Function {
  function boundFn (a) {
    const l = arguments.length
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)
        : fn.call(ctx, a)
      : fn.call(ctx)
  }

  boundFn._length = fn.length
  return boundFn
}

function nativeBind (fn: Function, ctx: Object): Function {
  return fn.bind(ctx)
}

export const bind = Function.prototype.bind
  ? nativeBind
  : polyfillBind

由此可见, Vue 的实例调用的方法,是经过 bind 后带有上下文的新方法。


siwuxie
528 声望67 粉丝

404