Note 1. ECMAScript. Bound functions.

As we know, ECMA-262-5 standardized bind method of the Function.prototype. The source of the current version of this method is “Prototype.js” library, although, the standardized version has several differences. This approach is well known and was successfully used in ES3, so the main purpose of the current note is to show the technical details and differences (which can cause confusing) of the ES5 implementation.

The current implementation of the Function.prototype.bind has two purposes, which are the static bound this value and the partial application of a function. Let’s consider them.

The main purpose of the bind method is to statically bind a this value for subsequent calls of a function. As we considered in the ECMA-262-3 in detail. Chapter 3. This, a this value can vary in every function call. So, the main purpose of the bind is to fix this “issue” which can appear e.g. when we attach a method of an object as event handler of some DOM element. Using a bound function we can always have a correct this value in the event’s handling.

var widget = {
  state: {...},
  onClick: function onWidgetClick(event) {
    if (this.state.active) {
      ...
    }
  }
};

document.getElementById("widget").onclick = widget.onClick.bind(widget);

I used simple click event attaching in the example above — via onclick method, but on practice you can better use multiple listeners pattern using addEventListener or attachEvent. However the goal of the example is to show how this value is predefined and bound to the widget object inside the onclick method, and this.state property is available.

Another purpose of the current “bind” implementation is a currying (or closer to mathematics — a partial application of a function). It is the technique of transforming a function of multiple arguments to a chain of functions each with a single argument that produce at the end the same result. Which means we can generate functions based on other functions, and some (or maybe all) arguments of the new function can be predefined. And applying the new function with the rest arguments (with the last part of arguments) forms a partial application of the function.

function foo(x, y) {
  // partial
  if (typeof y == "undefined") {
    return function partialFoo(y) {
      // complete
      return x + y;
    };
  }
  // complete
  return x + y;
}

foo(10, 20); // 30
foo(10)(20); // 30

var partialFoo = foo(10); // function
partialFoo(20); // 30

The practical rationale of partial application can be the case when you often use a function with the same (repeating from call to call) part of arguments. In such case it is convenient to encapsulate these arguments, making a new partial function. Another good practical example can be again attaching an event listener with bound this value and some bound data which will be available at the activation of the listener’s handler. As a simplest example of such bound data can be event object itself. Theoretically, partial application is related with some mathematical theorems and also with lambda calculus where functions have only one argument.

So, practically, using “bind” method, we can make partial functions:

function foo(x, y) {
  return x + y;
}

var partialFoo = foo.bind(null, 10);
partialFoo(20) // 30

In this example a static bound this value is not essential for us (we didn’t even use this keyword inside the function), but the currying is. Of course, we can combine these two purposes of the bind method.

In the previous section a brief overview of the bind method and its purpose was described. However, you should already know about it and this note is mostly about technical details of the bind method and bound functions exactly in ECMA-262-5.

Recently, there were debates whether the semantic implementation of this method should be the same as in “Prototype.js” library, however it is so now. Although, an alternative form with accepting an array of partial arguments was mentioned several times, arguing that it would be more convenient.

In contrast with the similar bind implementations written on JavaScript (before the ES5), the built-in implementation produces specific version of a normal function. The bound functions (see 15.3.4.5) do not have some internal properties which normal functions do. This is made for the optimization. Thus, a bound function does not have a prototype property or the [[Code]] (the function body code), [[FormalParameters]] (the list of formal parameter names), and [[Scope]] (a parent lexical or variable environment) internal properties.

Thus, absence of the [[Scope]] property provides optimization, because such incomplete function objects do not form closures. They are just intermediate proxies which delegate to the original functions.

However, a bound function has additionally the following internal properties: [[TargetFunction]] — a reference to a target function from which the bound function is created; [[BoundThis]] — exactly a bound this value; [[BoundArgs]] — a bound arguments which are used in partial application of the function.

Three important internal methods — [[Call]], [[Construct]] and [[HasInstance]] are overloaded for the bound functions.

