Why can't I add properties to a string object in javascript? - javascript

I inherited some javascript code another developer wrote. He didn't like the grid component we used throughout the project, so he decided to write his own. The grid he wrote can't sort dates, because it can only bind to strings / numbers. He converts all dates to strings before using them. I looked at the string formatting of date function he wrote, and figured I could just add a date property to the string with the original value, and then when sorting see if the string has a date property and sort based on that. However, it seems like you can't add properties to strings in javascript. I wasn't aware there were certain types you can't add properties to. For example:
<html>
<script>
var test = "test";
test.test = "test inner";
console.log(test);
console.log(test.test);
</script>
test.test will be undefined. Weird. My question is why this code doesn't work? And also, if you can think of any workarounds for sorting dates on that grid (besides actually binding to date objects instead of strings, which would be a pain to fix,) that would be really helpful.

There are 8 language types in JavaScript:
7 primitive types: Undefined, Null, Boolean, Number, BigInt, String, and Symbol
1 non-primitive type: Object
Values of the primitive types are called primitive values and they cannot have properties.
Values of the Object non-primitive type are called objects an they can have properties.
When you try to assign a property named 'bar' to a variable foo, like so:
foo.bar = 'abc';
then the result will depend on the type of the value of foo:
(a) if the value of foo is of the type Undefined or Null, then an error will be thrown,
(b) if the value of foo is of the type Object, then a named property 'bar' will be defined on the object foo (if necessary), and its value will be set to 'abc',
(c) if the value of foo is of any other type, then a TypeError will be thrown in strict mode: “can't assign to property "bar" on foo: not an object”. In loose mode, the above assignment operation will be a no op. In either case, the variable foo will not be changed in any way.
So, as you can see, assigning properties to variables only makes sense if those variables are objects. If that is not the case, then the assignment will either do nothing at all, or even throw an error.
In your case, the variable test contains a value of the type String, so this:
test.test = "test inner";
does nothing at all.
However, since ES5 introduced accessor properties, there is an exception to what I've said above. Accessor properties allow us to define functions which are invoked whenever the property is either retrieved or set.
For instance:
var str = '';
str.prop;
Here str is a variable holding a String value. Therefore, accessing a property of that variable should be a no-op (str.prop merely returns undefined). This is true with one exception: if String.prototype contains a accessor property 'prop' with a defined getter, then that getter will be invoked.
So, if this is defined:
Object.defineProperty( String.prototype, 'prop', {
get: function () {
// this function is the getter
}
});
then this
str.prop;
will invoke that getter function. This also works in strict mode.
Live demo: http://jsfiddle.net/fmNgu/
However, I don't think that adding accessor properties to the built-in prototypes would be a good practice.

If you use a String object you can add properties:
var test = new String("test");
test.test = "test inner";
console.log(test.toString()); // prints out "test"
console.log(test.test); // prints out "test inner"

Related

How can I add a method to a string, using it's prototype when it is immutable?

String is immutable which means we can only read its property, not modify or create or delete any of the property or method. So basically string is freeze.
My question is how is it possible to add a new method to the string prototype?
var a = '';
String.prototype.hi = function(){
console.log('hi');
}
a.hi()
Output: hi
Why is it not throwing any error?
String is immutable which means we can only read its property, not modify or create or delete any of the property or method
This is not true. By strings being immutable we mean that the string object itself is freezed. This doesn't mean that String.prototype, which is a separate object, is freezed. When we add properties to the String.prototype object, that doesn't mean we've mutated any strings. It's just another object not the string itself.
String in Javascript is immutable, it is a true statement. So, you are correct.
So, the value of the string is immutable but not it's prototype. String inherited the prototype from its parent object. The functions available in the prototype do not modify the value of string, it returns a new instance of string.
When it comes to inheritance, JavaScript only has one construct:
objects. Each object has a private property which holds a link to
another object called its prototype. That prototype object has a
prototype of its own, and so on until an object is reached with null
as its prototype. By definition, null has no prototype, and acts as
the final link in this prototype chain.
The value of string in javascript is loosely coupled with prototype. If you replace the prototype of string with empty object, there will be no change of the value. You will get the exactly same value for the string when you will access it.
About prototype, you've got already enough answers what is it...I gues that you even knew it.
So in your example you are not mutating the string it self, what you've noticed is called primitive wrapper objects
MDN
Example:
var a = 'A';
String.prototype.test = function() {
return this;
}
console.log('a primitive type =>', typeof a)
console.log('a primitive object wrapper type =>', typeof a.test());
Yes, It is possible to add new functions to String prototype as like for other objects.
Though it is worth looking at this information present on JavaScript|MDN String
Just remove invocation from your code.
Code:
var a = '';
String.prototype.hi = function(){
console.log('hi');
}
a.hi();

Assigning a function to a member of a number, why is that not a failure?

