I'm trying to understand some code Mozilla put out on constructor chaining. I've added comments to the parts I think I understand, but I'm still not clear on everything that's going on here. Can someone explain line by line what is going on in this code?
// Using apply() to chain constructors.
Function.prototype.construct = function (aArgs) {
// What is this line of code doing?
var fConstructor = this, fNewConstr = function () { fConstructor.apply(this, aArgs); };
// Assign the function prototype to the new function constructor's prototype.
fNewConstr.prototype = fConstructor.prototype;
// Return the new function constructor.
return new fNewConstr();
};
// Example usage.
function MyConstructor () {
// Iterate through the arguments passed into the constructor and add them as properties.
for (var nProp = 0; nProp < arguments.length; nProp++) {
this["property" + nProp] = arguments[nProp];
}
}
var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);
// alerts "Hello world!"
alert(myInstance.property1);
// alerts "true"
alert(myInstance instanceof MyConstructor);
// alerts "MyConstructor"
alert(myInstance.constructor);
The original code can be found here.
Basically, this is an alternate way to call a constructor function, which gives you the opportunity to wrap the constructor call in another function. I'll focus on the line you are confused about. fConstructor is set to this, which references our original constructor function, in this example that is MyConstructor. fNewConstr is the constructor which will override the original constructor. Within that fNewConstr you could implement additional code not found in the MyConstructor. Within fNewConstr, we call fConstructor using the Function apply method, passing this as the context, and the aArgs array passed to the construct method. Then we set the prototype of the fNewConstr to the fConstructor prototype to complete the inheritance chain. Finally, we return a new instance of fNewConstr. Prefixing the new keyword to a function call creates a new object, sets its prototype to the prototype of the function, and invokes the function in the context of the new item. Because we apply the fConstructor method with the fNewConstr's context, the result is essentially the same as calling new MyConstructor(). Make sense? Or do I need to go into more detail.
Related
I created a class using function like -
function Person(name) {
this.name = name;
}
var obj = new Person("Ferdinand");
console.log(obj.name);
I am creating an object obj and prints its property name it is working fine and prints "Ferdinand", but if we forget to write new key word like below program -
function Person(name) {
this.name = name;
}
var obj = Person("Ferdinand");
console.log(name);
Here I am not able to understand why name is available in console, it is printing "Ferdinand". But we are using Person as a function and assigning name variable in function itself so why its value is available outside function.
JavaScript does not implement objects in the classical sense. Instead, they are implemented directly:
var thing={}; // new object
Because you typically want multiple objects to follow a common pattern and behaviour, you can use what is called a constructor function to help. In your example, Person is a constructor function.
When using a constructor, you call it indirectly using the new command:
var thing = new Person(…);
In fact, there’s a lot of magic going on here:
JavaScript begins by creating a new object: var thing={};.
Then the this key word is assigned to the new object
Finally the constructor is run, using the this value to assign local values.
If you call a constructor function directly, the first 2 steps do not apply, and the function is left trying to work in its own definition of this which is generally not what you want.
If you really want to call the constructor function without bothering with new, you can try the following:
function Person(data) {
// “new” safe constructor
if(!(this instanceof Person)) {
return new Person(data);
}
This new version checks whether it is being called as a proper constructor, and, if not, calls itself properly.
I have a function for creating objects as follows:
function person() {
this.name = "test person";
}
var me = new person();
Now I'm planning to wrap this function into another one like this for assertion purposes.
function reliable_person() {
/* do check on params if any*/
return new person();
}
var me = new reliable_person();
Is this a possible approach? I know there are better ways for such checks within the constructor itself, but is this a reliable solution?
Invoking a function with new constructs a new object and uses the given function to initialise the object. Something that's a bit special about this is that if you return a non-primitive value, like an object, from that function, that value will be used as the new object instead. Sounds complicated, but what it boils down to is that reliable_person() and new reliable_person() result in the exact same thing, because you're returning an object from it. So using new with that function is pointless.
Removing the superfluous new from it, all that's left is a normal function which returns an object (a "factory function"). Yes, that works and is "reliable".
Having this:
sillyObject = {
init: function init(sillySettings) {
name = sillySettings.name
}
};
sillyObject.showAlert = function(x) {
return alert(x);
};
When I run this code:
var sillyvar = new sillyObject()
sillyvar.init(mySettings);
silly.showAlert("silly!");
I get an error but instead if I run the same thing using Object.create it runs..
var sillyvar = Object.create(sillyObject);
sillyvar.init(mySettings);
silly.showAlert("silly!");
Any (silly) help will be appreciated.
new and Object.create are two fundamentally different things.
new is going to expect to be followed by a function, and if not (as you saw) it will give you an error. That is because new expects to call a constructor function and then use that as a basis for a new execution context. During that context, the function has this bound to the scope of the execution context. Once the function is done executing it returns the this value which usually has had some data attached to it. In your example, that would look like this:
function sillyObject() {}
sillyObject.prototype.init = function(sillySettings) {
//perhaps you wanted to attach this name to the sillyObject?
name = sillySettings.name;
//which would look like this
this.name = sillySettings.name;
//because `this` here refers to the object context (remember?)
};
sillyObject.prototype.showAlert = function(x){
return alert(x);//returning alert simply returns undefined (not sure why this is used here)
};
and then you could use new, it would create the execution context using the constructor and then attach the prototype and you would end up with a new instance of sillyObject (all instances would be different).
var sO = new sillyObject();
sO.init(mySettings);
sO.showAlert("silly!");
Object.create() on the other hand is expecting an object as an argument (which is why your version worked here). It will create a new object using that object argument as a template basically. Or as MDN explains it "The Object.create() method creates a new object with the specified prototype object and properties". This basically creates a copy if nothing else is done with the Object, and that is why the alert worked here but not in the new version.
If you tried doing new sillyObject(), the error you get is Uncaught TypeError: object is not a function since sillyObject is an object and not a function.
This answer gives a good overview on the new keyword.
Object.create does not do the same thing as new. It will create a new object with sillyObject as the prototype.
sillyObject is a Object not a function. new will create a new instance of a function. You probably want to use this or just .prototype
var sillyObject = function () {
this.sillySettings = {};
}
sillyObject.prototype = {
init : function (name) {
this.sillySettings.name = name;
},
showAlert: function (x) {
return alert(x);
}
};
var silly = new sillyObject();
silly.init('foo');
silly.showAlert('bar');
this.sillySettings isn't a function so we don't keep it in the prototype. We can keep init and showAlert in the prototype. We use prototype because when using new we use sillyObject() so imagine the variable silly being replaced this sillyObject() showing why we use prototype because silly is instantiated as a function.
Is there an easier way to call filter on a prototype method without an anonymous function?
I wonder if there is an equivalent to myArray.filter(function(it){ it.method() }).
This looks close to what might work (it doesn't):
function X() {}
X.prototype.method = function() { console.log(this); }
[new X(), new X()].filter(X.prototype.method.call);
Instead I get a TypeError in both latest Firefox and Chrome, it's because it doesn't quite do what I want:
x = function() { console.log(this) }
x.call(123) //logs 123
y = x.call //reports that y is of type function in console
y(123) //TypeError: Function.prototype.call called on incompatible undefined
y.call(x, 123); //this is what you really need
I tried using bind, maybe I'm missing it, but if it's not a one-liner, it's not any better than the anonymous method form:
function X() {}
X.prototype.method = function() { console.log(this); }
y = X.prototype.method.call
y.bind(X.prototype.method)
[new X(), new X()].filter(y);
Let's set up some variables:
var method = X.prototype.method,
array = [new X(), new X()];
Your attempt can now be written as:
array.filter(method.call);
The problem is that call is getting called but with no this. It needs a this of method. method.call is precisely the same as the raw Function.prototype.call, with no binding to any this. Merely saying method.call does not give you a version of call bound to method. To arrange for call to be bound to the right this, namely method, you need to, well, bind it:
array.filter(method.call.bind(method));
Walking through this:
method.call.bind(method) returns a new
version of Function#call which is bound to X#method; think of it as
method.call(waiting), which is waiting to be called with a
value which will call X#method against a particular instance of X.
Array#filter passes each argument in the array to that bound version
of Function#call, which results in method.call(elt, remaining_args...),
which is the equivalent of elt.method(remaining_args...).
Output:
> array.filter(method.call.bind(method));
X {method: function}
X {method: function}
Some sugar
One could make this more semantic and readable with a little wrapper, which we will call thisify:
function thisify(fn) { return fn.call.bind(fn); }
array.filter(thisify(method));
Using the context parameter to filter
You could use the little-used context parameter of filter and its brethren (except reduce), essentially, letting filter do the binding for you, if you choose to look at it that way, since
Array#filter(fn, context) === Array#filter(fn.bind(context))
So we can just write:
array.filter(method.call, method);
That actually looks cleaner to me. I doubt if it could get much simpler than that.
I'm not understanding why in the following code, obj.BaseMethod doesn't return the method defined in BaseClass constructor. In other words, why is it that
SubClass.prototype.BaseMethod
is defined, but
new SubClass().prototype.BaseMethod
is undefined.
http://jsfiddle.net/HvxJ4/4/
I'm obviously missing something important here.
function BaseClass() {
var privatestuff = "blah";
return {
BaseMethod: function() {
console.log('BaseMethod called');
}
}
}
function SubClass() {
var moreprivates = "stuff";
return {
SubClassMethod: function() {
console.log('SubClassMethod called');
}
}
}
SubClass.prototype = new BaseClass();
var obj = new SubClass();
obj.SubClassMethod(); // SubClassMethod called
obj.BaseMethod(); // Uncaught TypeError: undefined is not a function
UPDATE
I actually understood how to get my code working using
this.method = function() { }
in my constructor function. I just didn't understand why the code above didn't do the same thing.
The answer is that if you return an object in a constructor function, you are no longer using "protoypal" inheritance.
The thing that makes this most clear to me was this answer
https://stackoverflow.com/a/2118831/834770
Quoting Douglas Crockford in Chapter 5, Inheritance, of JavaScript:
(...)
Douglas Crockford then explains how the new operator could be
implemented as a JavaScript function. This function makes use of
several other functions defined in the book, so I rewrote it in a
(somewhat) simpler form below:
function createNew(constructor) {
// a function to explain the new operator:
// var object = createNew(constructor);
// is equivalent to
// var object = new constructor();
//
// param: constructor, a function
// return: a new instance of the "constructor" kind of objects
// step 1. create a new empty object instance
// linked to the prototype of provided constructor
var hiddenLink = function(){};
hiddenLink.prototype = constructor.prototype;
var instance = new hiddenLink(); // cheap trick here: using new to implement new
// step 2. apply the constructor the new instance and get the result
var result = constructor.apply(instance); // make this a reference to instance within constructor
// step 3. check the result, and choose whether to return it or the created instance
if (typeof result === 'object') {
return object;
} else {
return instance;
}
}
So, in short, if you return an object in this function, then the inheritance bit is effectively ignored.
Here's a way to think of the statement
new SubClass().prototype.BaseMethod
First, the new keyword tells JavaScript to create a new, empty object, i.e. {}
Then JavaScript sets the context (this) to be equal to that new object and calls the function after new. So in this case JavaScript will look for a function to call, but your syntax doesn't reference a defined function, so the result is undefined.
Contrast that with a typical approach for defining objects in JavaScript:
function ExampleObject() {
// stuff related to the example object
this.exampleProperty = "value";
}
var exObj = new ExampleOject();
In this case new creates the empty object {} as before, but now there is a defined function to call. When this function is called, the newly created object (set equal to this) will have an exampleProperty set equal to "value". The resulting object is then assigned to the variable exObj.
It may sound strange to those coming from a Java background (or similar), but JavaScript doesn't really support the concept of classes. The language made the unfortunate choice of trying to make it's prototypical inheritance look like classical inheritance, but it's really not the same. If you're going to be spending a lot of time in JavaScript, you might want to stop trying to think in terms of classes and subclasses and learn a bit about prototypes instead.