-
-
Save valueof/9a6d60e9c15ac4239b3d to your computer and use it in GitHub Desktop.
class Channel { | |
constructor (opts) { | |
/* ... */ | |
} | |
load () { | |
var widget = document.createElement("iframe"); | |
widget.{ | |
style.{ | |
width = "100%"; | |
border = "none"; | |
overflow = "hidden"; | |
display = "none"; | |
}; | |
dataset.{ | |
disqusUid = this.uid; | |
}; | |
allowTransparency = true; // < IE8 iframe transparency | |
frameBorder = 0; | |
width = "100%"; | |
id = "dsq" + this.uid; | |
src = this.target + "#" + this.uid; | |
}; | |
} | |
}; |
var Channel = function (opts) { /* ... */ }; | |
Channel.prototype.load = function () { | |
var widget = document.createElement("iframe"); | |
widget.{ | |
style.{ | |
width = "100%"; | |
border = "none"; | |
overflow = "hidden"; | |
display = "none"; | |
}; | |
dataset.{ | |
disqusUid = this.uid; | |
}; | |
allowTransparency = true; // < IE8 iframe transparency | |
frameBorder = 0; | |
width = "100%"; | |
id = "dsq" + this.uid; | |
src = this.target + "#" + this.uid; | |
}; | |
}; |
var Channel = function (opts) { /* ... */ }; | |
Channel.prototype.load = function () { | |
var widget = document.createElement("iframe"); | |
widget.{ | |
style.{ | |
width = "100%"; | |
border = "none"; | |
overflow = "hidden"; | |
display = "none"; | |
}; | |
allowTransparency = true; // < IE8 iframe transparency | |
frameBorder = 0; | |
width = "100%"; | |
id = "dsq" + this.uid; | |
data-disqus-uid = this.uid | |
src = this.target + "#" + this.uid; | |
}; | |
}; |
var Channel = function (opts) { /* ... */ }; | |
Channel.prototype.load = function () { | |
var widget = document.createElement('iframe'); | |
widget.setAttribute('style', 'width:100%; border:none; overflow:hidden; display:none'); | |
widget.setAttribute('allowTransparency', 'true'); // < IE8 iframe transparency | |
widget.setAttribute('frameBorder', '0'); | |
widget.setAttribute('width', '100%'); | |
widget.setAttribute('id', 'dsq' + this.uid); | |
widget.setAttribute('data-disqus-uid', this.uid); | |
widget.setAttribute('src', this.target + '#' + this.uid); | |
// ... | |
}; |
it is most definitely aesthetically pleasing but in exchange for that syntax the code block now relies on 3 new language semantics where the old code relied on only one, function calls.
a new syntax that reduces the required semantics for code and/or reduces boiler plate is great but this change actually increases the semantics.
Added another example, rewrote Channel.prototype.load
stuff with class
syntax as implemented in Traceur.
"Moustache"? The library's called "mustache" :-p
Interesting to see =
used for assignment in this context. How does scoping work?
var widget = document.createElement("iframe");
var id = "outside";
widget.{
id = "dsq" + this.uid;
data-disqus-uid = id;
};
alert(widget["data-disqus-uid"]); // ?
Also, I've only seen assignments in the examples. Can any arbitrary statement appear within the .{ ... }
? Using =
implies to me that it can, but I would have reservations about that.
@mikeal I don't see how you count three?
I'm not really clear why the syntax of .{...} should be any different than an object literal. ie:
{a: true, b: false}.{b: true, c: false} === {a: true, b: true, c: false}
As written it looks like .{ is just another form of "with", rather than a true Object.extend.
@ljharb Because object literal defines a property ([[DefineOwnProperty]]
) while.{ a = true }
sets their values ([[Put]]
; hence my tweet poll yesterday). There is a discussion on es-discuss whether the proposal should cover both cases (=
for [[Put]]
and :
for [[DefneOwnProperty]]
) but I personally think it's not worth the confusion.
And again, this es-discuss thread is a much better source of information than my attempts at summarizing. :-)
@ljharb What Anton said, and also because object literal syntax suggests creating a new object, and shouldn't be used as the syntax for modifying an existing object.
This is not like with
except insofar as it might accomplish some use cases (but definitely not all, e.g. the way with
is used in template engines). It doesn't affect the scoping semantics at all.
I'm curious what you mean by "true Object.extend"?
Just as a clarification here, this syntax has been demoted to "strawman" status which means it's not being considered for ES6, so it's an "early-stage" proposal for post-ES6. So no need to panic. :) I'm personally not convinced the new syntax is worth it, though I'm open to some variations of it. (Though I'm dead set against using object literal syntax for object updates.)
I didn't realize that's what "strawman" meant, so that definitely means I can relax.
by "true Object.extend", I was under the impression that Object.extend(foo, {})
would be equivalent to foo.{}
.
So what happens if you do foo.{ bar = true; }
when bar
isn't a previously defined property on foo
? Does it throw an error or silently ignore it? Does the block .{...}
have its own scope, so i can use the var
keyword inside it for temporary variables without necessarily assigning them to properties? What happens if an error is thrown from inside the .{...}
block, or if a return statement is used? Within the foo.{...}
block, are all outer variables namespaced? ie, what if I try to refer to foo
from inside the block?
To me, unless an object's property is explicitly defined with defineProperty, an object literal only suggests to me a map of keys to values, so I don't see any issue with using it ass a mass assignment vector (a la jQuery.fn.attr({})
)
In general, I feel that any new syntax that generates this many questions is likely too unintuitive to be useful.
- .{} scoping.
- different semantics for assignment (assigns to object) in that scope
with
style named scoping of variables in that scope.
You could count 2 and 3 as one thing since they are part of the same feature but to me they are both new semantics to think about because both naming and assignment rules are thought of separately and are changed in this new scoping system.
@mikeal There's no scoping going on here at all. The scope chain is identical inside the .{} body. There's no with
here. It's just syntactic sugar for a bunch of property assignments, nothing more.
"syntactic sugar" is a nice way of saying "syntactic semantics".
yes, it's very easy to reason about once you know all of the rules, but there are more rules in the new example than the previous.
let's just talk a little about the loss of assumptions.
assignment has exactly three forms in ES5.
global name = 'str'
var (function scoped with hoisting) var name = 'str'
property assignment obj.prop = 'str'
For all three of these existing forms there is a rule you can observe in the line the assignment takes place. In fact, you can assume all the rules regarding assignment by looking at the line assignment takes place the only exception being that for function scope hoisting you'll have to look to the beginning of the function.
This new form removes that assumption. With this change, you must check up the code to either a .{
or function (){
to understand the assignment rules. This does not only apply to where this new feature is used but in all assignment code because understanding the assignment rules is crucial to understanding in any code block and once this feature is in you can't assume any assignment isn't using it by only looking at the line the assignment is made.
Assumptions are the best kinds of features, removing them is a zero sum game.
With this change, you must check up the code to either a
.{
or function(){
to understand the assignment rules.
That's true, and a fair objection. I'm definitely not okay with the obj.{ foo: bar}
syntax, because there's nothing about it that suggests that it's updating the object, which is why I floated the assignment syntax. I could imagine alternatives that make the distinction clearer more locally, like obj{ .foo = bar; }
. That probably doesn't quite work because of ASI, though. Others have suggested obj.={ foo: bar }
. Not sure what I think of that. Food for thought.
Dave
I feel like a longer code example with lots of assignments would make the problems that this would cause much more apparent...
I definitely think foo.={normal object literal}
would be the cleanest. It has an equals sign connoting assignment, it's an object literal so it uses familiar syntax and connotes a hash map, and it'd be functionally equivalent to Object.extend(foo, {})
so the behavior would be shimmable in earlier ES engines.
One thing that's unclear to me with the obj.={...}
syntax is how to notate nested updates (aka "deep copy" in e.g. jQuery). For example, does obj.prop
have a baz
property in this example:
var obj = { prop: { baz: 3 } };
obj.={
prop: {
foo: 1,
bar: 2
}
}
The above syntax doesn't suggest a deep copy. The syntax Allen proposed is recursively consistent so it makes more sense as a deep copy:
var obj = { prop: { baz: 3 } };
obj.{
prop.{
foo: 1,
bar: 2
}
}
but I really don't like the use of "dot-literal" to suggest object update.
But actually, in terms of semantics -- even without resolving the syntax question -- it's not clear to me whether shallow or deep is the more common case. For example, jQuery lets you decide with a flag.
Dave
just want to float this, what is wrong with obj.update({ foo: bar })
or Object.update(obj, {foo: bar})
?
this achieves the desired flexibility in nesting without new syntax and is definitely explicitly "updating" the object.
Are there not enough bikesheds?
So what happens if you do foo.{ bar = true; } when bar isn't a previously defined property on foo?
Same as foo.bar = true;
today.
just want to float this, what is wrong with obj.update({ foo: bar }) or Object.update(obj, {foo: bar})?
I feel like it's similar to your argument against o.{ a = b }
. To figure out whether you are defining a new property or updating the current one you will have to check the code to see if it's o.update({ ... });
or o.otherMethod({ ... });
or o = { ... };
.
@mikeal: I mostly agree; I don't think "mustache" has been all that well motivated so far. I've been opposed to it for the most part since the beginning, but for me where I started to see some more win was when -- with the variation using assignment syntax -- I realized it could also be used for chaining (aka the fluent style aka cascades) for API's that don't return this
for you. For example:
a.{ pop(); pop() }
or
a{ .pop(); .pop() }
or what have you. But I generally agree with you that syntax has to be well-understood and well-motivated to be worth adding. Mustache is definitely not at that point.
Dave
those rules are only as unclear with .update as they are with new syntax. you still have to learn the rules, the only difference is that there is an english word pointing you in the right direction.
i agree, it's not compelling enough to me yet to justify the loss in assumptions. while i agree that using :
is not clear about updating it doesn't fall in to the trap of stepping on the existing assignment semantics because you already have to look to the top of an object literal definition to understand it.
@mikeal, yeah I agree—was just pointing out that it doesn't solve the unclear rules problem.
sure, not entirely, but it doesn't step on the rules people now assume about assignment statements that exist entirely outside of using this feature.
Object.update, w00t.
We killed <| the other week. I think .{ is going down. But we should really do something like Object.update (Prototype's Object.extend). We talk about paving cowpaths, that's an obvious one and it leads somewhere good.
/be
@BrendanEich, yes, Object.extend (update) was one of the most popular items on JSFixed.
@isaacs Here's a lovely unpainted bikeshed for you: how about Object.update
for deep and Object.set
for shallow?
Dave
Given the proliferation of subtly different Object.extend implementations, I think this would be a good thing for the ES spec to lay down a decision on. In node, we eventually just removed util.extend entirely, because it was too complicated to satisfy everyone's desires for it to be all things, handling getters and deeply nested objects and so on. New syntax for this is silly, though.
@dherman If I understand you properly, then, Object.update would deep-copy, and Object.set would shallow-copy?
So:
var a = { b: { c: 'd', e: Object.create(Object.prototype, {f: { get: function () { throw 'no can get!' }, configurable: false }}) } }
var b = Object.update({x: 'y'}, a)
var c = Object.set({x: 'y'}, a)
In this example, then, c.b === a.b
, b.b !== a.b
, and b.b.e
is an evil getter?
I have no strong opinions about the method names. set
and update
are fine. But it is a bad idea to introduce new syntax for this, even though it is cute.
@izs: agree on syntax being bad (premature, unjustified, impossible to polyfill, etc.).
@dherman: first, I wonder if we shouldn't just try to standardize Object.extend as the shallow thing, and make it match prototype but handle enumerable accessors too.
Second thought: naming. The set vs. update names do not connote shallow vs. deep. No name that I know of does, so the boring but clear way is Object.deepExtend. Too long? It's not the common case going by existing libraries I know of.
/be
It is the other way around for me. I think the
.{ ... }
syntax in this form looks really nice.