4

.call, .apply, .bind methods of Function.prototype don't work for functions once defined as methods of Vue instance in .vue file.

I'm using @vue/cli project, compiled with vue-cli-service serve. Here's reduced example of code, which can be placed in any Vue single-file component definition.

    methods: {
        foo() {
            console.log(this);
        }
    },
    created() {
        this.foo.call({ bar: 42 });
        this.foo.apply({ bar: 42 });
        this.foo.bind({ bar: 42 })();
    }

Expected output to the console is triple { bar: 42 }, since this value was changed for these calls. However, VueComponent object is printed to the console.

I checked these methods for overloading, using this.foo.bind === Function.prototype.bind // returns true, they are not overloaded.

It may be caused by Vue using Proxy objects or even by templates compilation.

Simplest @vue/cli project with code above will be enough to reproduce the problem.

Thanks

1
  • this.foo({bar:42}) is sufficient Commented Oct 15, 2019 at 19:23

1 Answer 1

3

Vue component methods are bound to the context, which is component instance. This could be checked with:

function foo() {
    foo() {
        console.log(this);
    }

}

...
methods: { foo },
created() {
    this.foo !== foo // true
}
...

this.foo.bind === Function.prototype.bind check isn't representative since this.foo is regular function, so it inherits bind.

Once a function is bound, it cannot be re-bound or be called with different context:

const context = {};
const boundFoo = (function foo() { return this; }).bind(context);
// boundFoo.call({}) === context
// boundFoo.bind({})() === context

It isn't a good practice to rely on arbitrary dynamic this context in JS OOP. If a method needs a context, it should be provided to it as an argument:

methods: {
    foo(ctx) {
        console.log(ctx);
    }
},
created() {
    this.foo({ bar: 42 });
}

Or be shared as instance property, depending on the purpose.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for exhaustive answer. It's very odd, that bind function makes it impossible to ever call the resulted function with different context. Looks pretty shortsighted for me. Do you know for any reason it's designed this way?
I can't agree about not being a good practice to rely on this context in JS. It's not really a good practice to write in JS, from that point (and I would agree with that). But yet, prior to this particular problem, I found no reasons not to use bizarre, odd and unpredictable this keyword in JS. Also, Vue forces programmers to use this context by design.
This allows to pass methods as callbacks without losing original context, <component :callback-prop="foo" />, something that would require to bind them explicitly in created otherwise. A workaround would be to use data to define foo, it isn't bound automatically. The statement referred to arbitrary dynamic this, not just any this. The former was overused in jQuery and other legacy libs instead of params, one of reasons it declined is that it doesn't work with arrows.
See how bind works here developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . It does fn.apply(myContext) internally and ignores this that was provided to it when you're trying to call or re-bind bound function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.