Note 1. ECMAScript. Bound functions.

  • Thread starter Dmitry A. Soshnikov
  • Start date
A

Asen Bozhilov

Dmitry said:

The article is good and useful.

| Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
| 15.3.4.5
| 4. Let F be a new native ECMAScript object .

That step is described by ECMA-262-5 without any conditions. And the
following behavior is conformance:

function F(){}
var thisValue = {};
print(F.bind(thisValue) !== F.bind(thisValue)); //true

I do not see any purposes for that design. The design of `bind' permit
memory optimizations but unfortunately ECMA-262-5 does not describe
that.
 
D

Dmitry A. Soshnikov

The article is good and useful.

Yes, there two main goals which distinguish built-in implementation from
the JavaScript's `bind` implementations:

(1) with `new` operator `this` value refers to the newly created object,
but not to bound one;

(2) it's not possible to implement the spec's behavior in JavaScript of
the `new` bound functions (such as e.g. in "Prototype.js"): there if you
return `this` manually, you'll return bound `this`; if you return
"nothing" (implicit `this` value logically mention) -- then the result
is indeterminate and dependent on the return value of a bound function.

Alternatively, I would propose to separate these two tasks: static bound
`this` value is for "bind", and currying is for "curry" method (how it's
called in other languages).
| Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
| 15.3.4.5
| 4. Let F be a new native ECMAScript object .

That step is described by ECMA-262-5 without any conditions. And the
following behavior is conformance:

function F(){}
var thisValue = {};
print(F.bind(thisValue) !== F.bind(thisValue)); //true

I do not see any purposes for that design. The design of `bind' permit
memory optimizations but unfortunately ECMA-262-5 does not describe
that.

Yep, they could mention this optimization. If the function is the same
(with the same lexical environment chain), then, if there are no
pre-bound partial args, the bound functions should not be different.


P.S.: for testing I used again BESEN implementation which is (after
small bug fixes made today) are completely conformant in the question of
"bind".

Dmitry.
 
R

Richard Cornford

On 13.06.2010 23:50, Asen Bozhilov wrote:
| Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
| 15.3.4.5
| 4. Let F be a new native ECMAScript object .
That step is described by ECMA-262-5 without any conditions.
And the following behavior is conformance:
function F(){}
var thisValue = {};
print(F.bind(thisValue) !== F.bind(thisValue)); //true
I do not see any purposes for that design. The design of `bind'
permit memory optimizations but unfortunately ECMA-262-5 does
not describe that.

Yep, they could mention this optimization. If the function is
the same (with the same lexical environment chain), then, if
there are no pre-bound partial args, the bound functions should
not be different.
<snip>

Remember that javascript's functions (including functions returned
from calls to - bind -) are objects and so may, at any future time,
have properties added and values assigned to those properties.

var a = F.bind(thisValue);
var b = F.bind(thisValue);

a.something = 'test';

Do you now expect - b - to have a property called 'something' and for
that property to have the value 'test'? And worse, would you want the
existence of - b.something - to depend on an optional optimisation
where it may be there in some environments and not there in others? As
things stand, ES5 guarantees that each function returned from a call
to - bind - has unique identity, and so that an implementation behave
as if it has unique identity. I think that this is a good thing.

Richard.
 
A

Asen Bozhilov

Richard said:
Remember that javascript's functions (including functions returned
from calls to - bind -) are objects and so may, at any future time,
have properties added and values assigned to those properties.
var a = F.bind(thisValue);
var b = F.bind(thisValue);

a.something = 'test';

It can depend on setter/getter and it is not necessary the assigned
value to be accessed by:

b.something;
Do you now expect - b - to have a property called 'something' and for
that property to have the value 'test'? And worse, would you want the
existence of - b.something - to depend on an optional optimisation
where it may be there in some environments and not there in others? As
things stand, ES5 guarantees that each function returned from a call
to - bind - has unique identity, and so that an implementation behave
as if it has unique identity. I think that this is a good thing.

Certainly true, but in the near future we will see the approaches
like:

for (var i = 0, len = coll.length; i < len; i++) {
coll.onclick = function () {
//....
}.bind(thisValue);
}

And by ECMA-262 should be created `len' * 2 objects. Would you want
implementations to follow that or they will make optimizations behind
the scene and behind the ECMA-262 standard?
 
D

Dmitry A. Soshnikov

