Skip to content

Instantly share code, notes, and snippets.

@csalzman
Created January 4, 2017 15:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save csalzman/1e02380398cc8f4a5a43c4d630cd09b1 to your computer and use it in GitHub Desktop.
Save csalzman/1e02380398cc8f4a5a43c4d630cd09b1 to your computer and use it in GitHub Desktop.
<script type="text/javascript">
/*
Walking through this page as a tutorial on how Javascripts object model works:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model#Class-Based_vs._Prototype-Based_Languages
*/
//Function for Employee creates a constructor for Employee objects
function Employee() {
this.name = "";
this.dept = "General";
this.title = "";
}
//Manager is going to inherit from Employee and add a reports property
function Manager() {
Employee.call(this);
this.reports = [];
//Setting the title property that Employee setup
this.title = "Manager";
}
//***I am unsure what this does.***
Manager.prototype = Object.create(Employee.prototype);
//Create an instance of an object using the Manager constructor
var mintedManager = new Manager();
document.write(mintedManager.title);
</script>
@csalzman
Copy link
Author

csalzman commented Jan 4, 2017

Line 23 (and to some extent 16) is where I'm getting confused. I get that it's necessary, but don't understand why. It feels like a magic spell to me right now.

My read on this is that instead of inheriting from Object.prototype directly, this is setting Manager to inherit from Employee.prototype instead (which in turn inherits from Object.prototype up the chain). When you create an instance using the Manager constructor it then adds all (makes available?) the properties of Employee to Manager.

Is 23 is declaring that Manager.prototype should use the Employee prototype's objects and properties? Or is it extending Manager.prototype to include Employee? Why do we need 23 at all if we're calling Employee on 16? call() seems to me to bring along the properties of the Employee constructor.

And if you do that why does creating a new Manager instance have both the Employee and the Manager properties?

@hypomodern
Copy link

So line 16 Employee.call(this) calls the Employee() function from line 8 with a specific this value, namely the same this from the Manager() function. The result of Employee() in that case sets the name, dept, and title attributes on this. That's all line 16 does.

Line 23 sets the Manager's prototype to an object that inherits from Employee.prototype. If there were functions associated with the Employee prototype, this is how Manager would gain access to them. e.g.:

Employee.prototype.printTitle = function() {
  document.write(this.title);
};

Line 23 ensures that Managers also have the printTitle function in their prototype chain.

@keller
Copy link

keller commented Jan 4, 2017

I love and hate JS prototype inheritance. I think it's super cool, but a mess of an api. It feels to me like it's trying to trick devs into thinking it's like classical inheritance, which they successfully do trick devs into being confused. I think the biggest (recent) mistake was to add es6 classes, and hiding proto inheritance more, rather than adding a better proto inheritance api.

@hypomodern is correct that Employee.call(this) is used to "call" the Employee() "function" but it's a little confusing because Employee isn't a normal function. It's a special JS constructor function so in this case Employee.call(this) is used to chain constructors. So when new Manager() is called, it also runs the Employee() constructor function, sending it's this and thus setting this.name, this.dept, and this.title for the new manager object.

Line 23 is creating the new manager object's prototype to be a newly created object with it's prototype the same as the Employee prototype, which I think by default is Object. So your code really doesn't need that line since setting a prototype is basically empty. @hypomodern is correct that setting a prototype for Employee will allow that line to be useful.

function Employee() {
	this.name = "";
	this.dept = "General";
	this.title = "";
}

Employee.prototype.company = 'Acme';

function Manager() {
	Employee.call(this);
	this.reports = [];
	this.title = "Manager";
}
//***This line will set the Employee.prototype in the Manager prototype chain ***
Manager.prototype = Object.create(Employee.prototype);

var mintedManager = new Manager();
document.write(mintedManager.company);

@keller
Copy link

keller commented Jan 4, 2017

Alternatively you could do something like:

function Employee() {
	this.name = "";
	this.dept = "General";
	this.title = "";
}

Employee.prototype.company = 'Acme';

function Manager() {
	this.reports = [];
	this.title = "Manager";
}

Manager.prototype = new Employee();
var mintedManager = new Manager();
document.write(mintedManager.company);

But this is slightly different, it sets the new Employee object as the actual prototype.

@keller
Copy link

keller commented Jan 4, 2017

I think I can see reasons to mix new and object.create but I think it's a bit confusing to do that. Generally I like to use one or the other, I prefer Object.create and object literals. For example you can have a similar prototype chain with this code:

var employeeProto = {
  name: "",
  dept: "General",
  title: ""
};


function newManager() {
  var manager = Object.create(employeeProto);
  manager.title = "Manager";
  return manager;
}


var mintedManager = newManager();

document.write(mintedManager.dept);

@csalzman
Copy link
Author

csalzman commented Jan 4, 2017

@hypomodern I thanked you on twitter too, but publicly (on this gist that will probably die someday in a gist purge) thank you!

@keller, thanks! Your object literal example makes a lot more sense to me given how its used in this example. It's helpful to understand the options!

"It feels to me like it's trying to trick devs into thinking it's like classical inheritance, which they successfully do trick devs into being confused" I've really really been feeling this as I'm learning about this. I'd actually started looking into it after reading a bit about classes in es6 and wondering what was making them work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment