3

I'm trying to better understand inheritance in javascript. My goal is to write a function that declares some variables and sets up an environment so that other functions can build off of it. I'm kind of thinking along the lines of mixins in Ruby. Here is a simple example that is not working.

function baseFunction(){

  this.my_var = "hello world";

};

function speakMessage(){

  alert(this.my_var);

}

speakMessage.prototype = baseFunction();

speakmessage();

I thought if I set the prototype the baseFunction than any properties not found in the speakMessage function will be searched for in the baseFunction. Is that not right? How cna I get this to work?

asolberg
  • 6,638
  • 9
  • 33
  • 46
  • 1
    Do you know what closures are? – kirugan Jan 03 '14 at 19:01
  • This code doesn't do what you think it does. When you call `baseFunction()`, `this` will actually be `window`! You need to use the `new` keyword. `speakMessage.prototype = new baseFunction();`, and then `new speakMessage();` – gen_Eric Jan 03 '14 at 19:10
  • This is an interesting subject, the combination of prototype. i am use to just using new .... anyone have a clue where i can read about it farther ? – Neta Meta Jan 03 '14 at 19:13
  • The following answer may help you understand how to create instances using a constructor function and how one constructor function can inherit from another through prototype and invoking `Parent.apply(this,arguments);` http://stackoverflow.com/a/16063711/1641941 – HMR Jan 04 '14 at 04:14
  • @NetaMeta You are correct, you should use new if you'd like to speakMessage to inherrit from baseFunction. Currently setting speakMessage.prototype to the result of baseFunction (=undefined) wil set window.my_var (because calling baseFunction with window as the invoking object) and later alert "Hello World" because speakMessage is called with `window` as the invoking object. Got nothing to do with speakMessage inheriting anything from baseFunction. – HMR Jan 04 '14 at 04:24

3 Answers3

4

Trivial answer, but... speakMessage(); (big M) :-)

Javascript is case-sensitive.

And great comment from HMR I missed: functions act like constructors if used with new in Javascript. Note the two new I added to your code:

my_var = "goodbye world";

function baseFunction() {
  this.my_var = "hello world";
}

function speakMessage() {
  alert(this.my_var);
}

speakMessage.prototype = new baseFunction(); // new: inherit from object
new speakMessage(); // object constructor: hello world
speakMessage(); // function: goodbye world from window object context

Also have a look at HMR's answer.

Jan Turoň
  • 31,451
  • 23
  • 125
  • 169
  • @asolberg exactly :-) making my answer less brief, you could look at [bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) which enables you to play with `this`. Also have a look at [apply()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) and call functions. – Jan Turoň Jan 03 '14 at 19:16
  • 1
    The only reason that works is because `this` is window and setting speakMessage.prototype to undefined (because baseFunction doesn't return anything) does not make speakMessage inherit anyting. All it does is set window.my_var and later alert it because speakMessage is called with `window` as the invoking object. Got nothing to do with speakMessage inheriting anything from baseFunction. – HMR Jan 04 '14 at 04:18
  • @HMR: I missed it, great comment! I updated my answer to clarify how the context works and upvoted your answer for clarifying it even more. – Jan Turoň Jan 04 '14 at 08:47
  • 1
    @HMR - Thanks. I understand it much more clearly now. At first I was approaching from the wrong angle of "mixing" in functions ala Ruby style but I see now Javascript is prototypal and only through setting prototypes can you shape the prototype chain which will then determine your inheritance hierarchy. – asolberg Jan 11 '14 at 17:38
2

Tiny error. You called speakmessage(); when you created the function speakMessage(){}

This should fix it.

function baseFunction(){

  this.my_var = "hello world";

};

function speakMessage(){

  alert(this.my_var);

}

speakMessage.prototype = baseFunction();

speakMessage();
steve
  • 153
  • 1
  • 10
  • The fact that it gives the expected output does not make it work. Question suggest OP would like to inherit. My guess speakMessage should inherit from baseFunction. – HMR Jan 04 '14 at 04:26
2

For future stumblers upon; the answers given before only address a typo in the OP's question. As for speakMessage inheriting from baseFunction the OP's code is completely wrong and should/could look something like this:

(note that constructor functions should start with a capital letter)

function BaseFunction(args){
  //name is instance specific
  //defaults to World
  this.name = (args&&args.name)?args.name:"World";
};
//shared members 
BaseFunction.prototype.saySomething=function(){
  return "From BaseFunction:"+this.name;
};

function SpeakMessage(args){
  //re use BaseFunction constructor code
  BaseFunction.call(this,args);
}
//inherit shared members from prototype
SpeakMessage.prototype = Object.create(BaseFunction.prototype);
//repair built in constructor value (or it'll point to BaseFunction)
SpeakMessage.prototype.constructor=SpeakMessage;
//extend saySomething, this is optional an shows how
// to add extra functionality to existing Parent functions
SpeakMessage.prototype.saySomething=function(){
  return BaseFunction.prototype.saySomething.call(this) + 
    ", from SpeakMessage:"+this.name;
};

var world = new SpeakMessage();//defaults name to be "world"
//following will output: From BaseFunction:World, from SpeakMessage:World
console.log(world.saySomething());
var op = new SpeakMessage({name:"OP"});
//following will output:From BaseFunction:OP, from SpeakMessage:OP
console.log(op.saySomething());
var parent = new BaseFunction();
console.log(parent.saySomething());//=From BaseFunction:World

As in comments; for more info about constructor functions and prototype: https://stackoverflow.com/a/16063711/1641941

As for the comment mentioning closures; you can use closures instead of constructor functions for simpler objects:

var baseFunction=function(message){
  return function(){
    console.log("Hello "+message);
  };
}

var world = baseFunction("World");
var op = baseFunction("OP");
world();//=Hello World
op();//=Hello OP

More on closures here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Could you use just `new` instead of `Object.create` if you don't use properties, or is there any other difference? Looking at your `SpeakMessage.prototype.constructor=SpeakMessage;` - I believe parent should not be aware of his childs in good design, although Javascript enables it. – Jan Turoň Jan 04 '14 at 09:05
  • @JanTuroň constructor is a property of prototype that comes with JS. `var t=function();t.prototype.constructor===t;//true` but because you set `SomeConstructor.prototype=somethingElse` you overwrite that value, that's why you repair it. Parent is not aware of it's Child but Child is of it's Parent. For easier refactoring you could use helper functions to set this up. I will add a bit to this answer http://stackoverflow.com/a/16063711/1641941 on trickyness that comes with. – HMR Jan 04 '14 at 12:16
  • @JanTuroň As for using new, it's not a good idea because Parent may have instance specific members that are put on Child's prototype with no use (best case they are shadowed when you do Parent.call(this) in Child body) http://stackoverflow.com/q/20915711/1641941. Another reason is that Parent may have time and resource consuming code that doesn't need to be called or can't be called (some checks can't be resolved without needlessly complicating your code). If Parent's body is empty I guess you can use it but may have to re do when you refactor Parent code – HMR Jan 04 '14 at 12:49
  • Wow! You wrote a book in that SO answer in your comment! I bet this is not the first time you've seen Javascript :-) Well done. – Jan Turoň Jan 04 '14 at 16:39
  • @JanTuroň Thank you, I find SO very helpful and wish it was there 12 years ago. When I'm trying to figure out something I end up writing it as an answer and hope others would improve, correct or bring it up to date. – HMR Jan 05 '14 at 01:36
  • Thanks @HMR - very thorough. – asolberg Jan 11 '14 at 17:42