On 13.06.2010 23:50, Asen Bozhilov wrote:
| Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
| 15.3.4.5
| 4. Let F be a new native ECMAScript object .
That step is described by ECMA-262-5 without any conditions.
And the following behavior is conformance:
function F(){}
var thisValue = {};
print(F.bind(thisValue) !== F.bind(thisValue)); //true
I do not see any purposes for that design. The design of `bind'
permit memory optimizations but unfortunately ECMA-262-5 does
not describe that.

Yep, they could mention this optimization. If the function is
the same (with the same lexical environment chain), then, if
there are no pre-bound partial args, the bound functions should
not be different.
<snip>

Remember that javascript's functions (including functions returned
from calls to - bind -) are objects and so may, at any future time,
have properties added and values assigned to those properties.

var a = F.bind(thisValue);
var b = F.bind(thisValue);

a.something = 'test';

Yeah, really, somehow I completely forgot about it when was replying to
Asen's question -- although, myself, mentioned in the article (with
example creating `prototype' property) that function objects created via
`bind' method are extensible.
Do you now expect - b - to have a property called 'something' and for
that property to have the value 'test'? And worse, would you want the
existence of - b.something - to depend on an optional optimisation
where it may be there in some environments and not there in others? As
things stand, ES5 guarantees that each function returned from a call
to - bind - has unique identity, and so that an implementation behave
as if it has unique identity. I think that this is a good thing.

Yes, of course. Such optimization leads to ambiguities. So the ES5 is
logical and correct in this question.

Dmitry.
 
D

Dmitry A. Soshnikov

It can depend on setter/getter and it is not necessary the assigned
value to be accessed by:

b.something;

If it will be the same object (with the same identity), how will you
distinguish that getting from "some" object should return this property
and from "other" not? I put "some" and "other" in quotes, because there
is only one the same object. It could be some virtual table of
properties, related with index, but, that to complex implementation and
not worth all that.

Do you now expect - b - to have a property called 'something' and for
that property to have the value 'test'? And worse, would you want the
existence of - b.something - to depend on an optional optimisation
where it may be there in some environments and not there in others? As
things stand, ES5 guarantees that each function returned from a call
to - bind - has unique identity, and so that an implementation behave
as if it has unique identity. I think that this is a good thing.

Certainly true, but in the near future we will see the approaches
like:

for (var i = 0, len = coll.length; i< len; i++) {
coll.onclick = function () {
//....
}.bind(thisValue);
}

And by ECMA-262 should be created `len' * 2 objects. Would you want
implementations to follow that or they will make optimizations behind
the scene and behind the ECMA-262 standard?


By the way, I see that "joined objects" are gone from the ES5 too.

Dmitry.
 
R

Richard Cornford

It can depend on setter/getter and it is not necessary the
assigned value to be accessed by:

b.something;

If the whole behaves as if - a - and - b - are unique objects then how
that is a achieved doesn't matter much.
Do you now expect - b - to have a property called 'something'
and for that property to have the value 'test'? And worse, would
you want the existence of - b.something - to depend on an optional
optimisation where it may be there in some environments and not
there in others? As things stand, ES5 guarantees that each
function returned from a call to - bind - has unique identity,
and so that an implementation behave as if it has unique identity.
I think that this is a good thing.

Certainly true, but in the near future we will see the approaches
like:

for (var i = 0, len = coll.length; i < len; i++) {
coll.onclick = function () {
//....
}.bind(thisValue);

}

And by ECMA-262 should be created `len' * 2 objects.


There is no need to wait for the future, I have already seen code
exactly like that. Whether it should be seen is another matter given
that, as it stands, this:-

var f = (function () {
//....
}).bind(thisValue);

for (var i = 0, len = coll.length; i < len; i++) {
coll.onclick = f;
}

- should do the same with the creation of only two function objects.
Would you want implementations to follow that or they will make
optimizations behind the scene and behind the ECMA-262 standard?

Optimisations behind the scenes are never unwelcome, so long as the
specified behaviour is achieved. Here the functions returned from
calls to - bind - must behave as if they each have unique identify.

Richard.
 
R

Richard Cornford

On 14.06.2010 17:46, Asen Bozhilov wrote:

By the way, I see that "joined objects" are gone from the ES5 too.

Some years ago some of the participants in this group put quite a lot
of effort into trying to find evidence for "joined objects" in
javascript implementations, with zero success. So probably the only
thing that is lost by their being removed from ES5 is the possibility.

Richard.
 
G

Garrett Smith

[...]
Certainly true, but in the near future we will see the approaches
like:

for (var i = 0, len = coll.length; i< len; i++) {
coll.onclick = function () {
//....
}.bind(thisValue);

}

And by ECMA-262 should be created `len' * 2 objects.


