Method Added to JavaScript Object Class Appears when Traversing through Given Object - javascript

In my journey to become stronger in JavaScript, I have taken up a challenge to add a method, myMethod(), to the JavaScript Object class that can be called on a given Object, myObject, in order to manipulate its contents.
I have gotten as far as adding myMethod() to the Object prototype and traversing through myObject via myObject.myMethod(); however, I'm running into a curious problem: at the end of the given object, myMethod() is printed as an value of myObject even though, to my knowledge, it should not be.
Object.prototype.myMethod = function()
{
for (var key in this)
{
console.log(this[key]);
}
}
function processData(input)
{
//Enter your code here
var myObject = JSON.parse("{ \"myParam\": \"myValue\", \"anotherParam\": 123 }");
myObject.myMethod();
}
The following is the output of this code:
myValue
123
[Function]
Changing myObject[key] to myObject[key].toString() has the following output:
myValue
123
function ()
{
for (var key in this)
{
console.log(this[key].toString());
}
}
I am executing this script in WebStorm using a Node.js runtime.
The requirements of this challenge only calls for the contents of myObject, and I can't figure out how to stop myMethod() from showing up here. Is there something I'm missing?

In my journey to become stronger in JavaScript, I have taken up a challenge to add a method, myMethod(), to the JavaScript Object class that can be called on a given Object, myObject, in order to manipulate its contents.
Beware that this practice is seriously frowned-upon (here in 2017, possibly more than it strictly necessary*). Extending any built-in prototype is a bit fraught, but extending Object.prototype is particularly problematic since, of course, almost all objects inherit from it.
But if you're going to extend a prototype, it's important to add a non-enumerable property via Object.defineProperty or similar:
Object.defineProperty(Object.prototype, "myMethod", {
value: function() { /* ... */ }
});
The default for enumerable is false, so it won't show up in for-in loops, Object.keys arrays, etc.
* The "don't extend built-in prototypes" mantra started before ES5's introduction of a way of defining non-enumerable properties (e.g., defineProperty). Now that we can define non-enumerable properties, the chief concern is inter-script conflict, each trying to use the same name for something, or conflict with new features as they're added to JavaScript.

Use Object.defineProperty().
The Object class is used in a lot of libraries and frameworks and therefore is best left alone, but can be useful for things such as poly-fills for browser compatability.
if (!Object.prototype.myMethod) {
Object.defineProperty(Object.prototype, 'myMethod', {
value: function() { /* customer code here */ }
})
}

Related

Extending {} (an Object) in Typescript

