Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript -- 模拟实现call和apply #17

Open
mt51 opened this issue Oct 27, 2018 · 0 comments
Open

Javascript -- 模拟实现call和apply #17

mt51 opened this issue Oct 27, 2018 · 0 comments

Comments

@mt51
Copy link
Owner

mt51 commented Oct 27, 2018

模拟实现call()apply()

call()apply()都能够用来绑定this到某个对象上,我们来模拟实现一下相同的功能。

先看个例子

function () {
  console.log(this.a)
}
var a = 'global'

var obj = {
  a: 'local',
  foo: foo
}

obj.foo()  //  local

可以看到,通过对象的某个属性来调用函数时,该函数内部的this会指向当前对象。这样我们就有思路模拟callapply。也就是说将调用call或者apply的函数保存在this要绑定到的对象,然后通过该对象来调用函数就可以了。

模拟call的实现

Function.prototype._call = function (context) {
    context.fn = this
    var result = context.fn()
    delete context.fn
    return result
}

做个测试

function foo() {
    console.log(this.a)
}
var a = 'global'
var obj = {
    a: 'local'
}
foo._call(obj)  // 'local'

很简单,我们已经实现了简单的call的模拟,还有参数传递没有实现。

call接收的参数都是不固定的,从索引值第一个开始需要传递给调用的函数,而apply则是将数组的每一项逐个传入调用函数。可以使用eval()来执行这个函数来实现。

Function.prototype._call = function (context) {
    context.fn = this
    var args = []
    var len = arguments.length
    for (var i = 1; i < len; i++) {
        args.push('arguments[' + i + ']')
    }
    var result = eval('context.fn(' + args + ')')
    delete context.fn
    return result
}

继续做个测试

function foo (name, age) {
    console.log(name)
    console.log(age)
}
var obj = {}

foo._call(obj, 'name', 'age')   // 'name'  'age'

我们还记得,call可以传入nullundefined,这是this会指向全局对象,再修改下我们的实现

Functuin.prototype._call = function (context) {
    if (typeof this !== 'function') {
        throw Error('Function.prototype._call - what is trying to be bound is not callable')
    }
    context = context || window   //  这里只考虑了浏览器中的实现
    context.fn = this
    var args = []
    var len = arguments.length
    for (var i = 1; i < len; i++) {
        args.push('arguments[' + i + ']')
    }
    var result = eval('context.fn(' + args + ')')
    delete context.fn
    return result
}

模拟apply的实现

apply的模拟和call很类似,只是参数处理的时候有些不同。

Function.prototype._apply = function (context, args) {
    if (typeof this !== 'function') {
        throw Error('Function.prototype._apply - what is trying to be bound is not callable')
    }
    context = context || window   //  这里只考虑了浏览器中的实现
    context.fn = this
    var result
    if (!args) {
        result = context.fn()
    } else {
        var len = args.length
        var tmp = []
        for (var i = 0; i < len; i++) {
            tmp.push('args[' + i + ']')
        }
        result = eval('context.fn(' + tmp + ')')
    }
    delete context.fn
    return result
}

简单的测试一下

var a = 'global'
function foo (name, age) {
    console.log(this.a)
    console.log(name, age)
}
var obj = {
    a: 'local'
}

foo._apply(obj, ['name', 'age'])
//  local
//  'name' 'age'
@mt51 mt51 changed the title Javascript -- 模拟实现callapply Javascript -- 模拟实现call`和apply Oct 27, 2018
@mt51 mt51 changed the title Javascript -- 模拟实现call`和apply Javascript -- 模拟实现call和apply Oct 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant