4

I have a function that is already bound by using Function.prototype.bind method. Somehow, I want to override the this parameter of the bound function. But it does not work. Check the description in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind , it says that 'this' can not be override. Is there any workaround/ solution to override 'this' of bind function? My test code:

function testF() {console.log(this)}

var test = testF.bind('abc');
test = test.bind('xyz');
test() // print 'abc' instead of 'xyz'. I'm expecting to print 'xyz'
5
  • 4
    This sounds like an XY Problem - clearly whoever supplied the already bound function wants it bound - what end goal are you trying to achieve? It sounds more like an API design issue than anything else. Commented Mar 17, 2016 at 15:23
  • 1
    bind just wrap it with context but it is executed with the nearest context which is abc. Notice that you bind it twice. testF.bind('abc').bind('xyz'). The second Bind is contexing the outer binded function. Commented Mar 17, 2016 at 15:25
  • 2
    We might be able to help you find an alternative solution if you explain what you are trying to achieve overall. But as it stands, the answer is no. Commented Mar 17, 2016 at 15:26
  • 1
    You have your answer below. I'm sure there's a "workaround" but it's going to involve having access to the pre-bound function. Not really a workaround so much as "just doing it right the first time." Commented Mar 17, 2016 at 15:47
  • If you can control the values being bound you might want to take a look at the edit to my answer -- otherwise disregard, it won't solve you problem. I still stand by my earlier statements in the answer that you probably need to refactor or change your approach to begin with. Commented Mar 17, 2016 at 16:18

3 Answers 3

12

Is there any workaround/ solution to override 'this' of bind function?

No. As you can see in the spec, the .bind method returns a new bound function:

[...]
4. Let F be BoundFunctionCreate(Target, thisArg, args).
[...]
16. Return F.

A bound function is described as:

A bound function is an exotic object that wraps another function object. A bound function is callable (it has a [[Call]] internal method and may have a [[Construct]] internal method). Calling a bound function generally results in a call of its wrapped function.

A bound function also has an internal [[BoundThis]] property:

The value that is always passed as the this value when calling the wrapped function.

(emphasis mine)

Looking at the internal [[Call]] method confirms that too:

9.4.1.1 [[Call]] ( thisArgument, argumentsList)

When the [[Call]] internal method of an exotic bound function object, F, which was created using the bind function, is called with parameters thisArgument and argumentsList, a List of ECMAScript language values, the following steps are taken:

  1. Let target be the value of F’s [[BoundTargetFunction]] internal slot.
  2. Let boundThis be the value of F’s [[BoundThis]] internal slot.
  3. Let boundArgs be the value of F’s [[BoundArguments]] internal slot.
  4. Let args be a new list containing the same values as the list boundArgs in the same order followed by the same values as the list argumentsList in the same order.
  5. Return Call(target, boundThis, args).

As you can see, thisArgument is completely ignored, only the value of [[BoundThis]] is used. There is no way to change [[BoundThis]] after it was set.

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

Comments

3

I'm going to hazard that no there is not.

The entire point of bind is to force the bound function to always execute with a given context. A rough idea of what bind does is:

function simpleBind(fn, context) {
  return function() {
    return fn.apply(context, arguments);
  };
}

So you can clearly see here -- a new function is actually being returned here and so altering it's context has absolutely no effect on the wrapped (bound) function because it's using values from the enclosing function call.

My best guess at why you're looking to do something like this it hack something in place instead refactoring which is (almost always) the better approach. While I realize it may not be "in a budget" you're not really given a lot of options here. It's best to rethink your approach so you don't need to do these kinds of ugly hacks.

** This is a simplistic implementation just used to demonstrate the main point

edit

Taking some inspiration from @djechlin's comments I was going to demonstrate a way to "abuse" this simpleBind implementation by using reference type (I quote abuse, because this isn't really doing anything you weren't already allowed to do). In your case, where you're binding a string (assuming you have some control over bound values) you can do something simple like:

var context = {string: "abc"},
    fn = function() { console.log(this.string); },
    bound = fn.bind(context);
bound(); // logs 'abc'
context.string = "xyz";
bound(); // logs 'xyz'

This would only be useful if you could alter the values being bound to a function and uses the concept that they are references and not copies which is standard JS behavior.

4 Comments

A suggestion to add to your answer: Your toy implementation follows a pseudo-classical pattern that uses the closure to treat context as a private variable. If this pseudo-classical pattern hasn't been deeply flawed all these years, then context should be fully private, which means inaccessible. Your toy code convinced me after I realized the pseudo-classical pattern was being invoked. +1, of course.
To be honest @djechlin I don't know if I fully understand what your comment is saying (I'd like to know though!). I went with a simple, straightforward example to demonstrate a way that this works -- I know native bind is far more complex and that this is not a suitable replacement but it demonstrates the point I was making (that you cannot change context in a bound function).
Yeah I overcomplicated it. Looking at simpleBind I am positive it's impossible to change the this argument i.e. context that fn is called with. The reason is that I know from write pseudoclasses into Javascript that closures are one way to create private variables, which is exactly the pattern here.
@djechlin it is impossible to reassign it to something else, but not to change it. I added a note to my answer but you can use the reference nature of objects to alter the value used by a bound function.
0

Answer for general development:

//if the function is declared like this
let call=function(){console.log(this);}
// then you can pull the function out of bind through the prototype.
call.bind({hello:'my firend'});
call();// print {hello:'my firend'}
call= Object.getPrototypeOf(new call()).constructor;
call(); print {window}
call=call.bind({bay:'my darling'});
call(); print {bay:'my darling'}

Disadvantage: you need to call the constructor, as a result of which the body of the function is called and thus may have side effects from the call. For example, a call is calculated 1 time and subsequently the variables in the context of the function but outside of it will be changed or deleted. Or throw an error.

If you have access to the native function, then you need to do this:

function call(){
    console.log(this);
}
call=Object.assign(call.bind({hello:'my friend'}),{owner:call});
let otherCall=Object.assign(call.bind({hello:'my friend'}),{owner:call.owner});
otherCall();

Comments

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.