The [[Call]] internal method of every function is activated when we call a function (via the call expression, e.g. foo() or with using call and apply methods). When the [[Call]] internal method of a bound function is activated, it after own handling delegates to the original [[Call]] of the target function, passing needed this value and arguments. With pseudo-code it can be presented so:

boundFn.[[Call]] = function (thisValue, extraArgs):

  var
      boundArgs = boundFn.[[BoundArgs]],
      boundThis = boundFn.[[BoundThis]],
      targetFn = boundFn.[[TargetFunction]],
      args = boundArgs.concat(extraArgs);

      return targetFn.[[Call]](boundThis, args);

And then a normal [[Call]] of the target function is activated, establishing a new execution context evaluating the [[Code]] property of the target function (see 13.2.1 — a normal [[Call]] activation).

function F(x, y) {
  this.x = x;
  this.y = y;
  return this;
}

// bind "this" value and partially "x" argument
var BoundF = F.bind({z: 30}, 10);

// create object passing partial "y" argument
var objectFromCall = BoundF(20);

// because "this" inside "BoundF" refers
// to {z: 30} object, then we have
alert([
  objectFromCall.x, // 10, from [[BoundArgs]]
  objectFromCall.y, // 20, from extraArgs
  objectFromCall.z, // 30, from [[BoundThis]]
]);

In the algorithm of the bound [[Call]] we also see that passed by the caller this value (first argument thisValue in the pseudo-code) does not participate in actions, because the [[BoundThis]] is always used as this value and exactly it’s passed to the original [[Call]]. Therefore, using call or apply methods in respect of providing manual this value for a bound function won’t change anything — still bound this will be used:

var b = BoundF.call({z: 40});
alert(b.z); // still 30

The overloaded [[Construct]] method (see 15.3.4.5.2) also delegates to the original [[Construct]] which is defined in 13.2.2. However, there is one important note with using bound function as a constructor. The original [[Construct]] to which delegates the overloaded [[Construct]] of a bound function, calls then the original [[Call]] of the target function, but not the [[Call]] of the bound function. Which means that inside the initializing call of the function we do not have a bound this value, but instead the this value set to the newly created object.

This seems quite logical, though can confuse, because bound this value does not matter in using the bound function as a constructor. This is important difference from the JavaScript bind implementation e.g. in “Prototype.js” — there the bound this value is used instead, which is not conforming to ES5. And returning this value in “Prototype.js” implementation manually from a bound constructor, we will have the bound this value. But if there is no manual return this, then result of a bound constructor is indeterminate and dependent on returned value of the bound function.

In general case it is not possible to implement completely conforming bind behavior with using bound function as a constructor on JavaScript. Although, some acceptable emulation of the new bind behavior is possible.

In built-in ES5 bind implementation, having the same BoundF from the example above, we have the following results:

// put something to the prototype
// of newly created objects, notice, that we use
// exactly "F" function, but not "BoundF"
F.prototype.a = 40;

// 15.3.4.5.2 [[Construct]] for bound functions
var objectFromConstruct = new BoundF(20);

alert([
  objectFromConstruct.x, // 10, from [[BoundArgs]]
  objectFromConstruct.y, // 20, from extraArgs
  objectFromConstruct.z, // undefined, does not use [[BoundThis]]
  objectFromConstruct.a // 40, from F.prototype === objectFromConstruct.[[Prototype]]
]);

Notice again, that by default a bound function does not have a prototype property — it is taken from the original (target) function. And even if we define this property on the bound function manually (a bound function object is extensible), it will change nothing, because, repeat, [[Construct]] and [[Call]] at the end are applied to the target function.

alert(BoundF.prototype); // undefined

BoundF.prototype = {
  constructor: BoundF,
  a: 100,
  b: 200
};

var objectFromConstruct2 = new BoundF;

// no matter, prototype still is taken from
// F.prototype, but not BoundF.prototype,
// because exactly F.[[Construct]] is called from
// BoundF.[[Construct]]
alert([
  objectFromConstruct2.a, // 40, from F.prototype, but not 100
  objectFromConstruct2.b // undefined
]);