So I am trying to extend the object class. The reason why is because I want to write a function called isEqual
isEqual(object: any): boolean {
return JSON.stringify(this) === JSON.stringify(object);
}
I tried to extend Object, but there is no generic type.
I know I could just use a function that takes two arguments and checks it. But I want to learn how to do this in Typescript.
I read on StackOverflow that operator overloading is not possible (because originally I want to use the == operator to call the function above).
Could someone help me?
You are looking to add an isEqual() method to all instances of the global Object type. In JavaScript this can be done by adding the method to Object.prototype and all instances of objects will inherit the method automatically.
Before I say how to do it, it is almost universally considered a bad idea to modify native prototypes like that. See Why is extending native objects a bad practice? for the official SO answer about that. But briefly: Think of Object.prototype as common property that needs to be shared by everyone. If you start making changes to it, and other code doesn't expect those changes, then that code can fail to run properly. Conversely, some other code could potentially make changes that make your changes fail to run properly.
The naïve way of adding a method to Object.prototype, like Object.prototype.isEqual = ... or Object.defineProperty(Object.prototype, 'isEqual', {value(...){...}}) will end up putting isEqual as an enumerable property of all objects, meaning that a for...in loop would suddenly show "isEqual" as a key for every possible object everywhere.
The official advice here is don't do it.
If you still want to do it (e.g., maybe you are doing this with code nobody else will need to interact with, so the only person potentially harmed by this is you), then here's how. TypeScript supports declaration merging to allow additions to existing interfaces:
interface Object {
isEqual(object: object): boolean;
}
If you write that, the compiler will now expect that all Objects have an isEqual() method. (If your code is in a module, you will need to wrap that in a declare global { } block, known as global augmentation).
Of course you still have to implement it:
Object.defineProperty(Object.prototype, 'isEqual', {
value(object: object) {
return JSON.stringify(this) === JSON.stringify(object);
},
configurable: true,
enumerable: false // <-- we don't want to mess with for...in loops
});
Here I've used the Object.defineProperty() method so that we can explicitly prevent the method name from being enumerable.
And now you can verify that it works as desired:
interface Foo { a: number, b: string };
const o1: Foo = { a: 1, b: "two" };
const o2: Foo = { b: "two", a: 1 };
console.log(o1.isEqual(o2)); // false
The o1.isEqual(o2) compiles with no error, and when you run it, it uses your isEqual() method, and returns false as expected. (You did expect false, right? See sort object properties and JSON.stringify if you didn't.)
Playground link to code

How can I make a sub-function for a pre-existing data type? [duplicate]

I was working on an AJAX-enabled asp.net application.
I've just added some methods to Array.prototype like
Array.prototype.doSomething = function(){
...
}
This solution worked for me, being possible reuse code in a 'pretty' way.
But when I've tested it working with the entire page, I had problems..
We had some custom ajax extenders, and they started to behave as the unexpected: some controls displayed 'undefined' around its content or value.
What could be the cause for that? Am I missing something about modifing the prototype of standart objects?
Note: I'm pretty sure that the error begins when I modify the prototype for Array. It should be only compatible with IE.
While the potential for clashing with other bits o' code the override a function on a prototype is still a risk, if you want to do this with modern versions of JavaScript, you can use the Object.defineProperty method, e.g.
// functional sort
Object.defineProperty(Array.prototype, 'sortf', {
value: function(compare) { return [].concat(this).sort(compare); }
});
Modifying the built-in object prototypes is a bad idea in general, because it always has the potential to clash with code from other vendors or libraries that loads on the same page.
In the case of the Array object prototype, it is an especially bad idea, because it has the potential to interfere with any piece of code that iterates over the members of any array, for instance with for .. in.
To illustrate using an example (borrowed from here):
Array.prototype.foo = 1;
// somewhere deep in other javascript code...
var a = [1,2,3,4,5];
for (x in a){
// Now foo is a part of EVERY array and
// will show up here as a value of 'x'
}
Unfortunately, the existence of questionable code that does this has made it necessary to also avoid using plain for..in for array iteration, at least if you want maximum portability, just to guard against cases where some other nuisance code has modified the Array prototype. So you really need to do both: you should avoid plain for..in in case some n00b has modified the Array prototype, and you should avoid modifying the Array prototype so you don't mess up any code that uses plain for..in to iterate over arrays.
It would be better for you to create your own type of object constructor complete with doSomething function, rather than extending the built-in Array.
What about Object.defineProperty?
There now exists Object.defineProperty as a general way of extending object prototypes without the new properties being enumerable, though this still doesn't justify extending the built-in types, because even besides for..in there is still the potential for conflicts with other scripts. Consider someone using two Javascript frameworks that both try to extend the Array in a similar way and pick the same method name. Or, consider someone forking your code and then putting both the original and forked versions on the same page. Will the custom enhancements to the Array object still work?
This is the reality with Javascript, and why you should avoid modifying the prototypes of built-in types, even with Object.defineProperty. Define your own types with your own constructors.
There is a caution! Maybe you did that: fiddle demo
Let us say an array and a method foo which return first element:
var myArray = ["apple","ball","cat"];
foo(myArray) // <- 'apple'
function foo(array){
return array[0]
}
The above is okay because the functions are uplifted to the top during interpretation time.
But, this DOES NOT work: (Because the prototype is not defined)
myArray.foo() // <- 'undefined function foo'
Array.prototype.foo = function(){
return this[0]
}
For this to work, simply define prototypes at the top:
Array.prototype.foo = function(){
return this[0]
}
myArray.foo() // <- 'apple'
And YES! You can override prototypes!!! It is ALLOWED. You can even define your own own add method for Arrays.
You augmented generic types so to speak. You've probably overwritten some other lib's functionality and that's why it stopped working.
Suppose that some lib you're using extends Array with function Array.remove(). After the lib has loaded, you also add remove() to Array's prototype but with your own functionality. When lib will call your function it will probably work in a different way as expected and break it's execution... That's what's happening here.
Using Recursion
function forEachWithBreak(someArray, fn){
let breakFlag = false
function breakFn(){
breakFlag = true
}
function loop(indexIntoSomeArray){
if(!breakFlag && indexIntoSomeArray<someArray.length){
fn(someArray[indexIntoSomeArray],breakFn)
loop(indexIntoSomeArray+1)
}
}
loop(0)
}
Test 1 ... break is not called
forEachWithBreak(["a","b","c","d","e","f","g"], function(element, breakFn){
console.log(element)
})
Produces
a
b
c
d
e
f
g
Test 2 ... break is called after element c
forEachWithBreak(["a","b","c","d","e","f","g"], function(element, breakFn){
console.log(element)
if(element =="c"){breakFn()}
})
Produces
a
b
c
There are 2 problems (as mentioned above)
It's enumerable (i.e. will be seen in for .. in)
Potential clashes (js, yourself, third party, etc.)
To solve these 2 problems we will:
Use Object.defineProperty
Give a unique id for our methods
const arrayMethods = {
doSomething: "uuid() - a real function"
}
Object.defineProperty(Array.prototype, arrayMethods.doSomething, {
value() {
// Your code, log as an example
this.forEach(v => console.log(v))
}
})
const arr = [1, 2, 3]
arr[arrayMethods.doSomething]() // 1, 2, 3
The syntax is a bit weird but it's nice if you want to chain methods (just don't forget to return this):
arr
.map(x=>x+1)
[arrayMethods.log]()
.map(x=>x+1)
[arrayMethods.log]()
In general messing with the core javascript objects is a bad idea. You never know what any third party libraries might be expecting and changing the core objects in javascript changes them for everything.
If you use Prototype it's especially bad because prototype messes with the global scope as well and it's hard to tell if you are going to collide or not. Actually modifying core parts of any language is usually a bad idea even in javascript.
(lisp might be the small exception there)

How to get object itself in custom Object.prototype.xxx function?

