Sometimes you think you know something, and then it turns out you really don't...
I've been doing a lot of research into the Javascript inheritance over the last few days for a talk I'm giving at a local tech group. No problem I figured, I know this stuff, I'll just brush up...
Turns out, I've lots still to learn. Here is the first thing that surprised me:
function Vehicle() {
function move() {
console.log('moving');
}
return {move: move}
}
function OtherVehicle() {
this.alsoMove = function() {
console.log('also moving')
}
}
The first method is what I've always understood the module pattern to be. The second, though I don't know it's name, I understood to be basically equivalent - just a minor syntactic variation.
Except:
(new Vehicle()) instanceof Vehicle \\ returns false
(new OtherVehicle()) instanceof OtherVehicle \\ returns true
new Vehicle() returns an 'Object'.
Looking at this, that makes sense, but it was not what I was expecting.
Can anyone explain what is going on here, why, and maybe point me at a really good resource that gets into the guts of this?
As a follow-up.
var Car = function() {
function drive() { console.log('driving') };
return {drive: drive}
}
Car.prototype = new Vehicle() //I know, Object.create preferred
(new Car).move() //undefined
// However...
var Car = function() {
this.drive = function () { console.log('driving') };
}
Car.prototype = new Vehicle();
(new Car()).move() //output 'moving'
So, the module pattern can't be used in inheritance?
Or have I just woken from the wrong side of the bed today and should go for a run before I next touch a keyboard?
I don't know of any particularly good resource, but here's some feedback anyway.
new Vehicle() returns an 'Object'.
The return {}, overrides the default return value this, which is the instantiated object. Calling such functions with new is pointless as this gets discarded. As far as OOP languages go, JavaScript is a bit odd in that it allows you to return another value for this in a constructor.
So yes, inheritance used that way won't work: {} itself is not in any way linked to a parent/base class, which is why
(new Car).move() //undefined
is undefined.
Related
I am fully aware of other very similar posts about object.create but I really cannot arrive to answer on this particular question.
I saw a code once where it looks like it was using object.create to create new prototype and then adding back to constructor
function blah(){
// typical constructor
}
blah.prototype = Object.create(somethingelse.prototype);
blah.prototype.constructor = blah;
Something like that .. and I tried that in chrome dev tool but did not see them as equal.. Now trying to see what that code I did see was trying to accomplish? I assume there is more difference in Object create and new(than just missing constructor?)
function blah(){
//...
}
undefined
function somethingelse(){
//
}
undefined
somethingelse.prototype = {
// something else
}
Object {}
blah.prototype = Object.create(somethingelse.prototype);
Object {}
blah.prototype.constructor = blah;
blah(){
//...
}
blah == somethingelse;
false
You've created two different objects (blah and something).
I think you are confused about what inheritance means.
when you compare (==) 2 objects the reference is being evaluated - and it is different.
I am trying to create a simple helper function to automate javascript inheritance. Works like this:
var myClass = makeClass({
inherit: SomeSuperClass, //optional, obviously
constructor: function() {} // would like for this to be optional
anotherMethod: function(){} // just gets added to the prototype chain.
// etc
});
I have everything working wonderfully, and its pretty lightweight, but I've run across a bug that tells me I don't really understand what I'm doing. Working from the wonderful answer at https://stackoverflow.com/a/22316035/2475951 I have the following:
function makeClass(properties) {
// If the user doesn't supply a constructor, give them a generic function
if ( ! properties.constructor ){
properties.constructor = function(){};
}
if (properties.inherit) {
properties.constructor.prototype = Object.create(properties.inherit.prototype);
properties.constructor.prototype.constructor = properties.constructor;
}
return properties.constructor;
// Plus a simple loop to add the remaining methods given in properties to the prototype chain. Not important here
}
Now onto implementation. This works just as expected.
var Food = makeClass({
constructor: function(){}
});
var Bread = makeClass({
inherit: Food,
constructor: function(){}
});
var Sushi = makeClass({
inherit: Food,
constructor: function(){}
});
var bread = new Bread();
var sushi = new Sushi();
console.log(sushi instanceof Bread); // false
console.log(bread instanceof Sushi); // false
console.log(sushi.constructor); // [Function: Sushi]
console.log(bread.constructor); // [Function: Bread]
console.log(sushi instanceof Food); // true
console.log(bread instanceof Food); // true
console.log(sushi instanceof Sushi); // true
console.log(bread instanceof Bread); // true
My problem comes in one either Bread or Sushi don't supply a constructor. If Bread doesn't supply a constructor, thus using the generic function created in makeClass(), then:
(sushi instanceof Bread) become **true**
If Sushi doesn't supply a constructor then bread becomes an instance of Sushi. Why is this happening? I could understand if they both evaluate to true or false, but why does removing Bread's constructor affect the sushi instance? I would imagine my problem is in assigning an empty function to properties.constructor if it is void, but I don't know how else to do this.
If what I am wanting to do is not possible, or not best practice, I would like to know that too. Still, it seems like I'm missing something pretty basic. I've searched around SO and google for hours, and can't seem to find this same problem.
Thanks!
If Bread doesn't supply a constructor, thus using the generic function created in makeClass()
Not exactly. properties.constructor, for which you test in your condition, will (almost) always have a value: It does inherit .constructor from Object.prototype. This will make your makeClass() call return the Object function, and sushi is indeed an instanceof Object.
So this is one of the cases where we need to use the hasOwnProperty method:
…
// If the user doesn't supply a constructor, give them a generic function
if ( !properties.hasOwnProperty("constructor") ){
properties.constructor = function(){};
}
…
I'm building a Backbone Marionette Application. In Marionette you can do something like:
Marionette.Application.extend({
regions: {
mainRegion: function(){return someCondition?"#A":"#B"},
otherRegion: "#something"
}
})
I'm trying to implement this in my application with custom objects. What is the best way to achieve this?
Currently, I'm checking for every value:
if(typeof obj == "function") {
this.data.obj = obj();
}
else {
this.data.obj = obj;
}
or the corresponding ?: expression.
Is there an easier way to do this?
Underscore has a result method that does exactly what you want (see http://underscorejs.org/#result)
You can find the implementation here: http://underscorejs.org/docs/underscore.html#section-128
There is a potential solution (cue screaming) in adding value() [not to be confused with valueOf()] to every prototype you're interested in.
Function.prototype.value = function () { return this(); }; // Probably should be .call() so you can do arguments if necessary.
String.prototype.value = function () { return this; };
etc.
Then you can use this.data.obj = obj.value() for all calls.
If you're queasy about adding functions to prototypes, this isn't a solution you'd want to use. Otherwise you could write a wrapper function that takes obj as an argument and returns the right thing based on the type.
The duck typing idea proposed by dandavis is also good IMHO.
using prototype method we can create new methods... like...
Object.prototype.newMethod=function(){
// do something
}
Here I am defining the newMethod with an anonymous function... now if I want to use this method, I have to use it like: <object>.newMethod();
But now I want to create a new method which I can use like: <object>.newMethod;... no brackets... How can I do that...??
please don't use any jQuery...
Erm, you can't. To call a method, you write parentheses after it. Otherwise you're just referencing it.
The only exception to this rule is when you write something like new Date, where the parentheses are implict due to the new keyword and only because there are no arguments given.
I can't really understand why you would want to do that, but it is possible, albeit with a nasty hacky workaround. What you're actually looking for, AFAIK, is a magic property (like the someArray.length property).
var foo = {val:'foo'};
foo.length = (function(that)
{
return function()
{
return that.val.length;
}
})(foo);
//at this point foo.length(); returns 3, but still requires parentheses
//so, build another closure, and assign a valueOf method to the lenth method:
foo.length.valueOf = (function(method)
{
return function()
{
return method();//call the length method
}
})(foo.length);
console.log(foo.length +1);//logs 4
foo.val += 'bar';
console.log(foo.length);//logs 6
//BUT:: be carefull!!
alert(foo.length);//coerces to string, we haven't redefined the toString method, so the function code will be alerted
alert(foo.length + '');//alerts 6
This is just to show you that, yes it is theoretically possible, but please, please, don't use this kind of overly polluted hacks... I haven't thoroughly tested this, but ATM, I've already noticed that console.log(foo.length); can return a different value, not sure why, yet:
foo = {val:'foo'};
foo.length = (function(that){return function(){ return that.val.length;};})(foo);
foo.length.valueOf = (function(method){return function(){return method();};})(foo.length);
foo.length;//returns 3, great
foo.val += 'bar';
console.log(foo.length);//logged 3 at first, now it's back to logging 6!<-- don't trust this is the conclusion
The only way to call a function without parenthesis would be to define it using getters and setters.
Note these are new to JavaScript 1.8 and are not supported by all browsers.
So I am trying to learn javascript by learning how Mootools works internally. I am looking at these lines specifically:
var Type = this.Type = function(name, object){
if (name){
var lower = name.toLowerCase();
var typeCheck = function(item){
return (typeOf(item) == lower);
};
Type['is' + name] = typeCheck;
if (object != null){
object.prototype.$family = (function(){
return lower;
}).hide();
}
}
if (object == null) return null;
object.extend(this);
object.$constructor = Type;
object.prototype.$constructor = object;
return object;
};
//some more code
new Type('Type',Type);
What is happening here?
What is the object being assigned to in the constructor statement down at the bottom, the global window object?
Why is it being called as a constructor with the new statement when the Type function seems to only update the passed in object rather than creating a new one?
Specifically what is 'this' in the line object.extend(this); ? is it the global window object, if so is it adding all the key, value pairs of that object to the Type object?
Sheesh, questions lately seem to focus a lot more on mootools internals.
I will answer to the best of my knowledge as I am not a core dev.
Type in MooTools is pretty similar to Class (in fact, the Class constructor itself is a Type) but it focuses more on data / values and the Types of values. It also is meant to extend the native Types defined by ECMA spec and make them more flexible.
I guess there is no point in talking about data types in general (String, Number, Array, Object, etc). Why is there are need to extend them? Well, for starters, Duct Typing is a little quirky in js. typeof {}; // object, typeof []; // object, typeof new Date(); // object etc - not the most helpful, even if since all types inherit from object and is logical for them to be grouped together, it does not help you write code.
So, in the context of js objects, they are created from constructor objects...
What Type does is not replace the constructor functions but changes an existing constructor by adding new methods or properties to it.
eg. new Type('Array', Array);
This will turn the native Array constructor into a type object of sorts. You don't need to save the result or anything - it's a one off operation that mods the original and leaves it open for manipulation.
typeOf(Array); // type
So, what is different? Well, for starters, typeOf([]) is now able to actually tell us what it really is - array. And what really happens here is this: object.extend(Type);, the magical bit. It will copy to the target object all the properties defined on the Type object - you can see them here:
https://github.com/mootools/mootools-core/blob/master/Source/Core/Core.js#L211-232
So, immediately, your newly created Type gets the all important implement and extend methods.
More advanced, let's create a new type that is based on the native Array constructor:
var foo = new Type('foo', Array),
a = new foo();
// it's a real boy!
a.push('hi');
// ['hi'], foo, object
console.log(a, typeOf(a), typeof a);
But, what if we wanted a custom Type? One that is magical and special? No problem, because argument 2 can actually be a (anonymous) function.
var Awesome = new Type('awesome', function(name, surname) {
// console.log('I R teh construct0r!');
this.name = name;
this.surname = surname;
});
// extend it a little.
Awesome.implement({
getName: function() {
return this.name;
},
getSurname: function() {
return this.surname;
}
});
var Dimitar = new Awesome('dimitar', 'christoff');
console.log(typeOf(Dimitar)); // awesome
console.log(Dimitar.getName()); // dimitar
In another example, look at DOMEvent. It takes the above and makes it into a faster leaner Object type. https://github.com/mootools/mootools-core/blob/master/Source/Types/DOMEvent.js#L21
So why is that not a class? Because, Classes are more expensive and events happen all the time.
I hope this helps you somewhat. for a more detailed explanation, ask on the mailing list and hope for the best, perhaps a core dev will have time as it's not your standard 'how do I get the accordion working' type of question...