9 ms·
ES6 in Depth – Symbols
- asdf99 11y agoyet another duct tape kind of feature, from people already too corrupted by js, that will be further abused and cause yet more duct tape features in ES7. this is nothing but a more convoluted way of the pattern that sets a unique object as a unique value for comparison. well, it adds a label for easy debug. hooray.
- weavie 11y agoNo, also symbol keys don't pollute for-in, Object.keys or Object.getOwnPropertyNames so they are effectively non-global.
- yoshuaw 11y agoAs long as you don't use Symbol.for() that is - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... (I do think symbols are useful though, replied for correctness.)
- carussell 11y agoSymbols created with `Symbol.for` don't pollute any of those things, either. let x = Symbol.for("omghax"); let foo = Object.create(null) foo[x] = 'bar'; Object.keys(foo).length // returns 0
- gcb0 11y agothere was already a solution for that: .style in dom elements. it is a place where you dump style properties ad-nausea. Why not simply add a single new property to Object? It would do the very same thing, would not change the language. it would be readily available to all browsers if you just added that single new key in your logic as you already do with .style and others, and everyone would move on with their lives. I have to agree this is a duct tape by people thinking they know language design. Now i will have to wait until all browsers have this before i can use. and then i will still have to worry about older browsers (oh android, the IE6 of moment). Not to mention the time browser developers would waste actually improving things will be wasted on that syntax sugar.
- pmontra 11y agoSymbols are used all the time in Ruby (and a bunch of other languages), not as duct tape but as a very core feature of the language. Why should it be different in JavaScript? The most simple way to use them is to replace definitions like this one var north = 1; var south = 2; var east = 3; var west = 4; var direction1 = north; var direction2 = south; direction1 === direction2 ? "ops" : "ok"; The programmer is doing the work of the interprer/compiler here and make sure to pick unique values for all the constants that are going to be compared together. With symbols that becomes var north = new Symbol(); var south = new Symbol(); var east = new Symbol(); var west = new Symbol(); var direction1 = north; var direction2 = south; direction1 === direction2 ? "ops" : "ok"; which is an improvement even if it is (in a traditional JavaScript way) so much more verbose than Ruby's direction1 = :north direction2 = :south direction1 === direction2 ? "ops" : "ok" I just wish they'll add some syntactical sugar to do without that "new Symbol()" thing and create symbols as needed like Ruby does. Unfortunately, from https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol https://developer.mozilla.org/en/docs/Web/JavaScript/Referen... Symbols and JSON.stringify() Symbol-keyed properties will be completely ignored when using JSON.stringify(): JSON.stringify({[Symbol("foo")]: "foo"}); // '{}' We're going to manually serialize them when they leave the RAM.
- masklinn 11y agoThe base use case for JS symbols is not the same as Ruby symbols: regular strings will work just fine for that.
- ahoge 11y agoN, E, S, W? I think you want an enum for that.
- pmontra 11y agoIt was an example of replacing constants with symbols when the value of the constant really doesn't matter. Purposely not fancy stuff. I understand the advantages of enums (among the others, their values are constrained) but does JS have enums?
- skrebbel 11y agoI don't understand why the author compares ES6 symbols to Ruby symbols; they're nothing alike. It's kind of the opposite: Ruby symbols are guaranteed to be the same object if they have the same name, ES6 symbols are guaranteed to be a different object, even if they have the same name (description)
- espadrine 11y agos1 = Symbol.for('cat') s2 = Symbol.for('cat') s1 === s2 // true The most common use-case for them is to generate symbols that don't collide, though.
- ahoge 11y ago`Symbol.for` uses a global registry and the whole thing is "stringly typed". Looks like an anti-pattern to me. If you want one particular symbol from some library, you could import it.
- artursapek 11y agoThis was the one thing I didn't understand. Why is using a global Symbol registry any better than just using string properties like usual?
- Ded7xSEoPKYNsDd 11y agoThere won't be a problem if the string happens to be e.g. "toString", I guess. Although they added a 'Map' for that, too.
- towelguy 11y agos1 = Symbol("cat"); s2 = Symbol.for("cat"); s3 = Symbol.for("cat"); s1 == s2; // false s2 == s3; // true You can still have Symbols outside the registry.
- artursapek 11y ago
- espadrine 11y agoIf you think that this language feature has no equivalent in your favorite language, it is similar to Ruby's :symbol, where it is mostly used as keys to their hash tables (they're implemented with hashes internally). Common Lisp has something even closer for its macro system. When you write code that writes code, you often need to create new variable names that won't collide or shadow anything. `(gensym)` does that: http://www.lispworks.com/documentation/lw50/CLHS/Body/f_gensym.htm#gensym http://www.lispworks.com/documentation/lw50/CLHS/Body/f_gens.... Here's a list of languages with support for symbols. You will see even Objective-C has it: https://en.wikipedia.org/wiki/Symbol_%28programming%29#Support https://en.wikipedia.org/wiki/Symbol_%28programming%29#Suppo.... I'd be curious to learn how it is implemented in JS engines.
- inglor 11y agoLike a property basically - except the key is not a string and is known to the engine. This is both simple and uses existing JIT facilities. Symbol property access isn't particularly slower than regular access this way too. See http://blog.peschla.net/doxygen/v8_chromium_r157275/classv8_1_1internal_1_1_symbol_key.html http://blog.peschla.net/doxygen/v8_chromium_r157275/classv8_... - it's
- masklinn 11y ago> Here's a list of languages with support for symbols. You will see even Objective-C has it: https://en.wikipedia.org/wiki/Symbol_%28programming%29#Suppo... https://en.wikipedia.org/wiki/Symbol_%28programming%29#Suppo.... None of the symbols in that table match the purpose and use case for `new Symbol`, though gensym does.
- nabla9 11y agoThis is very limited symbol type. They might have named them keywords instead. It's just interned string with a new type. (It's like Common Lisp symbols limited inside the keyword package)
- unwind 11y agoAs a very green JS programmer, I found this post informative and fun. I think this sentence has an editing mistake in it: It’s simply a property whose name is not a symbol rather than a string. I think that the "not" shouldn't be there, the double negation makes the sentence fail to parse for me at least (ObCaveat: not a native speaker).
- yoshuaw 11y agoI wrote a lil' article about using Symbols a month ago - https://medium.com/code-ops/party-tricks-with-es6-symbols-ee328fdb6c4b https://medium.com/code-ops/party-tricks-with-es6-symbols-ee... Probably the most interesting use I've found for Symbols so far is to detect if an object / function was created by a specific factory - https://gist.github.com/yoshuawuyts/2bf8d5394e6f995791a0 https://gist.github.com/yoshuawuyts/2bf8d5394e6f995791a0
- welfare 11y agoI'm not entirely convinced with the function-call syntax for declaring a Symbol. Why not using something more of the lines with Ruby's :symbol syntax?
- _pmf_ 11y ago> Why not using something more of the lines with Ruby's :symbol syntax? You mean Common Lisp's syntax, of course.
- Grue3 11y ago":symbol" is only syntax for keywords though. In general the syntax is "package:symbol" or just "symbol" (if it's in current package).
- mrighele 11y agoI guess because they do not behave in the same way. In fact new Symbol() is closer to Common Lisp's (gensym) than regular :symbol, since everytime you call it you get something unique. In particular object[:symbol] would behave quite differently (and would be probably worthless since everytime you would access a new property ). It would make more sense to have :symbol a shortcut for Symbol.for("symbol") instead, but I don't think it would be that much useful.
- masklinn 11y ago> Why not using something more of the lines with Ruby's :symbol syntax? Because Ruby's symbols are completely different and designed to collide. A Ruby symbol (like an Erlang atom) is a very cheap immutable string-like structure but :symbol is :symbol. A Javascript symbol is (as others noted) much closer to a lisp unique symbol (the output of `gensym`), and having it be a function is perfectly fine since you have to keep it around anyway (or you can't access symbol-named properties). Symbol.for is roughly equivalent to :symbol, but the use case for it is much more limited and there's really no reason for a literal version./
- carussell 11y agoHere's the other thing that neither of the other commenters mentioned: You can't go around peppering your code with use of this new syntax if you want it to continue working on older runtimes. With `Symbol`, you can at least create a polyfill that approximates what it's shooting for. > declaring a Symbol Don't think of it as "declaring a symbol" (because it isn't). Think of it as generating a symbol (because it is).
- meric 11y agoLooks like Symbols is a type being introduced because objects, when used as a key in another object, gets turned into a string, unlike Lua tables can be used as keys of other tables, and are guaranteed to not be equal to any other value. Lua tables are built-in symbols. Javascript's objects are not.
- thomasfoster96 11y agoAre you talking about Maps in ES6 or Symbols? Using an object as a key is what you can do with Maps.
- bcoates 11y agoYeah, it seems like kind of an odd choice to introduce a whole new type and extend the kind of values that can index an object, when extending object indexing alone would do everything symbols do anyway.
- xienze 11y ago> Sometimes it would be awfully convenient to stash some extra data on a JavaScript object that really belongs to someone else. Convenient yes, a good idea, no. > Other code using for-in or Object.keys() may stumble over the property you created. > The standard committee may decide to add an .isMoving() method to all elements. Then you’re really hosed! So I dunno, maybe don't stash properties into an object that doesn't belong to you? It's this sort of thing that makes me hate the culture around JavaScript. Hacks upon hacks upon hacks just to save a little effort.
- sombremesa 11y agoI don't see why they didn't just use an Object/dictionary. Shouldn't be slow to iterate and solves this exact use case.
- deleted 11y ago[deleted]
- teechap 11y agoI think the only way .indexOf could be faster than linear is if the array is sorted...
- deleted 11y ago[deleted]
- teechap 11y agoLinear definitely isn't faster than binary search. To answer your original question, .indexOf is definitely implemented as linear search in JavaScript
- Demiurge 11y agoOk, it makes sense to me now. indexOf is used on mostly unsorted things, like strings. So, I guess the original problem might use a sorted array, or an object instead of an array, since I think the object keys are sorted and looked up pretty fast.
- Demiurge 11y agoI got it, it didn't even occur to me the array would be unsorted.
- IshKebab 11y agoI wish javascript had some kind of flag so you could get it to act sanely. All the comparison operators would work like they do in sane languages; type conversion wouldn't be quite so automatic and insane, etc.
- jordanwallwork 11y agoIf you just make sure to always use === then this is exactly what happens. You can use tools like jslint to warn you if you accidentally use ==
- mkolosick 11y agoNot quite though. I had a bug with Javascript where I was reading in a number and forgot to parse it to a float. I ended up doing an addition with that value, which later got used as a float again. So I had 1 + "10" turn to 110 when I tried to use it. No == or === anywhere.
- jordanwallwork 11y agoI was only really referring to automatic type conversion for comparisons. That said, the example you gave sounds like the opposite, where you wanted it to do some auto-conversion and you're disappointed that it didn't? If I did 1 + "10" I don't think I'd want it to return 11!
- afandian 11y agoI'd want it to throw some kind of cast exception, personally, not try and do what it thinks I want. (I'm not the GP)
- Nadya 11y agoIt did convert the value. It converted the 1 to a string and added "10" to the end of it, resulting in "110" which then was converted to a float and used. The error in this case would be forgetting Javascript's rules of implicit conversion. Adding a float to a string results in a string.
- jordanwallwork 11y agoI'm not from a Ruby/Lisp background, so I don't know how symbols are typically used, but the explanation of why symbols are required is to prevent collisions if two libraries chose to use the same property name; so given that, what is the point of the Symbol registry? I don't understand what the value of a symbol that is referenced by a string is, over simply using the string?
- mohaine 11y agoI think the idea is so that while symbols can prevent collisions, they can also make it impossible to set a value. The registry allows you to set the value by name, but by doing this you have to keep in mind that there might be a name collision and allows you to handle the collision. Before there was no way tell that a name collision even happened.
- carussell 11y agoSee `Symbol.hasInstance` and `Symbol.iterator`; take them as examples. Mapping an object to its iterator is going to be part of the spec and will be required at the language level (`for...of`). Before, if you looked at all of an objects' properties in SpiderMonkey, you will have noticed an `@@iterator` property. The spec could achieve the same thing as it's trying for with `Symbol.iterator` by just using a string-valued `@@iterator` property, but you still get the collision with anybody who would have used that name for something else or anyone looping over all the properties. The gist is this: symbols are just a way to have non-strings that you can use for properties. There are also facilities to generate them in such a way that the symbol will never, ever collide with those generated by somebody else, but that's not their only purpose.
- jordanwallwork 11y agoAh yeah that makes sense, cheers!
- PuercoPop 11y agoOne advantage, fast comparison by pointer equality. (Similar to interned strings)
- agd 11y agoCall Symbol.for(string). This accesses a set of existing symbols called the symbol registry. Unlike the unique symbols defined by Symbol(), symbols in the symbol registry are shared. If you call Symbol.for("cat") thirty times, it will return the same symbol each time. The registry is useful when multiple web pages, or multiple modules within the same web page, need to share a symbol. Have they added inbuilt support for globals here? People could easily add their own global symbols, why does this need to be built in?
- carussell 11y agoCould you reword the question? I don't understand what you're asking.
- agd 11y agoI guess I don't understand why the global symbol registry exists. If someone wants a global symbol map, they can create their own. What else does this add over a user implementation, and what are the use cases?
- pavlov 11y agoMaking the registry global allows compilers to optimize away those Symbol.for() calls, since they know that each call to Symbol.for("cat") is going to resolve to the same symbol instance.
- agd 11y agoBut if people are concerned about performance, they can just pre-allocate/memoize the symbols. It doesn't seem like the registry adds any new functionality, and it might encourage use of globals. Surely that's a bad thing?
- lispm 11y ago> Calling Symbol() creates a new symbol, a value that’s not equal to any other value. Lisp: CL-USER 1 > (eq (make-symbol "Foo") (make-symbol "Foo")) NIL > Symbols aren’t exactly like anything else CL-USER 2 > (type-of (make-symbol "Foo")) SYMBOL > Trying to concatenate a symbol with strings will result in a TypeError. CL-USER 3 > (concatenate 'string "abc" 'foo "def") Error: In a call to LENGTH: FOO (of type SYMBOL) is not of type SEQUENCE. > There are three ways to obtain a symbol. > Call Symbol() (make-symbol "FOO") > Call Symbol.for(string) (find-symbol "FOO") Other than that symbols in Common Lisp have a package, a value, a function and a property list. Symbols can be interned in a package or not. So-called Keyword symbols are in the package KEYWORD and have itself as the value. :I-AM-A-KEYWORD evaluates to :I-AM-A-KEYWORD.
- drudru11 11y agoThat first example with 'eq is a little surprising to me. Previous to your post, I believed that Common Lisp symbols were equivalent to Lisp atoms. That is, I just thought of symbols as a way of interning strings (like Smalltalk, Objc, Ruby, etc.) ES6 Symbols reminded me of gensym. I didn't expect to see ES6 having the same behaviour as Common Lisp! Thanks for posting.
- nni 11y agoIt is my understanding that symbols were going to be a way to have private methods. This aspect of them - the reason they were going to be introduced in the first place - was dropped, and so you are left with its current limited form with a much narrower use-case. From A Rossberg (he's also the guy behind SoundScript) in a March 2014 stackoverflow post (http://stackoverflow.com/questions/21724326/why-bring-symbols-to-javascript/22280202#22280202 http://stackoverflow.com/questions/21724326/why-bring-symbol...): "Enabling private properties ... was indeed the original motivation for introducing symbols into JavaScript. Unfortunately, however, they ended up being severely downgraded, and not private after all. They are now known as unique symbols, and their only use is to avoid name clashes between properties....Whether that is strong enough a motivation to add symbols to the language is debatable." and from R. Waldron as part of this response (https://esdiscuss.org/topic/proposal-about-private-symbol https://esdiscuss.org/topic/proposal-about-private-symbol) to a proposal about a private symbol (Dec 2014) "Ultimately it was decided that Symbol is just a symbol and that "private" things will be dealt with orthogonally (and at a later date)." YMMV
- braythwayt 11y agoYou can still use them to make almost-completely-private methods and properties, it's just the syntactic sugar that was dropped: http://raganwald.com/2015/06/04/classes-are-expressions.html http://raganwald.com/2015/06/04/classes-are-expressions.html HN discussion: https://news.ycombinator.com/item?id=9660658 https://news.ycombinator.com/item?id=9660658
- BinaryIdiot 11y agoSymbols are odd. Maybe it's just me but they feel like they work "funny" in JavaScript. As the article says you can't implicitly convert a symbol's description to string. Symbol is now the only native object in JavaScript that has this behavior. var str = "something" + "str"; // Works var num = "something" + 5; // Works var func = "something" + function () { }; // Works var obj = "something" + { some: "test" }; // Works var bool = "something" + true; // Works var dt = "something" + Date.now(); // Works var und = "something" + undefined; // Works var nul = "something" + null; // Works var nan = "something" + NaN; // Works var sym = "something" + Symbol("test"); // Throws TypeError Another thing, which is more of a style thing in my opinion, is you can actually define a property with a Symbol which just seems awkward to me. I mean sure you can use it as a property by design so why wouldn't you be able to use defineProperty? I always felt those should be public types of properties where you can add additional logic where necessary. var obj = { }; Object.defineProperty(obj, Symbol("MyProp"), { get: function () { return 15; } }); I feel like almost the same thing with Symbol could be accomplished with a simple UUID generator. It's a neat little thing it just feels awkward to me with how JavaScript works.
- jkrems 11y agoThe other funny thing is: ES6 introduced `Map`, `Set`, and the `class` keyword. All of them throw if you try to create a new instance without `new`. It also introduced `Symbol`, which throws if you do use `new`.