Hacker News new | past | comments | ask | show | jobs | submit login
Advanced objects in JavaScript (tipling.com)
162 points by btipling on Aug 24, 2014 | hide | past | favorite | 45 comments



I once participated in a web security research project where we used ES6 Proxies to sandbox third-party scripts, basically controlling what DOM operations they're allowed to do in a whitelist. This is relevant when you include sketchy advertisement scripts or scripts you don't trust (e.g. "trusted" scripts but delivered through an untrusted CDN). By default, no operations were allowed and by whitelisting until there were no more errors, we managed to effectively completely sandbox jQuery, Google Maps and Google Analytics. None of these scripts were allowed to execute more operations than the ones exactly required for them to function. Proxies were an elegant way to intercept getters and setters on window, document, location, etc...


Are you able to link to the research? It sounds like a native-JS version of NoScript, which is really interesting (apologies to the dead comment under me which asked something similar)


Yep, here's the paper: http://www.acsac.org/2012/openconf/modules/request.php?modul...

It was presented at ACSAC 2012.


This is actually a large part of why ES5 added these things, to enable Caja and the like.


Reminds me of https://github.com/tildeio/oasis.js but not sure if it solves the same type of problem.


This is interesting, I hadn't heard of Oasis.js. Both approaches try to solve the same problem, i.e. manage to sandbox third-party scripts. However, the implementation is different: oasis.js uses iframes, we chose for not using iframes, mainly because iframes are not enough to limit access to security-sensitive APIs such as XHR, Geolocation and local storage.


One constraint for Oasis.js when picking a technology was the compatibility with IE8. So we use modern technology everywhere we can, but we have to find a way to polyfill them for older browsers (mostly IE8/9).


Articles like this, about up-and-coming features in JavaScript, usually make me think two things:

1) JavaScript is starting to become a "big" language, in terms of number of keywords and syntax;

2) While many of these things that JavaScript is (finally) gaining are things we're familiar with in other languages (and have empirically found to be useful), they often seem somewhat more unwieldy, fiddly, and complicated in JavaScript.

As a handwavy example, Ruby generally has most of the stuff these sorts of articles describe, and it can be complicated at times, but usually (in my experience) only when you're trying to do something complicated anyway; in JavaScript, a lot of it looks quite complicated out of the box even when you're doing something that ought to be simple.

There are many JavaScript developers out there, some with years of experience, who are confused about, say, `var Foo = function() {}` versus `function Foo() {}`, or the difference between `new Foo()` and `Object.create(Foo.prototype)`. To be honest, it slightly worries me that even more complexity is being added at this point.


JavaScript has a lot of complexity and little gotchas. I often compare the simplicity of Go with JavaScript and wish JS were more like that, but I still find myself enjoying JavaScript. I hope that "use strict"; evolves to further limit the bad pit falls even experienced developers can get stuck in. Some of the new features like isPrototypeOf can replace the old behavior, like instanceof. I would hope that once browsers start supporting more of the new things we all migrate to using them, if they're implemented well.


Good thing that this is all coming to Javascript. But I can't help but think we're in bizarro land when the main language of the most ubiquitous medium of the 21st century is only getting all that rather basic stuff (at least by all the other languages' standards) in 2015 or something...


Everything there besides Proxies and Symbols is already in common browsers, and I can't say I'm familiar with Proxies and Symbols in other languages (not to say some other languages necessarily don't have them, but I don't think most users consider them basic or encounter them often).


Proxies are like __get/__call in PHP, method_missing in Ruby and the similar magic methods Python has.

Symbols are sort of JavaScript's answer to private members. Closest to __ for members in Python.


I'm a bit puzzled what symbols are. If you want a unique thing that !== anything except itself, make an empty object. It seems to me: var foo = Symbol("foo") ought to let you dereference via bar.foo vs. bar[foo] (the latter shouldn't even work!) or it's not what it seems it wants to be.


Symbol has other traits beyond just not being unique. Properties added via symbols are not iterated over in a for in and do not show up in calls to Object. getOwnPropertyNames()


The point of it is you can implement private properties, as I understand it:

    function MyClass() {
        var mysymbol = new Symbol("foo");
        this[mysymbol] = 3;
    }

    var bar = new MyClass();
    bar.foo // this won't work


What a convoluted mechanism! But where do you keep your reference to the symbol to keep it private? Anywhere you keep it (e.g. in a closure somewhere) that's private doesn't need symbols, so what problem does this solve?