There is no need to wait for the future, I have already seen code
exactly like that.


I see it too.

I remember seeing that approach being used in Google Gears code, with
code comments labeled "aa" (apparently the code reviewer).

The approach given is inefficient and clumsy. Beginners want to do this
to fix variable this arg but it's not necessary.

A better solution for what the above code appears to be doing would be
to register an event handler -- just one -- on a common ancestor and use
delegation.

// Typical approach using delegation.
container.onclick = function(ev) {
var target = getTarget(ev);
if(isPanelActuator(target)) {
panelActuatorClickHandler(target, ev);
}
}

The initialization is reduced to one assignment and one event handler
function object creation. There is no need for a bind function. Although
the approach uses a layer indirection with bubbling, the resulting code
is actually shorter and simpler.

Garrett
 
A

Asen Bozhilov

Garrett said:
Richard said:
Asen Bozhilov wrote:
for (var i = 0, len = coll.length; i<  len; i++) {
     coll.onclick = function () {
         //....
     }.bind(thisValue);
}
And by ECMA-262 should be created `len' * 2 objects.

There is no need to wait for the future, I have already seen code
exactly like that.

I see it too.

The approach given is inefficient and clumsy. Beginners want to do this
to fix variable this arg but it's not necessary.

A better solution for what the above code appears to be doing would be
to register an event handler -- just one -- on a common ancestor and use
delegation.

// Typical approach using delegation.
container.onclick = function(ev) {
   var target = getTarget(ev);
   if(isPanelActuator(target)) {
     panelActuatorClickHandler(target, ev);
   }

}


Yes, that approach is better than registration handler for each
element. For example I use similar code in Firefox extension which
should handle user clicks on pages in opened tabs. So my code looks:

var content = document.getElementById('content');
content.addEventListener('click', handleClick, true);

May be FAQ should noticed about that approach. Here are many threads
in which that approach is posted, especially Thomas Lahn has posted
many times.
 
D

Dmitry A. Soshnikov

Some years ago some of the participants in this group put quite a lot
of effort into trying to find evidence for "joined objects" in
javascript implementations, with zero success. So probably the only
thing that is lost by their being removed from ES5 is the possibility.

Yep, it's quit strange thing, even if the [[Scope]] property is
indistinguishable. Because of again -- joined objects should copy
non-internal properties bidirectionally (similar to the case with
mentioned Asen's proposal for "bind"):

function A() {
function B(x) {return x*x;}
return B;
}

var b1 = A();
var b2 = A();

b1.foo = "bar";

alert(b2.foo); // should be "bar" if objects are joined
alert(b1 === b2); should be true

I remember one discussion (but can't find it now, if you'll find, let me
know) where joined objects are recognized as not so needed, so I guess
they were removed from ES5 because of that.

Dmitry.
 
D

Dmitry A. Soshnikov

On 14.06.2010 14:46, Dmitry A. Soshnikov wrote:

(2) it's not possible to implement the spec's behavior in JavaScript of
the `new` bound functions

Yes, it's possible: <URL: http://bit.ly/aboVXU> -- implemented by
Bozhilov; I've updated the article.

Dmitry.
 
G

Garrett Smith

[...]

May be FAQ should noticed about that approach. Here are many threads
in which that approach is posted, especially Thomas Lahn has posted
many times.
That would be a really good idea. The subject comes up enough.

An event delegation entry and an execution context entry would both be
useful. The execution context entry might be worded as: What does the
"this" keyword refer to in a function call?

It is mentioned in the code guidelines doc in three places:

http://jibbering.com/faq/notes/code-guidelines/#design

| Do not traverse over elements to modify the style or add an event
| callback to each element.

and below that:

| For events, use event delegation. That is, replace a loop that adds a
| callback to each element in a collection with a callback on a common
| ancestor.

Garrett
 
P

pedz

It is mentioned in the code guidelines doc in three places:

http://jibbering.com/faq/notes/code-guidelines/#design

I was curious so I poked the link. The first bullet was not clear to
me.

Why is:
goog.isDef = function(val) {
return val !== undefined;
};

a useless function?

Also, reading a little further, the term "nonstandard" might be
avoided. The function statement, it says, is "nonstandard" but it is
in the 3 and 5 standards. The key point is that it is "allowed" and
not "required" and it is that choice of the implementers that make it
a bad choice to use. Calling something in the standards a nonstandard
isn't helping the unenlightened.
 
D

David Mark

I was curious so I poked the link.  The first bullet was not clear to
me.

Why is:


a useless function?

Because it is nothing but a strict comparison with a performance
penalty (a function call).
Also, reading a little further, the term "nonstandard" might be
avoided.  The function statement, it says, is "nonstandard" but it is
in the 3 and 5 standards.
The key point is that it is "allowed" and
not "required" and it is that choice of the implementers that make it
a bad choice to use.

It is best avoided if cross-browser compatibility is desired.
Calling something in the standards a nonstandard
isn't helping the unenlightened.

Mentions do not make standards. Requirements do. I agree there is a
lot of confusion out there as most developers do not read the specs
carefully (if at all). For example, it seems like every time I tell a
beginner that there is no standard for the window object, they cite a
mention of it in the ES3 specifications, despite the fact that the
document does not define host objects as anything but implementation-
dependent. It is, after all, a *language* specification (and the
unrelated DOM recommendations don't shed much light on that object
either).
 
R

RobG

Dmitry said:

The article is good and useful.

| Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
| 15.3.4.5
| 4. Let F be a new native ECMAScript object .

Seems to me "bind" would be better named "setThis" as that is more
descriptive of what it does. It would also avoid a naming conflict
with implementations of bind in existing libraries.

But I guess I'm too late with that suggestion in regard to ECMAScript
ed 5.
 
L

Lasse Reichstein Nielsen

Dmitry A. Soshnikov said:
On 14.06.2010 14:46, Dmitry A. Soshnikov wrote:

Yes, it's possible: <URL: http://bit.ly/aboVXU> -- implemented by
Bozhilov; I've updated the article.

It's distinguishable from the correct behavior. If you use the bound
function as a method of an instance created from it, then it will
believe it to be a construct call, even if it isn't.


The problem is that you can't identify a construct call
programmatically. Testing whether "this" is an instance of the
function being created can be faked.

/L
 
G

Garrett Smith

I was curious so I poked the link. The first bullet was not clear to
me.

Why is:


a useless function?

Yes. Not only is it is useless, it can lead to potential misuse.

Compared to `val !== undefined`, `goog.isDef(val)` is less clear and
less efficient.

It could /potentially/ be useful would be in an iteration style pattern
where a callback was needed however the way `goog.isDef` is organized
indicates that it is not intended to be used in that manner, but instead
as a general method to determine if a value is not undefined.

To see how Google uses goog.isDef, I did a search for goog.isDef and
came up with the following:
http://code.google.com/p/doctype/wiki/ArticleCoordinates

| Updated Nov 15, 2008 by pilgrim
| Labels: is-article, about-dom
| ArticleCoordinates
| HOWTO deal with page coordinates (goog.math.Coordinates)
| [...]

| /**
| * Class for representing coordinates and positions.
| * @param {Number} opt_x Left
| * @param {Number} opt_y Top
| * @constructor
| */
| goog.math.Coordinate = function(opt_x, opt_y) {
| /**
| * X-value
| * @type Number
| */
| this.x = goog.isDef(opt_x) ? Number(opt_x) : undefined;
|
| /**
| * Y-value
| * @type Number
| */
| this.y = goog.isDef(opt_y) ? Number(opt_y) : undefined;
|
| };

goog.math.Coordinate first calls `goog.isDef(opt_x)`. As we've seen,
`goog.isDef` checks to see if the value of `opt_x` is not undefined. If
that is the case, then the value of opt_x is converted to a number,
otherwise `this.x` is given value undefined.

It is strange design to be giving `x` a value that could be any of: 1) a
finite number, 2) NaN, or 3) undefined. If avoiding NaN was wanted, (as
would happen for Number(undefined)), then the code fails on that
account. Instead, it could have succeeded by using instead the built-in
`isFinite` function. Example:

this.x = isFinite(opt_x) ? Number(opt_x) : undefined;

- and that could be changed to use the more efficient unary +, which
uses the same type conversion algorithm ToNumber.

Eliminating NaN values from being assigned to the object's `x` and `y`
properties would change Coordinate.equals to return true where two
Coorinates had both NaN values, which might make sense in what appears
as an overengineered alternative to {x:0, y:1}.

Google's goog.math.Coordinate provides a stellar example of why
goog.isDef is useless. It achieves this by showing an example of how the
abstraction reduces code clarity and makes the intent less clear.
Instead, the function isFinite probably should have been used
`isFinite`. (and ideally it would seem best to just use {x: 0, y: 0} on
an as-needed basis).

Although the function is useless, it is also potentially misleading.
This is because the identifier is "isDef" could be interpreted as "is
defined". The closest thing to describign "defined" in ECMAScript is
[[HasProperty]], however this function does not make a determination
about an object having a property; only that val !== undefined. The
function is potentially misleading to a beginner who might use the
function in lieu of the `in` operator to check property existence.

I actually had a conversation about a year ago with a prominent
javascript expert and contributor to YUI regarding a bug in YUI. The
code in question was ObjectAssert.hasProperty.

The problem came down to making the same false inference about "if
obj.prop is undefined, then obj does not have a property prop".

The problem function was calling YAHOO.lang.isUndefined, which does the
same thing that the goog.isDef function does but in the positive sense
(or negative sense, depending how you look at it). That is, it returns
`val === undefined`. Like goog.isDef, YAHOO.lang.isUndefined does not
check an object for the existence of property (that would require both
the object and a property to be passed in).

I noticed the problem when testing a "clone" type of function with an
object that had a property with the value undefined. Although it may
seem odd to give a property with value `undefined`, it can happen (as
the goog.Math.Coordinate does). I wanted to make sure that the property
had been cloned properly and the clone function was required to be able
to handle that case.

Realizing the problem in the YUI function, I filed a bug:

| ObjectAssert.hasProperty -- provides inaccurate results. Instead of
| checking for the presence of a property, Assert.hasProperty checks the
| value.

The author and his manager both wanted me prove what I'm saying with a
testcase, should I actually want the bug fixed.

Much easier, I found, was to just fix the bug myself.

Using a similar strategy to YAHOO.lang.isUndefined, goog.isDef checks to
see if the value is not undefined. If the code must determine if an
object has a property, then goog.isDef(myObj.prop) will not provide that
information. The reason it won't tell you if the object has the property
is that the object could have a property, and the value could be undefined.

That type of abstraction is not useful. It is a useless abstraction that
requires explanation.

What is worse is that it can actually be deceptive to those who don't
know the difference between an object having a property with an
undefined value and an object not having a property.

In contrast val !== undefined is instantly recognizable to anyone who
understands ECMAScript.
Also, reading a little further, the term "nonstandard" might be
avoided. The function statement, it says, is "nonstandard" but it is
in the 3 and 5 standards. The key point is that it is "allowed" and
not "required" and it is that choice of the implementers that make it
a bad choice to use. Calling something in the standards a nonstandard
isn't helping the unenlightened.

No, a Function statement is not defined by either 3 or 5 editions.
Function statement is a nonstandard syntax extension.

Please take a look at the FAQ and see if that doesn't clear things up
for you.
http://jibbering.com/faq/#functionStatement

Asen noted that function declaration appearing where only statements are
allowed will result in a SyntaxError in BESEN and DMDScript. I plan to
add that to the FAQ. Currently, we have:

| Implementations that have the function statement extension process
| Fze as a Statement, in order, while other known implementations
| evaluate Fze upon entering the execution context that it appears in.
| For consistent behavior across implementations, avoid function
| statement; use either FunctionExpression or FunctionDeclaration
| instead.

I'd like to change the FAQ to mention that.

Proposed:
| Implementations that have the function statement extension process
| Fze as a Statement, in order. Others, including JScript, evaluate Fze
| upon entering the execution context that it appears in. Yet others,
| notably BESEN and DMDScript, throw a SyntaxError.
|
| For consistent behavior across implementations, do not use function
| statement; use either FunctionExpression or FunctionDeclaration
| instead.

Garrett
 
G

Garrett Smith

Yes. Not only is it is useless, it can lead to potential misuse.

Correction: Not only is it useless...
It is strange design to be giving `x` a value that could be any of: 1) a
finite number, 2) NaN, or 3) undefined. If avoiding NaN was wanted, (as
would happen for Number(undefined)), then the code fails on that
account. Instead, it could have succeeded by using instead the built-in
`isFinite` function. Example:

Sorry, Number(undefined) results in NaN, but that would not happen
because that case is covered by goog.isDef.

Number(NaN) and cases where `opt_x` is not undefined, but converts to
NaN are not covered. If disallowing NaN values to be assigned to the `x`
and `y` properties is wanted, the code fails.

Garrett
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top