1

I'm troubleshooting some 3rd party code on a client's website. The client was having issues with the code not working. I found that the issue was related to bound JS functions. Arguments that were passed to the bound function were undefined. I couldn't figure out why. Everything seems fine. However, I then found that the client has overridden the Bind function. Here is what they have:

Function.prototype.bind = function(scope) {
  var _function = this;

  return function() {
    return _function.apply(scope, arguments);
  };
};

So if I create a function

var sumFunction = function(a, b){
  console.log("a: " + a);
  console.log("b: " + b);
  return a + b;
}

Then bind that function:

var boundFunction = sumFunction.bind(null, 10);

When I call that bound function I get the following:

console.log(boundFunction(20));

a: 20
b: undefined
NaN

I found a similar SO question that was using the same bind function. javascript custom scope binding function

It appears that it used to work? The SO question I linked seemed to work back in 2013, but now it doesn't form me.

Is this just outdated? JavaScript isn't my main strength, but my client will want to know why their function is causing the problem.

I found the overridden bind function to be odd. Especially the line return _function.apply(scope, arguments); It seems like passing the entire arguments object is incorrect. Shouldn't it only send the arguments in array position 1 and higher? I tried changing that to this to test:

Function.prototype.bind = function(scope) {

  var _function = this;
  var newArgs = Array.prototype.slice.call(arguments, 1)

  return function() {
    return _function.apply(scope, newArgs );
  };
};

But now I just get the following:

console.log(boundFunction(20));

a: 10
b: undefined
NaN

1 Answer 1

3

When the function is bounded, there might be an array of arguments after the 1st, so use slice(1) to get them. When the function is called, get the all the arguments, and concat both args arrays.

Concat both arrays of arguments:

Function.prototype.bind = function(scope) {
  var _function = this;
  var args1 = Array.prototype.slice.call(arguments, 1);

  return function() {
    var args2 = Array.prototype.slice.call(arguments, 0);
    return _function.apply(scope, args1.concat(args2));
  };
};

var sumFunction = function(a, b){
  console.log("a: " + a);
  console.log("b: " + b);
  return a + b;
}

var boundFunction = sumFunction.bind(null, 10);

console.log(boundFunction(20));

However, calling slice on arguments, might cause the V8 engine to skip optimisation on the function. A better way would be to just iterate the arguments manually, and add them to a single array:

Function.prototype.bind = function(scope) {
  var args = [];
  var _function = this;
  for(var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }

  return function() {
    var newArgs = args.slice(0);
    for(var i = 0; i < arguments.length; i++) { newArgs.push(arguments[i]); }
    return _function.apply(scope, newArgs);
  };
};

var sumFunction = function(a, b){
  console.log("a: " + a);
  console.log("b: " + b);
  return a + b;
}

var boundFunction = sumFunction.bind(null, 10);

console.log(boundFunction(20));

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

7 Comments

Thanks for the quick comment! So what has changed since 2013? Do you happen to know? Apparently, based on the SO I linked, it used to work. Which is odd to me.
The original polyfills we used for bind didn't include applying the extra arguments. This code doesn't include the arguments either. Someone probably disabled the polyfill in the past, and someone restored it lately.
Okay, so this bind function is just some polyfill that the client has added? That makes sense. I don't know why else they would have overridden it.
There was a problem in some implementations of arguments in older browsers, that caused memory leaks in that case. Of course the only mention I can find right now is an answer in SO that doesn't give many details.
Found one of the articles - jstips.co/en/javascript/… . Turns out it's more of a browser engine optimisation problem than a memory leak.
|

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.