From my Chrome interpreter:
a = 3; // OK, of course.
a.f = function() { return 4; }; // OK. To a number? Ok
a; // Prints 3
a.f(); // f is not a function.
a.f; // Undefined
Of course, a is not an object, and I cannot assign new members to something that isn't an object. But, why does the interpreter swallows the a.f assignment if after that the method or the member doesn't even exist?
If you look at 8.7.2 of the ECMA 5.1, you will notice this note at the bottom:
The object that may be created in step 1 is not accessible outside of the above method. An implementation might choose to avoid the actual creation of that transient object. The only situations where such an actual property assignment that uses this internal method can have visible effect are when it either invokes an accessor function or is in violation of a Throw predicated error check. When Throw is true any property assignment that would create a new property on the transient object throws an error.
Step 1 is Let O be ToObject(base).
If you look at the ToObject() method in 9.9, you will find these two rows in the table:
Number
Create a new Number object whose [[PrimitiveValue]] internal property is set to the value of the argument. See 15.7 for a description of Number objects.
Object
The result is the input argument (no conversion).
So it looks like when you attempt to set the function on the number, it is actually happening (although as a noop), it just becomes unaccessible after the assignment, due to the assignment happening on an internal transient object. When you do it on a regular object, it returns the actual object, so the assignment makes sense.
There's not really a good reason why it wasn't forbidden, it could easily have been if that was how it was implemented when the language was developed.
But why was it implemented without an error? Because it nicely mirrors what happens when you access a property on a primitive: it does implicitly get cast to an object. It doesn't matter whether you use the property reference for assignment or for retrieval, there's an object involved. The assignment will work as usual, even if the coerced object is thrown away immediately afterwards.
We even can see that when we provide an inherited accessor for that object on its prototype:
"use strict";
Object.defineProperty(Number.prototype, "f", {
set: function(value) {
console.log(typeof this+" "+this+" got "+value+" assigned to .f");
},
get: function() {
console.log(typeof this+" "+this+"'s .f property was accessed");
return function() {
console.log("method called on "+typeof this+" "+this);
};
},
configurable: true
});
(3).f = "test value";
(3).f();

Are objects like String and Number, etc...considered "constructor functions"?

I've been reading tutorials for a few weeks now and I've just figured out that when I use the prototype property on a constructor function, the key/value pairs on that prototype property are copied to the proto property of the newly instantiated object.
function F(){};
F.prototype.k = "v";
console.dir(F.k)//logs "undefined"
console.dir(F.prototype.k)//logs "v"
var o = new F;
console.dir(o.k)//logs "v"
So, key "k" is on the prototype property in the constructor and then transfers this to the proto property of newly instantiated object "o" which is why it can be accessed as if it were just a normal key/value pair on the object. Ok, that makes sense to me...but I thought about it and I've seen people use the new keyword for built-in things like String (although this is usually not done)
var s = new String;
the above code is an example of the way a new string item could be created the same way objects can be instantiated based on constructor functions. This made me wonder "is String just a constructor function????!" So I tested this out:
console.dir(String.prototype)
And I got a whole list of exactly the same properties that are attached to s. So, is "String" just a constructor function? The same behavior seems to be true of these items:
console.dir(String.prototype);
console.dir(Number.prototype);
console.dir(Boolean.prototype);
console.dir(Array.prototype);
console.dir(Object.prototype);
console.dir(Function.prototype);
console.dir(Date.prototype);
They all appear to behave exactly the same as constructor functions. They are even all capitalized rather than camelcase. Are they just constructor functions with a few built-in bells and whistles added to them?
There are 6 data types in JavaScript:
Boolean
Number
String
Null
Undefined
Object
The first five are so called primitive types. Three of the primitive types, Boolean, Number and String, also have an equivalent Object implementation.
Whenever you use (Boolean|Number|String) literals, you are creating a value of that corresponding type.
You can also create Boolean, Number and String objects by calling the corresponding constructor function with the new operator:
var s = 'foo'; // type string
var s2 = new String('foo'); // type object
Now, the reason why you can access properties on both of them is that JavaScript auto-boxes primitive values. I.e. when you are trying to access a property on a primitive value, it will temporarily convert the value to an object and access the property.
So
var v = "foo";
alert(v.length);
is essentially
var v = "foo";
alert((new String(v)).length);
If the String, Number or Boolean function is called without the new keyword, they will return a primitive value instead, which makes them act as conversion functions.
And to round this up: Every function has a prototype property, every function can potentially act as a constructor. Whether it really does depends on the implementation of the function.

Defining Functions in Object.create()

