It's not the setup to a joke, i'm really asking.
Douglas Crockford is fond of saying that in the javascript prototypal object-oriented language there is no need for new.
He explains that new was simply added to give people coming from class-based (i.e. "classical") object oriented programming languages some level of comfort:
JavaScript, We Hardly new Ya
JavaScript is a prototypal language, but it has a new operator that tries to make it look sort of like a classical language. That tends to confuse programmers, leading to some problematic programming patterns.
You never need to use new Object() in JavaScript. Use the object literal {} instead.
Okay, fine:
new bad
{} good
But then commenter Vítor De Araújo pointed out that the two are not the same. He gives an example showing that a string is not like an object:
A string object and a string value are not the same thing:
js> p = "Foo"
Foo
js> p.weight = 42
42
js> p.weight // Returns undefined
js> q = new String("Foo")
Foo
js> q.weight = 42
42
js> q.weight
42
The string value cannot have new properties. The same thing is valid for other types.
What is going on here that an string is not an object? Am i confusing javascript with some other languages, where everything is an object?
"Everything is an object"... that's one of the big misconceptions that exist all around the language.
Not everything is an object, there are what we call primitive values, which are string, number, boolean, null, and undefined.
That's true, a string is a primitive value, but you can access all the methods inherited from String.prototype as if it were an object.
The property accessor operators (the dot and the bracket notation), temporarily convert the string value to a String object, for being able to access those methods, e.g.:
"ab".charAt(1); // "b"
What happens behind the scenes is something like this:
new String("ab").charAt(1); // "b", temporal conversion ToObject
As with the other primitive values, such as Boolean, and Number, there are object wrappers, which are simply objects that contain the primitive value, as in your example:
var strObj = new String("");
strObj.prop = "foo";
typeof strObj; // "object"
typeof strObj.prop; // "string"
While with a primitive:
var strValue = "";
strValue.prop = "foo";
typeof strValue; // "string"
typeof strValue.prop; // "undefined"
And this happens because again, the property accessor on the second line above, creates a new temporal object, as:
var strValue = "";
new String(strValue).prop = "foo"; // a new object which is discarded
//...
The most important difference between a string and an object is that objects must follow this rule for the == operator:
An expression comparing Objects is only true if the operands reference
the same Object.
So, whereas strings have a convenient == that compares the value, you're out of luck when it comes to making any other immutable object type behave like a value type.
(There may be other differences too, but this is the only one that causes JavaScript developers excitement on a daily basis). Examples:
"hello" == "hello"
-> true
new String("hello") == new String("hello") // beware!
-> false
Related
The official documentation as well as tons of articles on the internet say that 'some string' is a primitive value, meaning that it creates a copy each time we assign it to a variable.
However, this question (and answer to it) How to force JavaScript to deep copy a string? demonstrates that actually V8 does not copy a string even on the substr method.
It would also be insane to copy strings every time we pass them into functions and would not make sense. In languages like C#, Java, or Python, the String data type is definitely a reference type.
Furthermore, this link shows the hierarchy and we can see HeapObject after all.
https://thlorenz.com/v8-dox/build/v8-3.25.30/html/d7/da4/classv8_1_1internal_1_1_sliced_string.html
Finally, after inspecting
let copy = someStringInitializedAbove
in Devtools it is clear that a new copy of that string has not been created!
So I am pretty sure that strings are not copied on assignment. But I still do not understand why so many articles like JS Primitives vs Reference say that they are.
Fundamentally, because the specification says so:
string value
primitive value that is a finite ordered sequence of zero or more 16-bit unsigned integer values
The specification also defines that there are String objects, as distinct from primitive strings. (Similarly there are primitive number, boolean, and symbol types, and Number and Boolean and Symbol objects.)
Primitive strings follow all the rules of other primitives. At a language level, they're treated exactly the way primitive numbers and booleans are. For all intents and purposes, they are primitive values. But as you say, it would be insane for a = b to literally make a copy of the string in b and put that copy in a. Implementations don't have to do that because primitive string values are immutable (just like primitive number values). You can't change any characters in a string, you can only create a new string. If strings were mutable, the implementation would have to make a copy when you did a = b (but if they were mutable the spec would be written differently).
Note that primitive strings and String objects really are different things:
const s = "hey";
const o = new String("hey");
// Here, the string `s` refers to is temporarily
// converted to a string object so we can perform an
// object operation on it (setting a property).
s.foo = "bar";
// But that temporary object is never stored anywhere,
// `s` still just contains the primitive, so getting
// the property won't find it:
console.log(s.foo); // undefined
// `o` is a String object, which means it can have properties
o.foo = "bar";
console.log(o.foo); // "bar"
So why have primitive strings? You'd have to ask Brendan Eich (and he's reasonably responsive on Twitter), but I suspect it was so that the definition of the equivalence operators (==, ===, !=, and !==) didn't have to either be something that could be overloaded by an object type for its own purposes, or special-cased for strings.
So why have string objects? Having String objects (and Number objects, and Boolean objects, and Symbol objects) along with rules saying when a temporary object version of a primitive is created make it possible to define methods on primitives. When you do:
console.log("example".toUpperCase());
in specification terms, a String object is created (by the GetValue operation) and then the property toUpperCase is looked up on that object and (in the above) called. Primitive strings therefore get their toUpperCase (and other standard methods) from String.prototype and Object.prototype. But the temporary object that gets created is not accessible to code except in some edge cases,¹ and JavaScript engines can avoid literally creating the object outside of those edge cases. The advantage to that is that new methods can be added to String.prototype and used on primitive strings.
¹ "What edge cases?" I hear you ask. The most common one I can think of is when you've added your own method to String.prototype (or similar) in loose mode code:
Object.defineProperty(String.prototype, "example", {
value() {
console.log(`typeof this: ${typeof this}`);
console.log(`this instance of String: ${this instanceof String}`);
},
writable: true,
configurable: true
});
"foo".example();
// typeof this: object
// this instance of String: true
There, the JavaScript engine was forced to create the String object because this can't be a primitive in loose mode.
Strict mode makes it possible to avoid creating the object, because in strict mode this isn't required to be an object type, it can be a primitive (in this case, a primitive string):
"use strict";
Object.defineProperty(String.prototype, "example", {
value() {
console.log(`typeof this: ${typeof this}`);
console.log(`this instance of String: ${this instanceof String}`);
},
writable: true,
configurable: true
});
"foo".example();
// typeof this: string
// this instanceof String: false
I am trying to understand why the 'keys' method does not return the properties and methods of the String object. In other words what is unique about this object? I tested this theory by creating a generic object, giving it 1 property and 1 method and then running the .keys method on it, and it returned both the property and the method. Since String is an object in Javascript, assumed applying .keys method to it would do the same —at the least returning the .length method in the returned set.
Using Chrome's console I ran the following cases:
typeof String // "function"
"function" == typeof String // true
"object" == typeof String // false
Two notes in addition to my main question:
In the scope of JavaScript:
Is a function not an object?
Aren't most things objects outside primitives and some other special cases?
Some background information
Effectively Object.keys returns a list of own enumerable properties on the Object instance. These are properties belonging only to that instance of the Object class, excluding any prototypically inherited properties and or methods. These are values that would return true in the following examples:
// Create an Object with own properties
var obj = {
foo: 'bar'
}
obj.hasOwnProperty('foo') // => true, brilliant
As many will know, this would still hold true when properties are added after the Object has been constructed. As can be seen in this example:
// Create an Object the retroactively add an enumerable property
var obj = {}
obj.foo = 'bar'
obj.hasOwnProperty('foo') // => true, great
Objects, functions, and arrays all behave this way; whereas strings don't. You can easily see this by trying to add own properties to a string and then reading them back, like this:
var str = 'foo'
str.bar = 'baz'
console.log(str) // => undefined, hmm
// What about "hasOwnProperty"?
str.hasOwnProperty('bar') // => false... too bad
So to answer the question...
Own enumerable properties inherently cannot exist on instances of the String type and cannot be added retrospectively either, because own properties are not assignable to strings, period.
Although this explanation doesn't explain the decisions made whilst implementing this, it certainly gives some underlying sanity as to why String.keys doesn't, or simply can't, exist; and why Object.keys always returns undefined when supplied with a string.
Functions are objects. The defined behavior of typeof is somewhat idiosyncratic. Values in JavaScript are either objects or primitives.
The "keys" property of String instances is undefined because, well, it is not defined on the String prototype. You can add such a property if you like, though working with String instances instead of string primitives is a recipe for lots of weird bugs.
console.log(String)
>function String() { [native code] }
var a = new String()
console.log(a)
>String {length: 0, [[PrimitiveValue]]: ""}
typeof a
>"object"
it should be "class" instead of "function", as like in other languages, but function in js are first-class object (more details here What is meant by 'first class object'?)
object are the instanced classes
In JavaScript a String is a primitive value.
But is also a String object...
A primitive value is a value put directly into a variable.
So my question is:
var d = "foo";
does d contain directly foo or a reference to a string object like other languages?
Thanks.
If I understand it correctly, d will contain the string literal "foo", and not a reference to an object. However, the JavaScript engine will effectively cast the literal to an instance of String when necessary, which is why you can call methods of String.prototype on string literals:
"some string".toUpperCase(); //Method of String.prototype
The following snippet from MDN may help to explain it further (emphasis added):
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 and 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.
This is all explained in detail in the specification, but it's not exactly easy reading. I asked a related question recently (about why it is possible to do the above), so it might be worth reading the (very) detailed answer.
if you define
var d = "foo";
than d contains directly foo
but, if you define
var S = new String("foo");
then S is an Object
Example:
var s1 = "1";
var s2 = "1";
s1 == s2 -> true
var S1 = new String("2");
var S2 = new String("2");
S1 == S2 -> false
I think that every variable in Javascript actually represents an Object. Even a function is an Object.
I found two useful articles detailing this, located here and here. Seems like primitive types in JavaScript are passed by VALUE (i.e. when you pass if to a function it gets "sandboxed" within the function and the original variable's value won't change), while reference types are passed, you guessed it, by REFERENCE and passing it through to a function will change the original variable.
Primitive types in JavaScript are text (string), numeric (float / int), boolean and NULL (and the dreaded "undefined" type). Any custom objects, functions or standard arrays are considered reference types. I haven't researched the Date type though, but I'm sure it will fall into the primitive types.
Found this page about javascript variables, seems that:
Primitive type for javascript are booleans, numbers and text.
I believe there are no primitives in Javascript, in the Java sense at least - everything is an object of some kind.
So yes it is a reference to an object - if you extend the String object, d would have that extension.
If you mean primitives as in those types provided by the language, you've got a few, boolean, numbers, strings and dates are all defined by the language.
MDN states:
primitive, primitive value
A data that is not an object and does
not have any methods. JavaScript has 5
primitive datatypes: string, number,
boolean, null, undefined. With the
exception of null and undefined, all
primitives values have object
equivalents which wrap around the
primitive values, e.g. a String object
wraps around a string primitive. All
primitives are immutable.
So when we call a "s".replace or "s".anything is it equivalent to new String("s").replace and new String("s").anything?
No, string primitives do not have methods. As with numeric primitives, the JavaScript runtime will promote them to full-blown "String" objects when called upon to do so by constructs like:
var space = "hello there".indexOf(" ");
In some languages (well, Java in particular, but I think the term is in common use) it's said that the language "boxes" the primitives in their object wrappers when appropriate. With numbers it's a little more complicated due to the vagaries of the token grammar; you can't just say
var foo = 27.toLocaleString();
because the "." won't be interpreted the way you'd need it to be; however:
var foo = (27).toLocaleString();
works fine. With string primitives — and booleans, for that matter — the grammar isn't ambiguous, so for example:
var foo = true.toString();
will work.
The technically correct answer is "no".
The real-world answer is "no, but it will work anyway". That's because when you do something like
"s".replace()
the interpreter knows that you want to actually operate on the string as if you had created it with
var str = new String("s")
and therefore acts as if you had done that.
This question already has answers here:
How is a Javascript string not an object?
(2 answers)
Closed 7 years ago.
Here are two reasons to think strings are objects. First, you can create a string in the following way:
var mystring = new String("asdf");
I'm under the impression that the constructor function following the new operator has to return an object. Second, strings seem to have properties and methods. For example:
mystring.toUpperCase();
BUT, if strings were objects, then we'd expect something like the following to work:
function string_constructor() {
return "asdf";
}
var mystring = new string_constructor();
But it doesn't, and I've been told it doesn't because strings aren't objects. So are strings objects or not? And, either way, how can I make sense of everything I've listed?
Speaking about language types, Strings are values of the String type.
The language has five primitive types, which are String, Number, Boolean, Null and Undefined.
There are String objects (also for Number or Boolean), they are called primitive wrappers, they are created when you use the constructor function associated with them, for example:
typeof new String('foo'); // "object"
typeof 'foo'; // "string"
But don't get confused with them, you will rarely need to use primitive wrappers, because even if primitive values are not objects, you can still access their inherited properties, for example, on a string, you can access all members of String.prototype, e.g.:
'foo'.indexOf('o'); // 2
That's because the property accessor (the dot in this case) temporarily converts the primitive value to an object, for being able to resolve the indexOf property up in the prototype chain.
About the constructor function you have in your question, as you know, it won't return the string.
Functions called with the new operator return an implicit value, which is a new object that inherits from that function's prototype, for example:
function Test () {
// don't return anything (equivalent to returning undefined)
}
new Test() instanceof Test; // true, an object
If an object is returned from the constructor, that newly created object (this within the constructor) will be lost, so the explicit returned object will come out the function:
function Test2() {
return {foo: 'bar'};
}
new Test2().foo; // 'bar'
But in the case of primitive values, they are just ignored, and the new object from the constructor is implicitly returned (for more details check the [[Construct]] internal operation, (see step 9 and 10)).
In JavaScript, strings come in two flavors:
There is a String language type which contains values like "foo" and 'bar'. Those values are primitive values. Read about the String type here
Then there is a String constructor. (A constructor is a function object which is used to create new instances of a certain "class" (or pseudo-class)). So this: new String("foo")
will create a new object (a value of the type Object), which contains the primitive value "foo". Read about the String constructor here
In practice you don't use the new String('foo') notation, but the string literal notation 'foo'.
So to answer your question:
In JavaScript, strings are not objects. They are primitive values. However, there exist String objects which can be used to store string values, but those String objects are not used in practice.
Primitive strings behaves like objects in JavaScript because they are automatically converted to objects when you call an object method:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String
String objects may be created by
calling the constructor new String().
The String object wraps JavaScript's
string primitive data type with the
methods described below. The global
function String() can also be called
without new in front to create a
primitive string. String literals in
JavaScript are primitive strings.
Because JavaScript automatically
converts between string primitives and
String objects, you can call any of
the methods of the String object on a
string primitive. JavaScript
automatically converts the string
primitive to a temporary String
object, calls the method, then
discards the temporary String object.
For example, you can use the
String.length property on a string
primitive created from a string
literal
String are both primitive and object type. Think about int, float, char ... which have real Object classes like Integer, Float and Char in Java.
String is a wrapper around the primitive string datatype. When you do something like var s = "My String" then a String wrapper object is created behind the scenes when needed..
It is interesting however that typeof s = 'string' instead of 'object'. Anybody know why that is?
Strings are objects, but what you are doing in your example is not creating a new String. By using 'new' you are creating an instance of a Object, not a 'string'
var F = function() {
return 'c';
};
var a = new String('a');
var b = 'b';
var c = new F();
alert("a is: " + typeof a + ", b is: " + typeof b + ", c is: " + typeof c);
// alerts: a is: object, b is: string. c is: object
You shouldn't use 'new' for strings regardless (or arrays, or 'simple' objects.)