So, regarding this value in bound function cases we have this summary:

  • Simple call via BoundF()boundThis;
  • call / apply, i.e. BoundF.call(manualThis) — still boundThis;
  • as a constructor — new BoundF()newly created object.

The overloaded [[HasInstance]] method (see 15.3.4.5.3) also delegates to the original [[HasInstance]] which is defined in 15.3.5.3 section. This internal method participates in the instanceof operator, therefore it should return true in testing with both — the original and the bound functions:

alert([
  objectFromConstruct instanceof F, // true
  objectFromConstruct instanceof BoundF, // true
]);

Notice, that because of such delegation, instanceof returns true for testing an object created by the original constructor with the bound one:

var object = new F;

alert([
  object instanceof F, // true
  object instanceof BoundF, // true
]);

That also relates to using Object.create method. Even if we pass manually created BoundF.prototype as the first argument for Object.create, instanceof check returns false for testing with bound and original constructors:

var boundProto = {
  constructor: BoundF,
};

BoundF.prototype = boundProto;

var foo = Object.create(BoundF.prototype);

print(Object.getPrototypeOf(foo) === BoundF.prototype); // true

// but because of overloaded [[HasIntance]],
// which delegates to F.[[HasIntance]] we have false

print(foo instanceof BoundF); // false
print(foo instanceof F); // false

// in contrast with non-bound constructor

var bar = Object.create(F.prototype);

print(bar instanceof BoundF); // true
print(bar instanceof F); // true

// However, if we set F.prototype
// to the needed object, then
// instanceof will work for object,
// created by the bound constructor

F.prototype = boundProto;

print(foo instanceof BoundF); // true
print(foo instanceof F); // true

Another feature of bound functions — they throws a TypeError if there is an attempt to read the caller or the arguments properties on the bound functions, and this happens regardless whether the code is strict or not. In contrast, normal functions throws the same errors only in strict mode.

alert([
  F.caller; // error only in strict
  F.arguments; // error only in strict

  BoundF.caller; // always error
  BoundF.arguments; // always error
]);

One interesting feature described on MDC and which is related with bound functions, is an ability to call a constructor function with various number of arguments. Thus, needed arguments are passed as an array. I.e. we get sort of Function.prototype.apply but available for the applying with new — in using the function as a constructor. A slightly modified example from MDC:

Function.prototype.construct = function (args) {

  var
    boundArgs = [].concat.apply([null], args), 
    boundFn = this.bind.apply(this, boundArgs);
        
  return new boundFn();

}

function Point(x, y) {
  this.x = x;
  this.y = y;
}

var point = Point.construct([2, 4]);
console.log(point.x, point.y); // 2, 4

However, we should consider that such approach is not efficient, since every time we should create a new bound function for that, so the example is mostly academical.

So, we have the following key features of the bound functions created via the built-in bind method:

  • The main purpose is binding statically the this value and partial applications;
  • such functions are “lightweight” — they do not have [[Scope]] property (and therefore do not form closures), and also [[FormalParameters]], [[Code]] and prototype properties;
  • they delegate to the [[Call]], [[Construct]] and [[HasInstance]] of the original function;
  • thus inside the [[Call]] method this value is bound, but if the [[Call]] is activated from the [[Construct]] — not, there it refers to the newly created object;
  • independently from the strict mode such functions throw a TypeError exception for attempts of reading the caller or the arguments properties on these functions.

If you have questions, I will be glad to answer them in comments.

ECMA-262-5:

Other useful articles:


Written by: Dmitry A. Soshnikov.
Published on: 2010-06-13

Tags: , , , ,

 
 
 

One Comment:

  1. Gravatar of sowingsadness sowingsadness
    14. August 2012 at 07:13

    Честно говоря, это жесть.

    var b = BoundF.call({z: 40});
    alert(b.z); // still 30

    Чем они думали, когда задавали такое поведение?
    Это нарушение согласованности языка.
    Превращают JS в треш.


Leave a Reply

Code: For code you can use tags [js], [text], [ruby] and other.

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>