Object.prototype.getB = function() {
// how to get the current value a
return a.b;
};
const a = {b: 'c'};
a.getB();
As you can see, I want to make a function to all Object value. And I need to get the object value in this function then do something.
Monkey Patching
What you want to do is called monkey patching — you mutate a built-in prototype.
There are many wrong ways to do it, and it should be avoided entirely, because it has negative Web compatibility impacts.
I will, however, demonstrate how this can be done in a way that matches existing prototype features most closely.
In your case, the function body should return this.b.
In functions called as methods, you can get the object itself with the this keyword.
See How does the "this" keyword work? (section 4: “Entering function code”, subsection “Function properties”) for more details.
You correctly added the method to Object.prototype.
See Inheritance and the prototype chain for more details.
The tools
There is a number of tools involved when reasoning about monkey-patching:
Checking own property existence
Using property descriptors
Using the correct function kind
Getters and setters
Let’s assume you’re trying to implement theMethod on TheClass.
1. Checking own property existence
Depending on your use case you may want to check if the method you want to introduce already exists.
You can do that with Object.hasOwn; this is quite a new method, but in older environments it can simply be replaced by Object.prototype.hasOwnProperty.call.
Alternatively, use hasOwnProperty normally, but be aware that if you or someone else monkey-patched the hasOwnProperty method itself, this may lead to incorrect results.
Note that in does not check for own properties, exclusively, but for inherited properties as well, which isn’t (necessarily) what you want when you’re about to create an own property on an object.
Also, note that if(TheClass.prototype.theMethod) is not a property existence check; it’s a truthiness check.
Code samples
if(Object.hasOwn(TheClass.prototype, "theMethod")){
// Define the method.
}
if(Object.prototype.hasOwnProperty.call(TheClass.prototype, "theMethod")){
// Define the method.
}
if(TheClass.prototype.hasOwnProperty("theMethod")){
// Define the method.
}
2. Using property descriptors
You can choose the property descriptor however you like, but existing methods are writable, configurable, and non-enumerable (the last of which is the default when using defineProperty).
defineProperties can be used to define multiple properties in one go.
When simply assigning a property using =, the property becomes writable, configurable, and enumerable.
Code samples
// Define the method:
Object.defineProperty(TheClass.prototype, "theMethod", {
writable: true,
configurable: true,
value: function(){}
});
// Define the method:
Object.defineProperties(TheClass.prototype, {
theMethod: {
writable: true,
configurable: true,
value: function(){}
}
});
3. Using the correct function kind
JavaScript has four major kinds of functions which have different use cases.
“Is invokable” means that it can be called without new and “Is constructable” means that it can be called with new.
Function kind
Example
Is invokable
Is constructable
Has this binding
Arrow function
() => {}
Yes
No
No
Method
({ method(){} }).method
Yes
No
Yes
Class
(class{})
No
Yes
Yes
function function
(function(){})
Yes
Yes
Yes
What we’re looking for is a method that can be called (without new) and has its own this binding.
We could use functions, but, looking at existing methods, not only is new "HELLO".charAt(); strange, it also doesn’t work!
So the method should also not be constructable.
Therefore proper Methods are what we’re looking for.
Note that this obviously depends on your use case.
For example, if you want a constructable function, by all means, use a class instead.
Code sample
We go back to the previous code sample and instead of function(){} use a method definition.
// Define the method:
Object.defineProperty(TheClass.prototype, "theMethod", {
writable: true,
configurable: true,
value: {
theMethod(){
// Do the thing.
}
}.theMethod
});
Why bother with 2. and 3.?
The goal of the enumerability and constructability considerations is to create something that has the same “look and feel” as existing, built-in methods.
The difference between those and a naive implementation can be demonstrated using this snippet:
class TheClass{}
TheClass.prototype.theMethod = function(){};
Let’s compare this to a different, built-in method, like String.prototype.charAt:
Code snippet
Naive example
Built-in example
thePrototype
Is TheClass.prototype
Is String.prototype
theMethod
Is TheClass.prototype.theMethod
Is String.prototype.charAt
for(const p in thePrototype){ console.log(p); }
"theMethod" will be logged at some point.
"charAt" will never be logged.
new theMethod
Creates an instance of theMethod.
TypeError: theMethod is not a constructor.
Using the tools from subsections 2 and 3 make it possible to create methods that behave more like built-in methods.
4. Getters and setters
An alternative is to use a getter. Consider this:
const arr = [
"a",
"b",
"c",
];
console.log(arr.indexOfB); // 1
How would an indexOfB getter on the Array prototype look like?
We can’t use the above approach and replace value by get, or else we’ll get:
TypeError: property descriptors must not specify a value or be writable when a getter or setter has been specified
The property writable needs to be removed entirely from the descriptor.
Now value can be replaced by get:
Object.defineProperty(Array.prototype, "indexOfB", {
configurable: true,
get: {
indexOfB(){
return this.indexOf("b");
}
}.indexOfB
});
A setter can also be specified by adding a set property to the descriptor:
Object.defineProperty(Array.prototype, "indexOfB", {
configurable: true,
get: {
indexOfB(){
return this.indexOf("b");
}
}.indexOfB,
set: {
indexOfB(newValue){
// `newValue` is the assigned value.
// Use `this` for the current Array instance.
// No `return` necessary.
}
}.indexOfB
});
Web compatibility impact
There are a few reasons why anyone would want to extend built-in prototypes:
You got a brilliant idea yourself for a new feature to be added to all instances of whatever class you’re extending, or
You want to backport an existing, specified feature to older browsers.
If you extend the target object, there are two options to consider:
If the property doesn’t exist, supply it, otherwise, leave the existing property in place, or
Always replace the property with your own implementation, no matter if it exists or not.
All approaches mostly have disadvantages:
If you or someone else invented their own feature, but a standard method comes along which has the same name, then these features are almost guaranteed to be incompatible.
If you or someone else try to implement a standard feature in order to backport it to browsers that don’t support it, but don’t read the specification and “guess” how it works, then the polyfilled feature is almost guaranteed to be incompatible with the standard implementation.
Even if the spec is followed closely, who will make sure to keep up with spec changes?
Who will account for possible errors in the implementation?
If you or someone else choose to check if the feature exists before overriding it, then there’s a chance that as soon as someone with a browser which supports the feature visits your page, suddenly everything breaks because the implementations turn out to be incompatible.
If you or someone else choose to override the feature regardless, then at least the implementations are consistent, but then migration to the standard feature may be difficult.
If you write a library that is used a lot in other software, then the migration cost becomes so large that the standard itself has to change; this is why Array.prototype.contains had to be renamed to Array.prototype.includes[Reddit] [ESDiscuss] [Bugzilla] [MooTools], and Array.prototype.flatten could not be used and had to be named Array.prototype.flat instead[Pull request 1] [Pull request 2] [Bugzilla].
In other words, this is the reason we can’t have nice things.
Also, your library may not be interoperable with other libraries.
Alternatives
The simplest alternative is to define your own plain function:
const theMethod = (object) => object.theProperty; // Or whatever.
const theProperty = theMethod(theObject);
You could also consider a Proxy.
This way you can dynamically query the property and respond to it.
Let’s say you have an object with properties a through z and you want to implement methods getA through getZ:
const theProxiedObject = new Proxy(theObject, {
get(target, property, receiver){
const letter = property.match(/^get(?<letter>[A-Z])$/)?.groups?.letter.toLowerCase();
if(letter){
return () => target[letter];
}
return Reflect.get(target, property, receiver);
}
});
console.assert(theProxiedObject.getB() === theProxiedObject.b);
You could also extend your object’s prototype using another class, and use this instead:
class GetterOfB extends Object{
b;
constructor(init){
super();
Object.assign(this, init);
}
getB(){
return this.b;
}
}
const theObject = new GetterOfB({
b: "c"
});
const theB = theObject.getB();
All you have to keep in mind is to not modify things you didn’t define yourself.