And surely in your private context, bar.foo should still work.


What a convoluted mechanism!


I think the slow browser updates that existed prior to Chrome might have contributed to a lull in developing the language. Some of these features can't be fixed for old browsers with a polyfill, but now that updates are happening quicker and there's a lot of excitement around the language I hope we see more of these.


I don't think there were ever any "slow updates" to Javascript, except maybe in the period of IE5/6 dominance to Firefox 1.0. In the Browser Wars, Microsoft and Netscape competed to add features as fast as possible. Firefox still has many additional features in its JS engine that aren't in ECMAScript (for each, coroutines).

I'm more concerned about adding features without fully considering the consequences. When you add a feature to Javascript, it's there forever. Rushed feature dumps are how we end up with things like the Web Audio API.


True, the early period had a furious pace of updates. I guess I was referring to the era of ES6, that lasted a long time. I think Microsoft at one point actually said that IE6 was the final browser and they wouldn't release a new version. IE6 was amazing when it first came out, but it became a blight in the end that hurt more than it helped.


That's why some features have been postponed from ES6 to ES7


Alternatives to getType and setType functions (while still not using getters and setters):

Have a single polymorphic function called type: type() gets the type, type(value) sets the type.

Have .get and .set functions, called like: get('type'), set('type', value).


I like that idea, but with Proxy you don't have to write the functions any more, you could just define that one get on the handler you feed into Proxy and then in your code you can access via the . notation, like:

  var obj = new Proxy(
   {foo_: "bar"}, 
   {get: function(target, name) {
     return target[name + "_"]; 
   });
  console.log(obj.foo); //logs "bar"
Of course maybe you'd do something a little fancier than just return the value, like return null for property names with _ to prevent accessing private variables. I don't know what the performance of Proxy will be like, but it's an alternative to getters and setters and lets you have a fall through for things you haven't defined.

What I'm really excited about is reactivity on objects for setting and getting without using functions. You could have your setter trigger events without forcing someone to use a set function like you have in Backbone.

I think we'll see an entirely new set of model libraries because of these new ECMAScript 5 and 6 features.


I'm not yet excited about these getters and setters and new features in the latest JavaScript. I've put a lot of time and effort into developing ECMAScript (JavaScript) 3 patterns that overcome various limitations, and have got accustomed to the idea of deploying ECMAScript 3. In terms of providing an API, getters and setters will be very useful. It will be good to have out-of-the-box JavaScript functionality that does not require extra code to use, like various methods I use to overcome the ECMAScript 3 limitations.

Does anyone have an estimate for when ECMAScript 6 will be widely deployed across browsers, almost ubiquitous? I would say now it's safe to use SVG in many situations on the web that would not have been OK a few years ago, I'm curious about when the tipping point will be with ECMAScript 6.

Anecdotally, recently my customers have been a lot less interested in supporting older versions of IE, and I have used SVG in a couple of situations, and that would not have been OK a few years back. I have not been closely following the developments of ECMAScript 6, it has seemed like it will be a while until it's widely deployed. The only things I have found practical use for are typed arrays, and I have been using them in node specific code rather than in code that I expect to run across browsers.

Though I am interested in the latest developments in JavaScript, my focus will still be on what can be done with the JavaScript available in almost every browser. I am also interested in making use of getters and setters in my code but being able to compile it to ECMAScript 3, I may be able to do some relatively simple text replacement rather than having to parse and compile an abstract syntax tree.


Kangax's ES6 Compatibility Table tracks the implementation status for many browsers, though Chrome's ES6 support is hidden behind a pref: chrome://flags/#enable-javascript-harmony

http://kangax.github.io/compat-table/es6



> with Proxy you don't have to write the functions any more, you could just define that one get on the handler you feed into Proxy

Doable with "old" JS. Consider this function:

    function prop(name, gNs) {
        var _name = '_' + name, 
	    get = gNs && gNs.get, 
	    set = gNs && gNs.set, 
	    undef = function (v) { return typeof v == 'undefined'; },

        if (get && set)
            return function (x) { return undef(x) ? get.call(this, this[_name]) : (this[_name] = set.call(this, x), this); }

        if (get && !set)
            return function (x) { return undef(x) ? get.call(this, this[_name]) : (this[_name] = x, this); }

        if (!get && set)
            return function (x) { return undef(x) ? this[_name] : (this[_name] = set.call(this, x), this); }

        return function (x) { return undef(x) ? this[_name] : (this[_name] = x, this); }
    }
Pass `prop` (a) the name of a property you're defining getter/setter methods for and (b) an object with any custom getter/setter you might care to define (or neither, if you like), and it'll return a function that will act as both which you can assign to a prototype.


I like that, but you'd still have to call a function to get the properties though and this wouldn't prevent anyone from accessing those properties directly.

Like:

  obj.setFoo("bar");
  obj.getFoo();
  obj.foo = "bad stuff"; //overwritten
instead of

  obj.foo = "bar";
  obj.foo;
  obj.foo_ //error, not allowed access


True enough, and if privacy is a must have, my implementation is a non-starter.

I tend towards the (somewhat unpopular and certain arguable) opinion that need for privacy via a runtime-enforced mechanism is overrated... give us developers using an API/library effective/well-documented methods that do what we need and warn us when we shouldn't touch something with a convention (like an underscore), and usually we're happy to leave the black box closed.

It's usually when abstractions leak or implementations aren't complete that we're tempted to tinker across boundaries, and when that happens, the lack of a privacy enforcement mechanism may not be the real problem.

(OTOH, they can indeed help keep some bad situations from getting worse, and when trusted code is mingling with untrusted in situations like web mashups, privacy-enforcement can be really helpful for security...)


I don't know why, because I haven't thought through it enough yet but I really like the idea of freezing or protection in the case of a reactive data model like Backbone models. I'm thinking of maybe writing up some quick implementation where you provide a schema and you get a reactive object that is frozen and has a catch all. Just want to see where that goes.


This is pretty cool, but something about the notation confused me. Say we have an object called "foo":

    var foo = {};
Is it really necessary to define properties on it with this notation?

    Object.defineProperty(foo, 'bar', { value: 'baz' });
Since foo itself is an Object, wouldn't it also have this method? Why can't I define a property on foo like this?

    foo.defineProperty('bar', { value: 'baz' });


Regarding your first question, it isn't necessary to define properties with that notation at all. You could just do:

  foo.foo = "bar";
But then it would be writable and configurable.

  Object.defineProperty(foo, 'bar', { value: 'baz' });
This foo.foo cannot be deleted. It cannot be written to. So it's a little different. But really I was just demonstrating the use of Object.defineProperty in a non-verbose way. You can try this out yourself, I made a jsfiddle: http://jsfiddle.net/btipling/q1v3fn20/

Regarding your second question, defineProperty is a method on Object, not Object.prototype. "Instances" as you might call them only have direct access to methods found on their prototype chain. You can think of Object.defineProperty as a static method on Object if that helps. I wrote a post about "this" which includes some information about prototypes:

http://bjorn.tipling.com/all-this

You should probably first checkout the mdn documentation though: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid...


Small correction: Symbol¹ is available in Firefox since back in ~June and should arrive in Firefox Beta in ~10days.

1: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


Sadly there are still some kinks to work out so it's going to stay on Nightly only. https://bugzilla.mozilla.org/show_bug.cgi?id=1041631


So earliest Release date is sometime in February then. Not that bad of a delay at least.

Thanks for the update.


I wasn't aware that the Firefox nightly had Symbol, I will download Firefox nightly later and update the post, I should have Firefox nightly anyway. Thank you!

Edit, tested and updated blog post. Thanks again.


> You can use Object.getOwnPropertyNames to get all the properties defined on an object. This might be a nice alternative to a for in loop.

Careful. These have different behavior for own properties which are not enumerable. A for-in loop doesn't include them, but Object.getOwnPropertyNames does.


Right Object.keys(foo) is better.


And I also missed out on including this, thank you.


Nice catch, I will update the blog post.


CoffeeScript abstracts out a lot of of awkwardness of working with JS objects.


It does however concrete in a lot of awkwardness from working with compiling a static language into a dynamic one then running it.


CoffeeScript is not a static language - unless I'm misinterpreting what you mean by "static".

And the compilation is pretty light. The syntax is different, but for the most part the semantics are 1:1 with Javascript.

Having said that, any compile-to-language is going to introduce problems that need to be solved. Source maps and short watch+compile times have basically eliminated any problems for me.


You're perhaps thinking of TypeScript.


Yes, I love classes in CS. It makes using the prototype system so simple




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: