Beget function in JavaScript: The Good Parts - javascript

I am reading JavaScript: The Good Parts. In the book, a beget function is defined. Its purpose is to create and return a new object, which uses the other object as its prototype. Why does the beget function instantiate a new function instead of an object?
if( typeof Object.beget !== 'function' ){
Object.beget = function(o){
var F =new Function(){}; // this line, why it cannot be var F = new Object();
F.prototype = o;
return new F();
}
}

This has everything to do with the new keyword. In JavaScript, new only works with functions (which are a special type of object).
If you use new on just about any function, you will get an object back.
alert(typeof console.log); // function
var dumb = new console.log(); // dumb is an object
The type of object you get back depends on that function's prototype object.
alert(typeof console.log.prototype); // object, any new objects created by the new keyword will be of this type.
alert(typeof Function.prototype); // function, new objects are functions.
alert(typeof new Function()); // function, see?
alert(typeof (function(){})); // function, using literal syntax, equivalent
You may have noticed from above that Function itself is a function. In fact, all of the built-in constructors are functions (Function, Object, Number, Array, etc). Capitalization is just a convention to distinguish how you use a function.
So to get back to your question, the author uses an empty Function object simply because it can be used as a constructor. Objects cannot.
He then changes the constructor's prototype, so that it will return objects of that type.
Object.beget = function(o) {
var F = new Function(); // now F can be used as a constructor.
F.prototype = o; // All new objects F creates will be based on o.
return new F();
};

To add on to previous answers and avoid some confusion, this beget() function was replaced with create() via an errata, so different books seem to have different version of the code. The book on Safari Books Online has it printed like this:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
Note that it no longer encourages the use of the new keyword on line 3.

In my opinion,as the MDN said
The new operator creates an instance of a user-defined object type or of one of the built-in object types that has a constructor function.
Syntax
new constructor[([arguments])]
Parameters
constructor
A function that specifies the type of the object instance.
arguments
A list of values that the constructor will be called with.
So,wo have to use a function to create a new instance.
For example:
var list = new Array();
typeof Array;//"function"
This Array is not a object,but a constructor function.

For one thing, you can't use () on a generic object nor on anything else that isn't a function. You'll get an error something like "TypeError: F is not a function."

// create a temporary function
var F =new Function(){};
// set the prototype of the function to be o
F.prototype = o;
// create a new object from the function
return new F();
Because that's how new works. new creates a new object and places F.prototype in the prototype chain.
new F() === Object.create(F.prototype)

My books is
if (typeof Object.beget !== 'function') {
Object.beget = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}

your answer is in the book only. The beget method created a new object and that object is used as its prototype.
Below line is used to check if beget method is already used or not !
if( typeof Object.beget !== 'function' )
And then..
A constructor, F, is defined, its prototype is set to the passed in object and then a new instance is returned.

Related

Weird behaviour with constructors

Can someone please explain this behaviour to me.
First let's create a constructor and a new object using the constructor:
var MyConstructor = function() {};
var obj = new MyConstructor();
Everything is as expected:
console.log(obj.constructor === MyConstructor); //true
console.log(obj.__proto__ === MyConstructor.prototype); //true
Let's try again, but this time let's add a custom prototype to the constructor:
var MyConstructor2 = function() {};
var myProto = { fld: 'val' };
MyConstructor2.prototype = myProto;
var obj2 = new MyConstructor2();
Now things are not as I expect them to be:
console.log(obj2.constructor === MyConstructor2); //false?!?!
console.log(obj2.constructor === Object); //true, b-but i didnt use Object..
console.log(obj2.__proto__ === MyConstructor2.prototype); //true
Why is obj2.constructor referring to Object and not MyConstructor2?
--- edit1 ---
Just to clarify. If you create a new function:
var MyConstructor = function() {};
then Javascript implementation will also create a new object:
var temp = { constructor: MyConstructor };
and set it to:
MyConstructor.prototype = temp;
The thing to note here is that the temp object overwrites the constructor field from Object.prototype (and by default Object.prototype.constructor === Object).
So when I create a new object using the constructor:
var obj = new MyConstructor();
then the object inherits the constructor field which points to MyConstructor. In the second case there was no overwriting, so the second object inherited the constructor field directly from Object.prototype.
Each Function object has a prototype property whose "constructor" property is referencing the function. When you create a new prototype using the Object literal syntax, you are created a brand new object whose constructor is literally the Object function. You need to explicitly set the constructor property:
function MyConstructor2() {
}
MyConstructor2.prototype = {
constructor: MyConstructor2
};