Does Chrome retain each object's constructor?

In Chrome's JavaScript console:
> function create(proto) {
function Created() {}
Created.prototype = proto
return new Created
}
undefined
> cc = create()
Created {}
> cc
Created {}
Created is a function private to the create function; after create completes, there are no (known to me) references to Created. Yet Chrome can show the function's name at any time, starting from the object created by it.
Chrome didn't achieve this by following the "naïve" approach:
> cc.constructor
function Object() { [native code] }
> cc.toString()
"object [Object]"
and anyway, I didn't set constructor on the proto argument passed to create:
> cc.__proto__.hasOwnProperty("constructor")
false
One guess I had is that the JavaScript VM holds on to Created for the sake of the instanceof mechanism. It is said that instanceof
tests whether an object has in its prototype chain the prototype property of a constructor.
But in the above code I typed create(), effectively passing undefined as prototype; consequently Created doesn't even have its prototype set to the actual cc.__proto__. We can verify this if we hack create to expose the Created function:
function create(proto) {
function Created() {}
Created.prototype = proto
GlobalCreated = Created
return new Created
}
now let's type
> cc = create()
Created {}
> GlobalCreated
function Created() {}
> GlobalCreated.prototype
undefined
> cc instanceof GlobalCreated
TypeError: Function has non-object prototype 'undefined' in instanceof check
My questions (all closely related):
What exactly does Chrome's JavaScript engine retain to make that object presentation in the console work? Is it the constructor function, or just the function name?
Is that retention needed for anything more substantial than console printout?
What is the effect of such retention on memory consumption? What if, for example, the constructor function (or even its name) is abnormally huge?
Is it just Chrome? I've retested with Firebug and Safari, their consoles don't present the object that way. But do they still retain the same data, for other possible purposes (e.g. due to a genuine concern inherent to a JavaScript VM)?
Late edit:
I recently revisited this question/answer, and I think I've figured out why chrome seems to "hang on" to the Created name. It's not really something that is exclusive to V8, but I think it's the result of how V8 works behind the scenes (the hidden objects I explained in my initial answer), and what V8 is required to do (to conform to the ECMAScript standard).
Any function, constructor functions or otherwise, share the same constructor and prototype-chain by default:
function Created(){};
console.log(Created.constructor);//function Function() { [native code] }
console.log(Object.getPrototypeOf(Created));//function Empty() {}
console.log(Created.__proto__);//same as above
console.log(Created.prototype);//Created {}
This tells us a few things: All functions share the native Function constructor, and inherit from a specific function instance (function Empty(){}) that is used as their prototype. However, a function's prototype property is required to be an object, that the function would return if it were called as a constructor (see ECMAScript standard).
The value of the prototype property is used to initialise the [[Prototype]] internal property of a newly created object before the Function object is invoked as a constructor for that newly created object. This property has the attribute { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }.
We can verify this easily by looking at the Created.prototype.constructor:
console.log(Created.prototype.constructor);//function Created() {}
Now let's, for a moment, list the hidden classes V8 needs to, and probably will, create in order for it to comply to the standard:
function Created(){}
Hidden classes:
Object, of course: the mother of all objects, of which Function is a specific child
Function: This native object is, as we've demonstrated, the constructor
function Empty: The prototype, from which our function will inherit
Created our empty function that will inherit from all of the above
At this stage, nothing unusual has happened, and it's self-evident that, when we return an instance of this Created constructor, the Created function will get exposed because of its prototype.
Now, because we're reassigning the prototype property you could argue that this instance will be discarded, and is lost, but from what I understand, that's not how V8 will handle this situation. Instead, it'll create an additional hidden class, that simply overrides the prototype property of its parent after this statement is encountered:
Created.prototype = proto;
Its internal structure will end up looking something like this (numbered this time, because I'll refer back to certain stages within this inheritance chain further down):
Object, of course: the mother of all objects, of which Function is a specific child
Function: This native object is, as we've demonstrated, the constructor
function Empty: The prototype, from which our function will inherit
Created our empty function that will inherit from all of the above
Created2: extends the previous class (Created), and overrides prototype
So why is Created still visible?
That's the million dollar question, to which I think I have the answer now: Optimization
V8 simply can't, nor should it be allowed to, optimize out the Created hidden class (stage 4). Why? Because what will override prototype is an argument. It's something that can't be predicted. What V8 will probably do to optimize the code is to store a hidden object 4, and whenever the create function is called, it'll create a new hidden class that extends stage 4, overriding the prototype property with whatever value is passed to the function.
Because of this, Created.prototype will always exist somewhere inside each instance's internal representation. It's also important to note you could replace the prototype property with one that actually referenced an instance of Created (with a mucked-up prototype chain, but still):
cc = create();
console.log(Object.getPrototypeOf(cc))//Object {}
cc = create(new GlobalCreated);
console.log(Object.getPrototypeOf(cc));//Created {}
How's that for a mind-bender? Inception script-writers, eat your hearts out...
Anyway, I hope all of this dribble made some sense to someone out here, if not, I do respond to comments, so corrections to mistakes I may have made, or questions regarding some part of this update that is a bit unclear are welcome...
I'll try to answer question by question, but as you say, they're all closely related, so the answers overlap up to a point.
While reading this, bare in mind that I wrote this in one go, whilst feeling a bit feverish. I am not a V8 expert, and based this on recollections of my doing some digging in the V8 internals some time ago. The link at the bottom is to the official docs, and will of course contain more accurate and up-to-date information on the subject.
What is going on
What chrome's V8 engine actually does is create a hidden class for each object, and this class is mapped to the JS representation of the object.
Or as the people at google say so themselves:
To reduce the time required to access JavaScript properties, V8 does not use dynamic lookup to access properties. Instead, V8 dynamically creates hidden classes behind the scenes.
What happens in your case, extending, creating a new constructor from a particular instance and overriding the constructor property is actually nothing more than what you can see on this graph:
Where hidden class C0 could be regarded as the standard Object class. Basically, V8 interprets your code, builds a set of C++ like classes, and creates an instance if needed. The JS objects you have are set to point to the different instances whenever you change/add a property.
In your create function, this is -very likely- what is going on:
function create(proto)
{//^ creates a new instance of the Function class -> cf 1 in list below
function Created(){};//<- new instance of Created hidden class, which extends Function cf 2
function Created.prototype = proto;//<- assigns property to Created instance
return new Created;//<- create new instance, cf 3 for details
}
Right: Function is a native construct. The way V8 works means that there is a Function class that is referenced by all functions. They reference this class indirectly, though, because each function has its own specifcs, which are specified in a derived hidden class. create, then, should be seen as a reference to create extends HiddenFunction class.
Or, if you wish, in C++ syntax: class create : public Hidden::Function{/*specifics here*/}
The Create function references a hidden function identical to create. However, after declaring it, the class receives 1 propriety property, called prototype, so another hidden class is created, specifying this property. This is the basis of your constructor. Because the function body of create, where all of this happens, this is a given, and V8 will probably be clever enough to create these classes beforehand, anyway: in C++ pseudo-code, it'll look similar to code listing 1 below.
Each function call will assign a reference to a new instance Of the hidden class described above, to the Created name, which is local to create's scope. Of course, the returned instance of create does still retain the reference to this instance, but that's how JS scopes work, and so this applies to all engines... think of closures and you'll get what I mean (I'm really struggling with this nasty fever... sorry to nag about this)
At this stage Create points to an instance of this hidden class, which extends a class that extends a class (as I tried to explain in point 2). Using the new keyword triggers behaviour defined by the Function class, of course (as it's a JS language construct). This results in a hidden class to be created which is probably the same for all instances: it extends the native object, and this has a constructor property, which references the instance of Created we've just made. The instances returned by create though are all alike. Sure their constructors may have a different prototype property, but the objects they churn out all look the same. I'm fairly confident that V8 will only create 1 hidden class for the objects create returns. I can't see why the instances should require different hidden classes: their property names & count are the same, but each instance references another instance, but that's what classes are for
Anyway: code listing for item 2, a pseudo-code representation of what Created might look like in hidden-class terms:
//What a basic Function implementation might look like
namespace Hidden
{//"native" JS types
class Function : public Object
{
//implement new keyword for constructors, differs from Object
public:
Function(...);//constructor, function body etc...
Object * operator new ( const Function &);//JS references are more like pointers
int length;//functions have a magic length property
std::string name;
}
}
namespace Script
{//here we create classes for current script
class H_create : public Hidden::Function
{};
class H_Created : public Hidden::Function
{};//just a function
class H_Created_with_prototype : public H_Created
{//after declaring/creating a Created function, we add a property
//so V8 will create a hidden class. Optimizations may result in this class
// being the only one created, leaving out the H_Created class
public:
Hidden::Object prototype;
}
class H_create_returnVal : public Hidden::Object
{
public:
//the constructor receives the instance used as constructor
//which may be different for each instance of this value
H_create_returnVal(H_Created_with_prototype &use_proto);
}
}
Ignore any (likely) syntax oddities (it's been over a year since I wrote a line of C++), and ignoring namespaces and wacky names, The listed classes are, apart from the Hidden::Function effectively all the hidden classes that will ever need to be created to run your code. All your code then does is assign references to instances of these classes. The classes themselves don't take up much space in memory. And any other engine will create just as many objects, because they, too, need to comply with the ECMAScript specs.
So I guess, looking at it like this, this sort of answers all your questions: no not all engines work like this, but this approach won't cause massive amounts of memory to be used, Yes, this does mean a lot of information/data/references to all objects is retained, but that's just an unavoidable, and in some cases happy side-effect of this approach.
Update: I did a bit more digging, and found an example of how you could add JS functions to V8 using templates, it illustrates how V8 translates JS objects/functions to C++ classes, see the example here
This is just me speculating, but I wouldn't at all be surprized to learn that the way V8 works, and this retention business is heavily used in garbage-collection and memory management in general (EG: deleting a property changing hidden classes and the like)
For example:
var foo = {};//foo points to hidden class Object instance (call id C0)
foo.bar = 123;//foo points to child of Object, which has a property bar (C1)
foo.zar = 'new';//foo points to child of C1, with property zar (C2)
delete foo.zar;//C2 level is no longer required, foo points to C1 again
That last bit is just me guessing, but it could be possible for the GC to do this.
What is this retention used for
As I said, in V8, a JS object is actually a sort-of pointer to a C++ class. Accessing properties (and this includes the magic properties of arrays, too!), is fast. Really, really fast. In theory, accessing a property is an O(1) operation.
That's why, on IE:
var i,j;
for(i=0,j=arr.length;i<j;++i) arr[i] += j;
Is faster than:
for (i=0;i<arr.length;++i) arr[i] += arr.length;
While on chrome, arr.length is faster as shown her. I also answered that question, and it, too, contains some details on V8 you may want to check. It could be that my answer there doesn't (completely) apply anymore, because browsers and their engines change fast...
What about the memory
Not a big problem. Yes, Chrome can be a bit of resource hog at times, but the JS isn't always to blame. Write clean code, and the memory footprint won't be too different on most browsers.
If you create a huge constructor, then V8 will create a larger hidden class, but if that class specifies a lot of properties already, then chances of their being a need for additional hidden classes is smaller.
And of course, each function is an instance of the Function class. This being a native (and very important) type in a functional language will most likely be a highly optimized class anyway.
Anyway: as far as memory usage is concerned: V8 does a pretty good job managing memory. Far better than IE's of old, for example. So much so that the V8 engine is used for server-side JS (as in node.js), if memory really was an issue, then you wouldn't dream of running V8 on a server that must be up and running as much as possible, now would you?
Is this just Chrome
Yes, in a way. V8 does have a special take on how it consumes and runs JS. Rather than JIT-compiling your code to bytecode and running that, it compiles the AST straight into machine code. Again, like the hidden-classes trickery, this is to increase performance.
I know I included this graph in my answer on CR, but just for completeness' sake: Here's a graph that shows the differences between chrome (bottom) and other JS engines (top)
Notice that below the bytecode instructions and the CPU, there's an (orange) interpreter layer. That's what V8 doesn't need, owing to the JS being translated into machine code directly.
The downside being that this makes certain optimizations harder to do, especially concerning the ones where DOM data and user input is being used in the code (for example: someObject[document.getElementById('inputField').value]) and that the initial processing of the code is harder on the CPU.
The upside is: once the code is compiled into machine code, it's the fastest you're going to get, and running the code is likely to cause less overhead. A bytecode interpreter is heavier on the CPU most of the time, that's why busy loops on FF and IE can cause the browser to alert the user of a "running script" asking them if they want to stop it.
more on V8 internals here
I don't know much about Chrome's internals, so this is just a guess, but it seems to me that Chrome is performing some kind of static analysis on the code which created the function, and storing that for debugging purposes.
Take a look at this example:
> function create(proto) {
object = {}
object.x = {}
x = object.x
x.func = function() {}
x.func.prototype = proto
return new object.x.func
}
undefined
> create()
x.func {}
x.func? There's no way JavaScript has any built-in way for you to access the name of the variable a function was initially assigned to. Chrome must be doing that for its own reasons.
Now look at this example:
> function newFunc() {
return function() {}
}
> function create(proto) {
object = {}
object.x = {}
x = object.x
x.func = newFunc()
x.func.prototype = proto
return new object.x.func
}
undefined
> create()
Object {}
In this example, since we created the function in a separate closure before assigning it to a variable, Chrome doesn't know the "name" of the function, so it just says "Object".
These examples lead me to guess the following answers to your questions:
What exactly does Chrome's JavaScript engine retain to make that object presentation in the console work? Is it the constructor function, or just the function name?
It performs a static analysis of the code, and stores a string containing the function's "name" somewhere.
Is that retention needed for anything more substantial than console printout?
Probably not.
What is the effect of such retention on memory consumption? What if, for example, the constructor function (or even its name) is abnormally huge?
I'm not sure, but I'm guessing it's very unlikely to be an issue. Since the name of the function is determined using static analysis, the potential size of the function name is limited by the size of variable names in the script which created it (unless perhaps you're using eval, in which case I'm not sure).
Is it just Chrome? I've retested with Firebug and Safari, their consoles don't present the object that way. But do they still retain the same data, for other possible purposes (e.g. due to a genuine concern inherent to a JavaScript VM)?
I doubt it, this seems to be something specific to Chrome used to make debugging a bit easier. As far as I can tell, there's no other reason for a feature like this to exist.
Disclaimer: I am not a Google Chrome expert, however I think that these are not browser-specific, and can be explained by basic Javascript rules.
What exactly does Chrome's JavaScript engine retain to make that
object presentation in the console work? Is it the constructor
function, or just the function name?
Each Object or Function in Javascript has its inheritance chain, going up, all the way to the basic prototype.
You can not circumvent this by setting the prototype property to undefined, although it may seem like it from the console output.
So it is the whole constructor function that is retained because of inheritance, although not available to be accessed through global scope.
Is that retention needed for anything more substantial than console
printout?
Yes, it is needed for the prototype inheritance system to work.
What is the effect of such retention on memory consumption? What if,
for example, the constructor function (or even its name) is abnormally
huge?
Yes, this can cause a memory leak if used improperly.
This is why you should always delete and clean unused variables, so these and their prototypes can get collected by the garbage collector.
Is it just Chrome? I've retested with Firebug and Safari, their
consoles don't present the object that way. But do they still retain
the same data, for other possible purposes (e.g. due to a genuine
concern inherent to a JavaScript VM)?
This should work the same way across all browsers, because prototypal inheritance works the same. I have however not specifically tested for it. Please note that the console outputs int browsers can differ, and this does not mean anything, as each browser can implement its console in its own way.
//The real method to do clone
function doClone(source, keys, values, result) {
if (source == null || typeof (source) !== "object") {
return source;
}
if (source.Clone instanceof Function) {
return source.Clone();
}
if (source instanceof Date) {
if (!(result instanceof Date)) {
result = new Date();
}
result.setTime(source.getTime());
return result;
}
else if (source instanceof Array) {
if (!(result instanceof Array)) {
result = [];
}
for (var i = 0; i < source.length; i++) {
result[i] = clone(source[i], keys, values, result[i]);
}
return result;
}
try {
if (typeof result !== "object" || result == null) {
result = new source.constructor();
} else {
result.constructor = source.constructor;
}
if (source.prototype) {
result.prototype = source.prototype;
}
if (source.__proto__) {
result.__proto__ = source.__proto__;
}
} catch (e) {
if (Object.create) {
result = Object.create(source.constructor.prototype);
} else {
result = {};
}
}
if (result != null) {
// ReSharper disable once MissingHasOwnPropertyInForeach
for (var property in source) {
if (source.hasOwnProperty(property)) {
try {
var descriptor = Object.getOwnPropertyDescriptor(source, property);
if (descriptor != null) {
if (descriptor.get || descriptor.set) {
Object.defineProperty(result, property, descriptor);
} else {
descriptor.value = clone(descriptor.value, keys, values, result[property]);
Object.defineProperty(result, property, descriptor);
}
} else {
result[property] = clone(source[property], keys, values, result[property]);
}
} catch (e) {
result[property] = clone(source[property], keys, values, result[property]);
}
}
}
}
return result;
}
//The portal of clone method
function clone(source, keys, values, result) {
var index = keys.indexOf(source);
if (index !== -1) {
return values[index];
}
result = doClone(source, keys, values, result);
index = keys.indexOf(source);
if (index !== -1) {
values[index] = result;
} else {
keys.push(source);
values.push(result);
}
return result;
}
/**
* Core functions
*/
var X = {
/**
* Clone indicated source instance
* #param {} source The source instance to be clone
* #param {} target If indicated, copy source instance to target instance.
* #returns {}
*/
Clone: function (source, target) {
return clone(source, [], [], target);
}
}
You return a new instance from create to a object called Created.
create()()
> TypeError: object is not a function
If you were to remove the 'new' keyword, then you would expose the Created function to the caller's scope.

Extending core types without modifying prototype

How does one extend core JavaScript types (String, Date, etc.) without modifying their prototypes? For example, suppose I wanted to make a derived string class with some convenience methods:
function MyString() { }
MyString.prototype = new String();
MyString.prototype.reverse = function() {
return this.split('').reverse().join('');
};
var s = new MyString("Foobar"); // Hmm, where do we use the argument?
s.reverse();
// Chrome - TypeError: String.prototype.toString is not generic
// Firefox - TypeError: String.prototype.toString called on incompatible Object
The error seems to originate from String base methods, probably "split" in this case, since its methods are being applied to some non-string object. But if we can't apply the to non-string objects then can we really reuse them automatically?
[Edit]
Obviously my attempt is flawed in many ways but I think it demonstrates my intent. After some thinking, it seems that we can't reuse any of the String prototype object's functions without explicitly calling them on a String.
Is it possible to extend core types as such?
2 years later: mutating anything in global scope is a terrible idea
Original:
There being something "wrong" with extending native prototypes is FUD in ES5 browsers.
Object.defineProperty(String.prototype, "my_method", {
value: function _my_method() { ... },
configurable: true,
enumerable: false,
writeable: true
});
However if you have to support ES3 browsers then there are problems with people using for ... in loops on strings.
My opinion is that you can change native prototypes and should stop using any poorly written code that breaks
Update: Even this code does not fully extend the native String type (the length property does not work).
Imo it's probably not worth it to follow this approach. There are too many things to consider and you have to invest too much time to ensure that it fully works (if it does at all). #Raynos provides another interesting approach.
Nevertheless here is the idea:
It seems that you cannot call String.prototype.toString on anything else than a real string. You could override this method:
// constructor
function MyString(s) {
String.call(this, s); // call the "parent" constructor
this.s_ = s;
}
// create a new empty prototype to *not* override the original one
tmp = function(){};
tmp.prototype = String.prototype;
MyString.prototype = new tmp();
MyString.prototype.constructor = MyString;
// new method
MyString.prototype.reverse = function() {
return this.split('').reverse().join('');
};
// override
MyString.prototype.toString = function() {
return this.s_;
};
MyString.prototype.valueOf = function() {
return this.s_;
};
var s = new MyString("Foobar");
alert(s.reverse());
As you see, I also had to override valueOf to make it work.
But: I don't know whether these are the only methods you have to override and for other built-in types you might have to override other methods. A good start would be to take the ECMAScript specification and have a look at the specification of the methods.
E.g. the second step in the String.prototype.split algorithm is:
Let S be the result of calling ToString, giving it the this value as its argument.
If an object is passed to ToString, then it basically calls the toString method of this object. And that is why it works when we override toString.
Update: What does not work is s.length. So although you might be able to make the methods work, other properties seem to be more tricky.
First of all, in this code:
MyString.prototype = String.prototype;
MyString.prototype.reverse = function() {
this.split('').reverse().join('');
};
the variables MyString.prototype and String.prototype are both referencing the same object! Assigning to one is assigning to the other. When you dropped a reverse method into MyString.prototype you were also writing it to String.prototype. So try this:
MyString.prototype = String.prototype;
MyString.prototype.charAt = function () {alert("Haha");}
var s = new MyString();
s.charAt(4);
"dog".charAt(3);
The last two lines both alert because their prototypes are the same object. You really did extend String.prototype.
Now about your error. You called reverse on your MyString object. Where is this method defined? In the prototype, which is the same as String.prototype. You overwrote reverse. What is the first thing it does? It calls split on the target object. Now the thing is, in order for String.prototype.split to work it has to call String.prototype.toString. For example:
var s = new MyString();
if (s.split("")) {alert("Hi");}
This code generates an error:
TypeError: String.prototype.toString is not generic
What this means is that String.prototype.toString uses the internal representation of a string to do its thing (namely returning its internal primitive string), and cannot be applied to arbitrary target objects that share the string prototype. So when you called split, the implementation of split said "oh my target is not a string, let me call toString," but then toString said "my target is not a string and I'm not generic" so it threw the TypeError.
If you want to learn more about generics in JavaScript, you can see this MDN section on Array and String generics.
As for getting this to work without the error, see Alxandr's answer.
As for extending the exact built-in types like String and Date and so on without changing their prototypes, you really don't, without creating wrappers or delegates or subclasses. But then this won't allow the syntax like
d1.itervalTo(d2)
where d1 and d2 are instances of the built-in Date class whose prototype you did not extend. :-) JavaScript uses prototype chains for this kind of method call syntax. It just does. Excellent question though... but is this what you had in mind?
You got only one part wrong here. MyString.prototype shouldn't be String.prototype, it should be like this:
function MyString(s) { }
MyString.prototype = new String();
MyString.prototype.reverse = function() {
this.split('').reverse().join('');
};
var s = new MyString("Foobar");
s.reverse();
[Edit]
To answer your question in a better way, no it should not be possible.
If you take a look at this: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor it explains that you can't change the type on bools, ints and strings, thus they cannot be "subclassed".
I think the basic answer is you probably can't. What you can do is what Sugar.js does - create an object-like object and extend from that:
http://sugarjs.com/
Sugar.js is all about native object extensions, and they do not extend Object.prototype.

Categories