I want to create an object just using the Object.create function (without any prototype, first argument as null).
I read somewhere that properties determine the state of an object in JavaScript and JavaScript has three different kinds of properties:
named data properties
named accessor properties
internal properties
So when I define a function in an object, should I always define it as a named accessor property:
var obj = Object.create(null, {
a:{
get:function(){
alert('jQuery nyan!');
}
}
});​
Or should I just define the function as a named data property when it is neither a setter nor a getter? [e.g some jQuery functions that makes changes to the DOM obj]
var obj = Object.create(null, {
a:{
value:function(){
alert('jQuery nyan!');
}
}
});​
Which approach should I take? In terms of performance (speed) and memory management do they have differences? They both appear to work without any exception.
obj.a;
//output: 'jQuery nyan!'
To make it easier to refer to them, let's define them as follows
var objA = Object.create(null, {a: {get: function(){alert('jQuery nyan!');}}});
var objB = Object.create(null, {a: {value:function(){alert('jQuery nyan!');}}});
Now, there is almost no difference between the invocation of objA.a vs objB.a(), except the use of ().
There are some differences, however.
The main difference is that you can't pass parameters to a getter, it is invoked as-is. This means objA.a(1,2,3) will not invoke the function with arugments 1, 2, 3. It will in fact throw an error after invoking, assuming the getter does not return a Function (you're effectively trying to do undefined(1,2,3)).
A second difference requires us to remember Object.create's second parameter takes an object of descriptors, which includes the flag writable (which defaults to false). The difference here is you can not set writable:true on objA.a because "A property cannot both have accessors and be writable or have a value". This means that if you want the method of the getter changed, you must re-define the property, whereas for value you could enable the use of = to change the method associated with the property.
Additionally, with no setter objA.a = <expr> will not perform any action at all.
Normally, you'd only use getters and setters in the following instances, with value as the standard behaviour otherwise;
Lightweight calculating an output
Validating input (to protect an object)
Hiding a variable from direct access
Keeping a standard API where variable or property names may change
If you don't care about compatibility, using getter and setters could be a good approach to replace setNAME()s and getNAME()s. And there is no significant performance gain/loss comparing to function version.
Note that, cause it looks like accessing an variable, instead of calling a function, so the getter/setter function should be very light weight to meet this expectation.
And don't ever use one function for both getter and setter like jQuery does, it's simply very slow. As there is no function signature in javascript, simulate it with if/else will cause lots of performance loss.

Difference between undefined and not being defined in Javascript

See http://jsfiddle.net/FDhQF/1/ for a trivial example.
What's the difference between something being undefined and something not being defined in Javascript? For instance, trying to access a property for an object (effectively, trying to access a variable) that isn't defined will return undefined. But you can also set something = undefined. When you do that, trying to access it still return undefined, but the pointer is still there. An example, as above, is how iterating over an object still goes over the property that you've (re)declared as undefined. It seems like there are two different sorts of undefined. Can anyone shed some light on the situation?
Both, accessing a property that isn't defined on an object and a property that contains the primitive undefined value, will return you undefined.
For example:
var obj = {
a: undefined
};
obj.a; // undefined
obj.b; // undefined
The difference is that a is an own property, and b isn't:
obj.hasOwnProperty('a'); // true
obj.hasOwnProperty('b'); // false
In the first case a is an own property, even if it contains undefined as its value. In the second case, b is not an own property, accessing obj.b will look for a property named b, all way up in the prototype chain.
When the prototype chain ends (when it reaches an object with a null [[Prototype]]), the property lookup ends and undefined is explicitly returned.
You should know that the hasOwnProperty method checks only if the property physically exist on the object (own properties), but we have also inherited properties, for that case we can use the in operator, for example:
function Test () {}
Test.prototype.a = 'foo'; // instances of Test will inherit from Test.prototype
var obj = new Test(); // { a="foo", b="bar"}
obj.b = 'bar';
obj.hasOwnProperty('a'); // false
'a' in obj; // true
obj.a; // 'foo'
obj.hasOwnProperty('b'); // true
As you can see, obj inherits from Test.prototype, and the a property is not an own property, but it is available through the prototype chain. That's why hasOwnProperty returns false and the in operator returns true.
You can see how internally properties are resolved by the [[Get]] internal operation
Notes:
Accessing undefined as an identifier is not considered to be safe on ECMAScript 3 (the most widely implemented version of the language standard), because instead of being a language keyword (such as null for example) is just a property of the global object, and it is writable on this version of the Spec., meaning that if someone replaces its value (e.g. window.undefined = 'LOL';) , it will break your code.
The typeof operator as #strager mentions can be used instead, for example:
if (typeof obj.prop == 'undefined') { /*....*/ }
This operator returns always a string (it's safe to use == :), and its value depends of the type of its operand, the possible values are described here.
Another common way to solve this is to declare your own undefined variable, available on the scope of your functions, for example, some libraries use the following pattern:
(function(undefined) {
// code here
})();
The function has an argument named undefined, and it is executed immediately without passing any value to it (the last pair or parens make the invocation).
Maybe is worth mentioning that the undefined global property was finally described on ECMAScript 5 as non-writable (immutable, as well non-enumerable and non-configurable -non deletable-).
Using the hasOwnProperty method directly from an object instance is also not considered as safe, because if some object has a property with the same name, the original method will be shadowed. For example:
var obj = { hasOwnProperty: function () { /* evil code :) */ } };
If you call:
obj.hasOwnProperty('prop');
The method defined on the object will be executed (and you wouldn't want this since you know exactly which method you want to invoke...), because it shadows the one from the Object.prototype, however it can be safely invoked by:
Object.prototype.hasOwnProperty.call(obj, 'prop');
Here is a good explanation of "undefined". The gist however is that setting something to "undefined" is not UN-defining it, because "undefined" is actually a primitive value, which is used when a variable (or property) has not been assigned a value.

Categories