Dynamic instantiation in JavaScript [Classname as String]

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;
}

prototype property is absent in Chrome Dev Tools

I've just opened Chrome Dev Tools and typed this code:
function Car() {
this.value = 10;
}
var cc = new Car();
But when I typed this code:
cc.prototype
I got undefined. Why?
As I know 'cc' object must have the same prototype as Car constructor.
You need to use cc.__proto__ to get the prototype object.
See https://stackoverflow.com/a/9959753/4466618 for the difference.
The prototype is inside the cc.__proto__ attribute.
The browser (JS engine) place it there.
The new function looks something like:
function new() {
var newObj = {},
fn = this,
args = arguments,
fnReturn;
// Setup the internal prototype reference
newObj.__proto__ = fn.prototype;
// Run the constructor with the passed arguments
fnReturn = fn.apply(newObj, args);
if (typeof fnReturn === 'object') {
return fnReturn;
}
return newObj;
}
This line is the relevant line for you:
newObj.__proto__ = fn.prototype;
The Object.prototype
The Car.prototype is the definition of all the methods and properties of the Car object. The cc instance store it inits __proto__

How is 'Function' (not 'function') defined in Javascript?

I happened to notice Javascript also supports the Function keyword just as in ActionScript. Since both are derived from ECMA Script, existence of such similarities is a common knowledge. But I was curious on what Function represents in case of Javascript, if not a Class(as in as3).
In JS,
var func = new Function(); or var func = new Function;
Assigns a function called 'anonymous' to the variable 'func'.
Could it be simply a method to implement the actual 'function' in an Object oriented way..
Since most browsers(ff, ie & chrome) seem to implement it in the same way, is it addressed in the spec?
Function is the "class" all function extend from. All functions are really Function objects.
(function(){}) instanceof Function === true
You can also use new Function to make a function from a string (like eval).
var func = new Function('x,y', 'return x+y;')
"Every function in JavaScript is actually a Function object." MDN Documentation.
function myFunc(a, b) {
console.log( 'test' );
}
is essentially equal to
var myFunc = new Function('a', 'b', 'console.log( "test" )');
There are however some differences between to two ways of declaring a function. Read the doc!
1 way:
var Person = function() {
this.name;
this.age;
}
var p1 = new Person();
2nd way:
function Person() {
this.name;
this.age;
}
var p1 = new Person();

Custom prototype chain for a function

I am just curious whether I can include an object into function prototype chain. What I mean:
var test = function() { return 'a'; };
console.log(test.bind(this)); // return new bound function
// changing the prototype
// in console the prototype is really set as needed
// test => new object => function prototype => Object prototype
var F = function() {};
F.prototype = Function.prototype;
test.prototype = new F();
console.log(test.bind()); // this still works and returns new bound function
test.prototype.bind = function() {
return 'test';
};
console.log(test.bind()); // this does not work as expected -> returns new bound function instead of 'test'. When I do delete Function.prototype.bind, then console.log(test.bind) returns undefined
You have a function test. It is a instanceof Function, and inherits from Function.prototype so that you can call test.bind for example.
Then you set the "prototype" property of your function to an object inheriting from Function.prototype. That means that all instances of test will inherit from that object (and from Function.prototype):
var t = new test;
t instanceof test; // => true
t instanceof Function; // => true
Then you overwrite the test property on your custom prototype object. You do good to do so, because Function methods should only be called on functions (i.e. callable objects):
>>> Function.prototype.bind.call({})
Unhandled Error: Function.prototype.bind: this object not callable
In your tests with console.log you only apply the Function methods on your test function, not on instances of it. Good, because they'd fail. So, I can see no reason why any objects should inherit from Function - you can't construct functions that don't inherit directly from Function.

Categories

Resources