In the following example:
var tester = Object.create(null);
tester.forename = "Bob";
tester[function () { return "surname"; }] = "Jones";
Is javascript simply converting the function definition to a string to use as the property key?
Is javascript simply converting the function definition to a string to use as the property key?
Yes. Note that the string will not be "surname". In fact, to date, it's not specified what the string will be, and it varies from engine to engine. On some engines, it will be roughly what you have in the source. But it would be perfectly valid (e.g., within the spec) for it to be "dunno, some function". The next spec, ES6, is likely to say that, for Function#toString:
An implementation-dependent String source code representation of the this object is returned. This
representation has the syntax of a FunctionDeclaration FunctionExpression, GeneratorDeclaration,
GeneratorExpession, ClassDeclaration, ClassExpression, ArrowFunction, MethodDefinition, or GeneratorMethod
depending upon the actual characteristics of the object. In particular that the use and placement of white space, line
terminators, and semicolons within the representation String is implementation-dependent.
(This is the January 2014 wording.)
But that's the next spec, which isn't final yet, and will probably change a bit as the spec approaches completion (particularly with regard to native functions). As of the current spec, Function#toString can return anything it likes, and it need not vary from function to function.
Yes. The property names of objects are always strings.
If you attempt to use a different type, it will be coerced into string.
If you want them to be other types, you can use ES6 Map.
Related
As per this documentation,
The string representations of each of these objects are appended
together in the order listed and output.
Also as per answer
The + x coerces the object x into a string, which is just [object
Object]:
So, my question is
If I do
str = new String("hello")
console.log(str) //prints the string object but not 'hello'
console.log(""+str) //prints "hello"
So, in first case, it simply prints the object (doesn't invoke the toString() method).
But in second case, it doesn't coerce but simply print the primitive value. Why is that so?
Which method does console.log invokes to print the object?
Please note that - this is not a duplicate of this question.
Console API is not a standard API that is defined in any specification but is something that is implemented across all browsers, so vendors are usually at their liberty to implement in their own fashion as there's no standard spec to define the output of any methods in API.
Unless you check the actual implementation of the Console API for a particular browser, you can never be sure. There's a tracker on GitHub listing the differences between implementation from major browsers.
If you look at the implementation in FF (available here - search for log), it has a comment below
A multi line stringification of an object, designed for use by humans
The actual implementation checks for the type of argument that is passed to log() and based on it's type, it generates a different representation.
Coming to your case, log() prints two different values for strings created using literal notation and strings created using String constructor because they are two different types. As explained here, Strings created using literal notation are called String Primitives and strings created using String constructor are called String Objects.
var str1 = 'test';
var str2 = new String('hello');
typeof str1 // prints "string"
typeof str2 // prints "object"
As the types differ, their string representation differs in the Console API. If you go through the code for FF's Console implementation, the last statement is
return " " + aThing.toString() + "\n";
So to answer your question, Console API in FF calls toString() on the argument only if the argument type is not one of {undefined,null,object,set,map} types. It doesn't always call toString() or valueOf() methods. I didn't check the implementation of Chrome, so I won't comment on that.
It does not utilize toString, you can do something like this
clog = function(msg){console.log(msg.toString());}
clog(myObj);
This is more typing but will invoke obj.toString() as well:
console.log(`${obj}`);
console.log(str) calls str.valueOf() I guess.
From JavaScript- The Definitive Guide
Its job is to convert an object to a primitive value. The valueOf() method is invoked automatically when an object is used in a numeric context, with arithmetic operators (other than +) and with the relational operators, for example. Most objects do not have a reasonable primitive representation and do not define this method.
---edit----Sorry,copy the wrong line, I mean the ""+str,since there's a type converting
Consider the following class declaration:
class A {}
A.prototype.test1 = function test1() { console.log("test1") }
Evaluating the stringified version of test1 produces valid JS, as we can see here:
const a = new A
eval(`(${a.test1})`)() // outputs "test1"
However, if we build our class in a different but fundamentally equivalent way:
class B {
test2() { console.log("test2") }
}
Evaluating the stringified version of test2 fails:
const b = new B
eval(`(${b.test2})`)() // SyntaxError: Unexpected token {
(it was stringified to test2() { console.log("test2") } which is not valid JS except when embedded in a class declaration)
I can understand stringified native functions being not evaluable, for example "".indexOf.toString() will return a string containing [native code] and I accept that.
But isn't there a way to guarantee that Function.prototype.toString() called on user-defined functions (that is, function from which the source code is available) produces valid, evaluable JS?
But isn't there a way to guarantee that Function.prototype.toString() called on user-defined functions (that is, function from which the source code is available) produces valid, evaluable JS?
No. It depends on the type of the function. The specification requires the following:
toString Representation Requirements:
The string representation must have the syntax of a FunctionDeclaration, FunctionExpression, GeneratorDeclaration, GeneratorExpression, AsyncFunctionDeclaration, AsyncFunctionExpression, ClassDeclaration, ClassExpression, ArrowFunction, AsyncArrowFunction, or MethodDefinition depending upon the actual characteristics of the object.
The use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent.
If the object was defined using ECMAScript code and the returned string representation is not in the form of a MethodDefinition or GeneratorMethod then the representation must be such that if the string is evaluated, using eval in a lexical context that is equivalent to the lexical context used to create the original object, it will result in a new functionally equivalent object. In that case the returned source code must not mention freely any variables that were not mentioned freely by the original function's source code, even if these “extra” names were originally in scope.
If the implementation cannot produce a source code string that meets these criteria then it must return a string for which eval will throw a SyntaxError exception.
Since you have a MethodDefinition you get the representation of a method. If you have a function that is neither a MethodDefinition nor a GeneratorMethod, then you can likely get a representation that can be evaluated (third point), but even then, the spec says that implementation should return a representation that throws a syntax error, so ¯\_(ツ)_/¯.
I've noticed ## used in a few pages about new ES6 features, but I don't know what exactly it means (whether it's actually syntax or just some kind of documentation convention). And it's hard to google. Can someone explain it?
## describes what's called a well-known symbol. (Note that it isn't actually valid syntax in JS.) According to the ES6/ES20151 specification:
Well-known symbols are built-in Symbol values that are explicitly referenced by algorithms of this specification. They are typically used as the keys of properties whose values serve as extension points of a specification algorithm. Unless otherwise specified, well-known symbols values are shared by all Code Realms (8.2).
Code Realms refer to different instances of a JavaScript environment. For example, the Code Realm of the root document would be different to that of JavaScript running in an <iframe>.
An example of where it matter what code realm an object comes from is when trying to use instanceof to determine whether an object is an array (hint: it won't work if it's from another frame). To avoid these kinds of issues from popping up with symbols, they are shared so that references to (say) ##toString will work no matter where the object came from.
Some of these are exposed directly through the Symbol constructor, for example, ##toPrimitive is exposed as Symbol.toPrimitive. That can be used to override the value produced when attempting to convert an object to a primitive value, for example:
let a = { [Symbol.toPrimitive]: () => 1 };
console.log(+a); // 1
console.log(a.valueOf()); // (the same object)
console.log(a.toString()); // "[object Object]"
In general, symbols are used to provide unique properties on an object which cannot collide with a random property name, for example:
let a = Symbol();
let foo = { [a]: 1 };
foo[a]; // 1
There is no way to access the value except by getting the symbol from somewhere (though you can get all symbols for an object by calling Object.getOwnPropertySymbols, so they cannot be used to implement private properties or methods).
1: See this es-discuss topic for some discussion about the different names.
I want to know how is the string length of a string calculated in js.
Is is a function call or a class data member.
I want to know what happens when we execute the following code :
a = 'this is a string';
console.log(a.length); // what actually happens at this point?
Also if a do this :
a += ' added something';
console.log(a.length); // at what point is the new length calculated
//and/or updated for the object 'a';
And at last, do I need to store the string length in a temp variable while using a loop over the string or can I directly use the following (which one is faster/processor efficient) :
for(var i=0;i<a.length;i++){
// doing anything here
}
Summing up my question, I want to know the processing behind String.length and which practice is better while looping over strings?
A string is immutable in JavaScript.
a += "somestring" doesn't change the length of a string but makes a new string.
This means there is no "new length", but the length is just part of the definition of the string (more precisely it is stored in the same structure in implementations).
Regarding
for(i=0;i<a.length;i++){ // did you forget the 'var' keyword ?
a not so uncommon practice (if you don't change a) was to optimize it as
for (var i=0, l=a.length; i<l; i++)
in order to avoid the reading of the length but if you compare the performances with modern engines, you'll see this doesn't make the code any faster now.
What you must remember : querying the length of a string is fast because there is no computation. What's a little less fast is building strings (for example with concatenation).
Strings are a primitive type. At least that's what the documentation says. But we can access the length of the string as if we are accessing the property of an object(with the dot notation). Which indicates it's an object, Right?
Turns out, whenever we make a call from the string primitive to some property using the dot notation (for example, say length), the Js engine will take this primitive string and wrap it into an equivalent wrapper object, which is a String object. And then, the .length on that String object returns the length.
Interesting thing to note here is, that when we do something like this, our string still stays the same primitive string during all of this. And a temporary object is created to make our string operation work. Once the required property is fetched, this temporary object is deleted from the memory.
Hope this gives some high level understanding.
I'm answering your first question.
I'm also curious about this puzzle so I did some search myself, ended up finding -
Based on String documentation from Mozilla:
String literals (denoted by double or single quotes) and strings
returned from String calls in a non-constructor context (i.e., without
using the new keyword) are primitive strings. JavaScript automatically
converts primitives to String objects, so that it's possible to use
String object methods for primitive strings. In contexts where a
method is to be invoked on a primitive string or a property lookup
occurs, JavaScript will automatically wrap the string primitive and
call the method or perform the property lookup.
So as I understand, when you use somestring.length, the primitive string will first be wrapped as a String object, and then since the object has its property length, so it's just a internal method call to access and return.
Question is self explanatory. I know it is possible to extend primitive data types such as string but is it possible to overwrite it?
This is a question that has been asked in an interview.
No, you cannot overwrite anything. EcmaScript defines the primitive types Undefined, Null, Boolean, Number, and String; these are internal and will be used regardless of what you are doing (for example overwriting the global String constructor). Type conversion and evaluation of literals does not rely on any public functions but uses only these internal types and the algorithms specified for them.
Of course, if someone does string coercion with String(myval) instead of ""+myval assigning to the global String variable will have an effect on that code. Any internal use would still point to the "old" function.
If you were talking about prototype objects for the primitive types (when used as objects), those are not overwritable as well. You may extend those objects, but as soon as you assign to e.g. Number.prototype you just have lost a reference to the actual, original number protype object. Example spec for The Number constructor:
The [prototype] of the newly constructed object is set to the original Number prototype object, the one that is the initial value of Number.prototype (15.7.3.1)
Yes (edit: almost). Open up a Javascript console (F12 if you're using Chrome) and type
String = function(){alert('bang!')};
You can overwrite (edit: almost) everything in Javascript — even the window global context! evil.js is a library that uses this trick to rewrite many native objects as possible.
Needless to say, this is extremely dangerous. I performed the String remapping code above, and since writing it down I've caused over 520 Javascript errors (and I've seen 'bang' alerted quite a few times). Native objects are used everywhere, and you shouldn't modify these in case 3rd party code relies on them in ways you don't know about. This is one of the reasons Prototype.js lost popularity — because its extension of native objects would often work against the expectations of other code.
Edit: Factually incorrect assertion that absolutely everything could be overwritten, as pointed out in Bergi's answer. Edits made inline.
You can extend prototypes of native types.
String.prototype.moo = function() {
console.log( 'Moo!' )
};
'Cow says'.moo();
>> "Moo!"
However you cannot directly overwrite constructors of built-in types unless you overwrite the reference to the entire object:
String = function() {
console.log( 'Custom function.' )
};
new String( 'Hello!' );
>> "Custom function."
>> String {} // now you've broken your website ;)
...but still:
'Wat?!'
>> "Wat?!" // you can still create strings by typing letters in quotes
So... the answer is "yes but no". You can mess with native types (Number, Date, String...) but you cannot re-define them entirely from scratch. They're a part of JS engine that you're using (most likely native C++ code) and this brings some limitations.
Possible like this but you must always succeed without side effects.Not good practice.
function Array() {
var obj = this;
var ind = 0;
var getNext = function(x) {
obj[ind++] setter = getNext;
if (x) alert("Data stolen from array: " + x.toString());
};
this[ind++] setter = getNext;
}
var a = ["private stuff"];
// alert("Data stolen from array: private stuff");