Some small, pre-colored panels for the shed. Given that these are
mostly matters of syntax and not semantics, please believe me when I
suggest that the warts discussed herein present sharp edges that
should be rounded off by the committee -- not because they're
interesting in any way but because the primary users of the language
are shipping (bad) fixes around the network billions of times a day.
Such rank inefficiency needs sunset date.
Summary of proposals:
---------------------
// 1.) Really generic generics
ArrSubtype = function() { };
ArrSubtype.prototype = Object.create(Array.prototype);
var nas = new ArrSubtype();
nas.push("howdy", "pardner");
nas.slice(0) instanceof Array; // currently true
nas.slice(0) instanceof ArrSubtype; // will be true after fix
// 2.) Shorthand for "function", aka the "not lambda"
node.addEventListener("click", #(e){ e.preventDefault(); });
node.removeEventListener("click" obj!#(e){ ... }); // see #3
// 3.) Shorthand for Function.prototype.bind
var boundMethod = obj!method;
node.addEventListener("click", obj!method);
node.removeEventListener("click", obj!method);
Discussion:
-----------
1.) Really generic generics
Methods on Array.prototype that today return instances of Array should
instead return instances of whatever subtype begat them. Assume that
the DOM sucked less and that NodeList instances were subtypes of
Array. In that case, we should be able to do something like:
NodeList.prototype.parents = function() {
// should return a NodeList
return this.map(function(n) { return n.parentNode; });
}
document.querySelectorAll("p").slice(2).parents().filter(...);
Today, the only way for subtypes to use these generics is to wrap or
re-implement them. The functions that need to be fixed include:
map, filter, splice, slice, concat
Rationale:
* Kill code in libraries that exists only to wrap built-in methods
thanks to existing mis-specification of generics
* Sub-types of Array suffer many warts, but making Array.prototype
methods return instances of subtypes will allow DOM-side changes to
make subtyping much more natural in real-world systems
2.) Function shorthand
The most commonly-used object types in JavaScript have syntactic sugar:
new Array(...) == [ ... ]
new Object(); ... == { .... }
Why leave functions out of the party? Lambda syntaxes have been
discussed at great length with most of them either failing to remove
the "function" wart or giving up multi-line or visual delineation
properties. We instead propose just shortening the word "function".
Several variants are possible:
// equivalent:
function(){ return 10; }
#(){ return 10; }
#{ return 10; } // no args, optionally elide ()
// lambda-style return
#{ 10; }
// makes for easy-to-read operations:
[ ... ].map(#(i){ i+10; });
Rationale:
* less typing, particularly when supplying Array generic and DOM
callbacks
* equivalent semantics to the function(){} syntax
* able to work as a short-ish lambda-like declaration without large
changes
* "JavaScripty"; keeps the existing brace rules, keeps multi-line
behavior, and helps to visually delimit start/end of function object.
3.) Syntax for bound function de-reference
Many functions, both in the DOM and in the library, accept functions
as arguments. ES5 provides a bind() method that can ease the use of
these functions when the passed method comes from an object. This is,
however, inconvenient. E.g.:
node.addEventListener("click", obj.method.bind(obj, ...));
Also, insufficient. Event listeners can't be detached when using
Function.prototype.bind:
// doesn't do what it looks like it should
node.removeEventListener("click", obj.method.bind(obj, ...));
So why does this suck? Two reasons: it's long-ish to type, and it
doesn't do what the dot operator does -- i.e., return the same
function object every time.
What to do? We propose the bang operator -- ! -- as a new form of
binding de-reference of function objects. It's one character and even
includes a dot. It has the following additional properties:
1.) all de-references via bang from an object/function pair return
the *same* function object (modulo #4).
2.) non-function objects de-referenced via bang are not mangled in
any way (identical to dot-operator de-reference)
3.) there's no provision for binding arguments (curry)
4.) bang-bound functions are weakly referenced. If GC would
otherwise remove a function object from memory, having previously bang-
bound a function should not keep the function object alive
As from the intro:
node.addEventListener("click", obj!method);
// actually removed without extra book-keeping or wrappers!
node.removeEventListener("click", obj!method);
And a small (untested) version of the behavior as a function written
in ES5 (without weakref behavior):
var bang = function(obj, func) {
var fn = obj[func];
// Still no isFunction...*sigh*
if (Object.prototype.toString.call(fn) === "[object Function]") {
if (!obj["_bound"]) {
Object.defineProperty(obj, "_bound", {
value: {},
enumerable: false,
configurable: false,
writable: false
});
}
var bound = obj._bound[func];
if (!bound) {
bound = obj._bound[func] = fn.bind(obj);
}
return bound;
} else {
// not a function object, just return the deref
return fn;
}
};
// ES3 impl lacks nice syntax:
node.addEventListener("click", bang(obj, "method"));
// works!
node.removeEventListener("click", bang(obj, "method"));
Thoughts?
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss