我们知道 Vue 中传入的对象有一个属性对象methods
,其中的属性方法会被绑定到 Vue 实例上,可以通过this
访问到,那么我们这一篇文章就来实现一下方法的绑定
【注:本节所有代码都是在上一节代码的基础上增加的,所以上一节的代码将会省略】
首先确定我们预期的使用方式:
var l = new Lue({
methods: {
init: function () {
console.log(this)
}
},
ready: function () {
// 与上一节相同,先通过一个 $methods 作为所有方法被绑定到的对象
this.$methods.init()
}
})
根据上面的代码,我们大致可以推断出绑定方法的代码:
function Lue () {
this.$methods = {}
if (typeof opts.methods === 'object') {
for (var key in opts.methods) {
if (typeof opts.methods[key] === 'function') {
this.$methods[key] = opts.methods[key]
}
}
}
if (typeof opts.ready === 'function') {
opts.ready.call(this)
}
}
执行后控制台输出一个包含init
方法的对象,即我们传入的外部对象的methods
属性,而不是我们期望的l
实例。这是为什么呢?我们在 前端面试题——call与apply方法的异同这一节中已经剖析过了this
的指向问题——this永远指向该方法被持有的对象,即谁(哪个对象)持有这个方法,this
就指向这个对象。所以在这里打印出来的是methods
对象,而不是我们所期望的l
那我们该怎么做呢?很简单,用call
和apply
方法改变this
的指向
但是这里又有一个问题:
通过call
和apply
改变this
的函数会被立即执行
所以在处理方法的绑定时,我们需要延迟绑定,即在该方法需要被执行时,再绑定到l
上,但是又要能够通过this.$methods
访问到这个方法
是不是有点绕?没关系,直接上代码!
function Lue () {
this.$methods = {}
if (typeof opts.methods === 'object') {
this._bindMethods(opts.methods)
}
// 兼容当 methods 属性为方法,并且返回一个对象时的情况
if (typeof opts.methods === 'function' && typeof opts.methods() === 'object') {
this._bindMethods(opts.methods())
}
if (typeof opts.ready === 'function') {
opts.ready.call(this)
}
}
// 在 Lue 原型链上添加方法 _bindMethods
Lue.prototype._bindMethods = function (obj) {
var self = this
for (var key in obj) {
var val = obj[key]
if (typeof val === 'function') {
// 放该属性为方法时,在 $methods 上创建一个同名方法
// 这个方法的作用是改变传入的参数 $methods 属性上的对应方法的执行环境并执行
// 两个连续的三目运算的作用是:当该方法无参数时,直接通过 call 改变执行环境并执行;当存在 1 个参数时,通过 call 方法并传入这个参数 arg;当参数多于 1 个时,通过 apply 方法并传入整个参数列表
this.$methods[key] = function (arg) {
var length = arguments.length
length ? length > 1 ? val.apply(self, arguments) : val.call(self, arg)
: val.call(self)
}
}
}
}
以上代码修改后再次执行,控制台成功输出了我们预期的l
对象
大功告成!
这一节的内容到这里就结束啦!传说中的短小精悍,直戳要点!嗯,明天就是劳动节啦,虽然今天已经是假期第二天了:)虽然有点短,但小伙伴们不要介意啊,下周一定补回来!大家节日快乐,玩好吃好!但也不要忘了学习哦!
下面是重头戏!!!第一次公众号关注福利出炉~~~扫描二维码关注后回复【福利】即可领取
【HTML5程序设计】电子版