I want to know from where does console.log get the name of the constructing function when printing an object. Also, does that actually effect anything code wise?
function F() {
this.test = 'ok';
}
var f = new F();
console.log( f );
The output of console.log (in Chrome) is:
F {test: "ok"}
Where does the console.log get the F in F {test...?
If I change F.constructor, F.prototype, and f.constructor to something random, it still prints the original F:
function G() {
this.fail = 'bad';
}
function F() {
this.test = 'ok';
}
F.prototype = G;
F.constructor = G;
var f = new F();
console.log( f );
The output is still the same - F {test: "ok"}
Is this information is simply kept privately by the browser, my question is does it affect JavaScript code in any way? That is, will it creep up during comparison or inheritance, after I override the constructor's prototype and constructor properties?
UPDATE
The original purpose was to do the following.
function Person ( _name ) {
this.name = _name;
}
function Construct( _constructor, _args, _context ) {
function F () {
var context = _context || this;
return _constructor.apply( context, _args );
}
/* I want to have the constructed object by identified
as _constructor and not a F */
F.prototype = _constructor.prototype;
return new F();
}
function Make ( _who ) {
if ( 'person' === _who ) {
/* Remove the first argument, who, and pass along all the rest.
Constructors cannot be called with .apply so I have to use
this technique. */
return Construct( Person, Array.prototype.slice.call( arguments, 1 ) );
}
}
var dev = Make( 'person', 'John Doe' );
console.log( dev ); // prints `F {name: "John Doe"}`
As you can see, the resulting print of dev outputs F {name: "John Doe"}, which made me question whether I may run into problems later on if I'd like to make comparisons or inheritance with instances constructed in such a way.
Changing F.prototype replaces the content of F, not the name. The old prototype object still exists and a reference to it is stored internally in each instance of the old F. You cam check it by calling f.__proto__´ (deprecated) or Object.getPrototypeOf(f).
Note that __proto__ is an accessor proterty (internally a getter, not a real property), so it cannot be changed.
It's not difficult, because f is finally an instance of F and the order of scope resolving (this, prototype, ...) is obvious :-)
For example, you can run this code and you'll see that in this case it will print G:
function G() {
this.fail = 'bad';
}
function F() {
this.test = 'ok';
}
F.prototype = G;
F.constructor = G;
var f = new F(); // Prints F
console.log(f);
f.prototype = G; // Redefining f type info
f.constructor = G;
console.log(f); // Prints G
Your creating a new instance of F, so the browser prints that in order to help you keep track of your logging. Even though you change the prototype would you still have to create a new "F" in order to get the object.
function A () { something: 123 }
new A();
console.log result: A {}
new B();
console.log result: ReferenceError: B is not defined
object.constructor.name is another way of getting the name of an object constructor.
May I suggest another approach for the original intention? There is no problem with just using a different reference to the prototype object instead of the original one, so you can do
function construct(constructor, args, context) { //lowercase, as it's a function, not a class
return new constructor(args);
}
This should create the right object in the first place, no need to swap any prototypes.
Related
Let's consider the following code,
let f = function () {
this.a = 1;
this.b = 2;
}
let o = new f();
f.prototype.c = 3;
console.log(Object.getPrototypeOf(o));
Prints
[object Object] { c: 3 }
But if I use setPrototypeOf instead of f.prototype.c, the object is empty.
let f = function () {
this.a = 1;
this.b = 2;
}
let o = new f();
Object.setPrototypeOf(f, {c: 3});
console.log(Object.getPrototypeOf(o));
Prints
[object Object] { ... }
But if I use
let f = function () {
this.a = 1;
this.b = 2;
}
let o = new f();
Object.setPrototypeOf(f, {c: 3});
console.log(Object.getPrototypeOf(f));
Prints
[object Object] { c: 3 }
In short the question is, when using Object.setPrototypeOf(o), the Object prints empty,
and when using Object.setPrototypeOf(f), the objects prints the property which is added.
where as when setting prototype using f.prototype.c = 3, it is accessible by both Objects
prototype and the functions prototype.
When you Object.setPrototypeOf(f, ... ), the chain is actually 3 long:
The prototype of o is f. The prototype of f is {c:3}.
So your examples are not equivalent to each other:
1)
In the first example, you add the property c directly to the prototype that instances of f will use, so the proto of o contains c since f is the constructor for o.
In the last two examples you add c to the proto of f, so the prototype f was created with. Remember that functions are also just objects and that you're setting the proto of a function you use as a constructor. So the proto of o contains the proto of f which contains c.
2)
In the 2nd example, you getPrototypeOf() o.
In the 3rd, you getPrototypeOf() f. Hence only the 3rd example shows c again.
3)
If you inspect the element in chrome, you see that the constructor of the 2nd example is f, since you ask the proto of o.
In the 3rd example you'll see that the constructor is Object, since you ask the proto of f, which has been set to the object {c} and skip the proto from o to f.
PS: I'm aware this is might be a confusing explanation.
PPS: If you want inheritance, sticking to child.prototype = Object.create( parent.prototype ); child.constructor = child; or ES6 class class child extends parent when it can be used led to the least confusion for me personally.
var first = function first() {
this.value = 'first function';
};
// before .prototype.extra, first inherist from Function.
console.log( 'constructor of first before:', first.constructor );
first.prototype.extra = 'implemented with .prototype.extra';
// after .prototype.extra, first still inherits from Function, we did not change anything to first itself.
console.log( 'constructor of first after:', first.constructor );
// When first is used as a prototype, the instances will get "extra".
// Aka, everything that inherist from first.
var first_instance = new first();
console.log( 'extra on instance of first:', first_instance.extra );
// f itself does NOT have the extra property, only instances of first do.
console.log( 'extra on first itself:', first.extra );
console.log( '------' );
var second = function second() {
this.value = 'second function';
};
// before setPrototypeOf, second inherist from Function, like above.
console.log( 'constructor of second before:', second.constructor );
Object.setPrototypeOf( second, { extra: 'implemented with .setPrototypeOf()' });
// after setPrototypeOf, second inherist from Object, we broke the default inheritance chain.
console.log( 'constructor of second after:', second.constructor );
// BY doing this, we effectively turned second into an object.
// It no longer is a function, so we cannot use function methods like .call() or .apply()
console.log( 'second is object, not function: function.apply', second.apply );
console.log( 'second is object, not function: object.hasOwnProperty', second.hasOwnProperty );
// When second is used as a prototype, the instances will not get "extra".
var second_instance = new second();
console.log( 'extra on instance of second:', second_instance.extra );
// second itself Does have the extra property, sine we assigned it on the prototype used by second. not when second is used as the prototype.
console.log( 'extra on second itself:', second.extra );
I am working with Javascript and Appdescriptors in JSON format.
What I want to do is creating a instance of a Class, where the classname is saved as string in oModelConf[sModelName].type. If that is not the case I want to take "sap.ui.model.odata.ODataModel"
Related Question offers this solution:
function instantiate(className, args) {
var o, f, c;
c = window[className]; // get reference to class constructor function
f = function(){}; // dummy function
f.prototype = c.prototype; // reference same prototype
o = new f(); // instantiate dummy function to copy prototype properties
c.apply(o, args); // call class constructor, supplying new object as context
o.constructor = c; // assign correct constructor (not f)
return o;
}
This is not a very good solution I think.
EDIT It does not work for me because my class is not defined on window, so window[className] is undefined. I do not know where my function is defined in SAPUI5
A second Solution:
eval(`a = new ${oModelConf[sModelName].type || "sap.ui.model.odata.ODataModel"}(sServiceUrl, true);`);
This is not a better solution because we should not use eval().
Are there any better solutions?
EDIT2
Because of the url in pimskies answer I found an other solution:
Since window.sap.ui.model.odata.ODataModel is the same as sap.ui.model.odata.ODataModel and window.sap is the same as window[sap]
I could take my string, and replace all . with ][, put the right brackets to front and end.
I will not code that because it is not a going solution.(I should not have coded the evalthing too...)
You could use jQuery.sap.getObject to access the class:
var ModelClass = jQuery.sap.getObject(oModelConf[sModelName].type || "sap.ui.model.odata.ODataModel");
var model = new ModelClass();
Edit: An other way (which i would recommend if you use AMD)
If you are using the modern AMD modules and you don't know if the module containing your class has already been loaded, you should use sap.ui.require() to load the module asynchronously. It requires the module to be specified via its unified resource name (the conversion is probably the most ugly part):
var className = oModelConf[sModelName].type || "sap.ui.model.odata.ODataModel";
var urn = className.replace(".", "/"); //Convert to unified resource name
sap.ui.require([urn],function(ModelClass){
//This function is called when the module is available
var model = new ModelClass();
...
});
Maybe map the string to a class?
function Foo() {
console.log('new foo');
}
function Bar() {
console.log('new bar');
}
var objects = {
'foo': Foo,
'bar': Bar
};
var cls = objects.foo || Bar;
new cls();
https://jsfiddle.net/ckd56d9v/1/
Or take a look at this answer: https://stackoverflow.com/a/9804142/5930258
What not combine the two? Since window[className] is failing, replace it with
oModelConf[className].type || sap.ui.model.odata.ODataModel...
function instantiate(className, args) {
var o, f, c;
c = oModelConf[className] || sap.ui.model.odata.ODataModel;
f = function(){}; // dummy function
f.prototype = c.prototype; // reference same prototype
o = new f(); // instantiate dummy function to copy prototype properties
c.apply(o, args); // call class constructor, supplying new object as context
o.constructor = c; // assign correct constructor (not f)
return o;
}
According to various sources, including the specs, if one was to create a custom implementation of the simpler version of Object.create then it would go like this:
if (typeof Object.mycreate1 !== 'function') {
Object.mycreate1 = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
However, as new basically creates a new instance and calls F on it, I wonder whether or not this is also correct:
if (typeof Object.mycreate2 !== 'function') {
Object.mycreate2 = function( o ) {
var _ = { };
_.prototype = o;
return _;
}
}
Simple tests suggest that this works:
var p = { name : 'Jan', surname : 'Kowalski' };
var q = Object.mycreate2( p );
q.surname = 'Malinowski';
p.surname = 'Kowalski';
console.log( q.surname );
console.log( p.surname );
The question is: is the latter implementation correct? If no, I'd also like to know why.
I might probably know the answer: object's prototype property is read only and while it works in FireFox, it shouldn't. On the other hand, function's prototype is read-write and it can be changed.
The problem with this hypothetical answer is that I can't validate it in the specs.
Your second function is not equivalent to the first. Setting the "prototype" property of a simple object doesn't have any effect. A better test:
var x = Object.mycreate2({foo: "bar"});
console.log(x.foo); // undefined
The "prototype" property of a constructor function is interesting, because it actually does have a role in establishing the inheritance chain for a constructed object.
Consider the following code:
var f = function() { return 10; }
typeof f; // returns "function"
f(); // returns 10
var g = f;
g(); // returns 10, obviously
var h = new f;
h; // console evaluates to f - ????
h(); // Type error - called_non_callable
typeof h; // returns "object"
So, what is h here? The Chrome console seems to evaluate it as f, but it isn't callable. What does it mean to "new" a function like this? How is h now related to f?
As an aside, these two lines appear to be equivalent:
var h = new f;
var h = new f();
What's up with that?
The root of your confusion is the way that Chrome represents objects on its console.
The expression new f() in your example is represented as 'f' on the Chrome's console output, but just as a mere "convenience" of this particular console, for example, logging the following object:
({constructor: function Foo(){}});
Will show it represented as "Foo". Basically the console tries to find a representation of which constructor your object is instance of.
So, the console shows you f, but you are not logging the function, the new operator produces an object that inherits from f.prototype.
You are returning 10 from the function, but since you call it with new, and the return type is a primitive, the value is discarded, the newly created object that inherits form f.prototype is returned.
For example:
var F = function () { return 10; };
F.prototype.inherited = 'foo';
var h = new F(); // the 10 returned is ignored
h instanceof F; // true
h.inherited; // "foo"
On the other hand, if you return an object from a constructor, the object instance that the new operator creates behind the scened (and inherits from the constructor's prototype) will be lost, for example:
var F = function () { return {}; };
F.prototype.inherited = 'foo';
var h = new F(); // the empty object is returned
h instanceof F; // false
h.inherited; // undefined
Yes, new f; and new f(); are completely equivalent, although people recommend using the parentheses to add clarity to your code.
this is how object-orientation in JavaScript works. if you call new on a function, this function would be treated as if it's a constructor of a class (the keywords class to define a class like other languages do doesn't exist in js).
so calling var h = new f(); makes h an object of class f, and an object itself isn't callable (that's why you get called_non_callable).
if you want to give your "class" some methots, you should do it like this:
function cat(name) {
this.name = name;
this.talk = function() {
alert( this.name + " says meeow!" )
}
}
cat1 = new cat("felix")
cat1.talk() //alerts "felix says meeow!"
cat2 = new cat("ginger")
cat2.talk() //alerts "ginger says meeow!"
that's an example from page 2 of this tutorial. for more information, read that or ask Google for object-orientation in JavaScript.
I'd recommend reading https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/this, especially the section on "Function context".
In a nutshell, when you new function, you're creating a new object:
var h = new f;
This says: "Create a new object referenced by the variable named h of type f". The function is then treated as a constructor rather than what you're expecting.
Once the object has been created, it's no longer callable (which is why you're experiencing an error on the line h().
In most language (Javascript not excluded), the () of a constructor are optional (unless there are required parameters (in some languages)).
Basically, the new keyword calls f as the constructor of a new object. The first reply to this question, on StackOverflow, sums it up quite nicely:
What is the 'new' keyword in JavaScript?
In JavaScript, functions aren't just functions, and they're not just objects; they're also used to defined constructors for types.
Generally speaking, when you have a function f, that function defines a type, whether that was intended or not. So the line
var h = new f();
defines an object h whose type is f.
I've never seen the line
var h = new f;
but I'm assuming it does the same as the other line.
function a () {
return "foo";
}
a.b = function () {
return "bar";
}
function c () { };
c.prototype = a;
var d = new c();
d.b(); // returns "bar"
d(); // throws exception, d is not a function
Is there some way for d to be a function, and yet still inherit properties from a?
Actually, it turns out that this is possible, albeit in a non-standard way.
Mozilla, Webkit, Blink/V8, Rhino and ActionScript provide a non-standard __proto__ property, which allow changing the prototype of an object after it has been created. On these platforms, the following code is possible:
function a () {
return "foo";
}
a.b = function () {
return "bar";
}
function c () {
return "hatstand";
}
c.__proto__ = a;
c(); // returns "hatstand"
c.b(); // returns "bar"; inherited from a
This might be of use to anyone who doesn't need to worry about cross-platform compatibility.
However, note that only the properties of an object can be inherited. For example:
var d = {};
d.__proto__ = a;
d.b(); // returns "bar"
d(); // throws exception -- the fact that d is inheriting from a function
// doesn't make d itself a function.
Short answer: not possible.
This line of your code:
var d = new c();
automatically assumes that d is an object. Unless c is a constructor of a builtin object, e.g., Function. But if c is already defined by the language, you cannot manipulate its prototype, and cannot "inherit" it from whatever you like. Well, in some interpreters you can, but you cannot do it safely across all interpreters — the standard says: "you doth not mess with standard objects or the interpreter will smite you!".
The builtin objects are "unique" and JavaScript does not provide ways to duplicate them. It is not possible to recreate String, Number, Function, and so on without resorting to incompatible trickery.
Based on a discussion on meta about a similar question I'm posting this answer here based on #alexander-mills original
This can now be done in a standards compliant way
First create an object which inherits Function
const obj = Object.create(Function.prototype); // Ensures availability of call, apply ext
Then add you custom methods and properties to obj
Next declare the function
const f = function(){
// Hello, World!
};
And set obj as the prototype of f
Object.setPrototypeOf(f,obj);
Demonstraction
const obj = Object.create(Function.prototype);
// Define an 'answer' method on 'obj'
obj.answer = function() {
// Call this object
this.call(); // Logs 'Hello, World'
console.log('The ultimate answer is 42');
}
const f = function() {
// Standard example
console.log('Hello, World');
};
Object.setPrototypeOf(f, obj);
// 'f' is now an object with an 'answer' method
f.answer();
// But is still a callable function
f();
Yes, it is possible if you use the __proto__ property Daniel Cassidy mentioned. The trick is to have c actually return a function that has had a attached to its prototype chain.
function a () {
return "foo";
}
a.b = function () {
return "bar";
}
function c () {
var func = function() {
return "I am a function";
};
func.__proto__ = a;
return func;
}
c.prototype = a;
var d = new c();
d.b(); // returns "bar"
d(); // returns "I am a function"
However, you'll need to do some more tweaking of the prototype chain if you want instanceof to return better results.
d instanceof c // true
d instanceof a // false
c instanceof a // false
Does it have to actually be a prototype chain? You can use a mixin pattern to make a function have all of the properties of a instead. You can even wrap it in a nice "new" syntax to fake it if you really want.
function a () {
return "foo";
}
a.b = function () {
return "bar";
}
function c () {
var f = function(){
return a();
};
//mixin all properties on a
for(var prop in a){
f[prop] = a[prop];
}
return f; //just returns the function instead of "this"
};
var d = new c(); //doesn't need the new keyword, but just for fun it still works
alert(d()); //show "foo"
alert(d.b()); //shows "bar"
You can add properties to d without affecting a. The only difference between this and what you want is that changes to a will not affect existing "instances" of c.
This is something I've been trying to do for a while now. Special thanks to the authors above for their input.
Here is a chained implementation of using a "callable-object":
var $omnifarious = (function(Schema){
var fn = function f(){
console.log('ran f!!!');
return f;
};
Schema.prototype = {
w: function(w){ console.log('w'); return this; },
x: function(x){ console.log('x'); return this; },
y: function(y){ console.log('y'); return this; }
};
fn.__proto__ = (new Schema()).__proto__;
return fn;
})(function schema(){ console.log('created new schema', this); });
console.log(
$omnifarious()().w().x().y()()()
);
Also, with this "Schema" approach, it may be possible to create a reusable interface using Object.freeze() on Schema's prototype or __proto__ object. That might look something like this: fn.__proto__ = Object.freeze(new Schema().__proto__) -- this is not a practical example and more may be needed. That said, its a different discussion. Try it out and let me know!
Hope this is helpful.