This leads me to the most common approach which does correctly set up prototype chain but has a few problems:
function Animal(name) { this.name = name; } // This style of setting the prototype to an object // works for classes that inherit from Object. Animal.prototype = { sayMyName: function() { console.log(this.getWordsToSay() + " " + this.name); }, getWordsToSay: function() { // Abstract } } function Dog(name) { // Call the parent's constructor Animal.call(this, name); } // Setup the prototype chain mmm... calling // the Animal without the required params? Dog.prototype = new Animal(); Dog.prototype.getWordsToSay = function(){ return "Ruff Ruff"; } var dog = new Dog("Lassie"); dog.sayMyName(); // Outputs Ruff Ruff Lassie console.log(dog instanceof Animal); // true console.log(dog.constructor); // Animal ???? That's not right console.log("name" in Dog.prototype)// true, but undefined
Alright, what's going on?
- Dog.prototype now has a property called "name" that is set to undefined.
That wasn't intentional. I knew that call to Animal's constructor was funny. Though that won't cause a problem, because we add a "name" to the object in the constructor, it's not very elegant - dog (the instance) has a constructor property but it points to Animal,
that's just wrong
How can we fix that? Here's a first try
// This is a constructor that is used to setup inheritance without // invoking the base's constructor. It does nothing, so it doesn't // create properties on the prototype like our previous example did function surrogateCtor() {} function extend(base, sub) { // Copy the prototype from the base to setup inheritance surrogateCtor.prototype = base.prototype; // Tricky huh? sub.prototype = new surrogateCtor(); // Remember the constructor property was set wrong, let's fix it sub.prototype.constructor = sub; } // Let's try this function Animal(name) { this.name = name; } Animal.prototype = { sayMyName: function() { console.log(this.getWordsToSay() + " " + this.name); }, getWordsToSay: function() { // Abstract } } function Dog(name) { // Call the parent's constructor Animal.call(this, name); } // Setup the prototype chain the right way extend(Animal, Dog); Dog.prototype.getWordsToSay = function(){ return "Ruff Ruff"; } var dog = new Dog("Lassie"); dog.sayMyName(); // Outputs Ruff Ruff Lassie console.log(dog instanceof Animal); // true console.log(dog.constructor); // Dog console.log("name" in Dog.prototype)// false
Nice isn't it? Let's add some syntactic sugar to make it more user friendly.
- Add a reference to the base class so we don't have to hard code it
- Pass the object's prototype methods into the call
function surrogateCtor() {} function extend(base, sub, methods) { surrogateCtor.prototype = base.prototype; sub.prototype = new surrogateCtor(); sub.prototype.constructor = sub; // Add a reference to the parent's prototype sub.base = base.prototype; // Copy the methods passed in to the prototype for (var name in methods) { sub.prototype[name] = methods[name]; } // so we can define the constructor inline return sub; } // Use the same animal from above function Dog(name) { // Call the parent's constructor without hard coding the parent Dog.base.constructor.call(this, name); } extend(Animal, Dog, { getWordsToSay: function(){ return "Ruff Ruff"; } });
One more step, I don't even like hard coding the name of the class in the methods in case I rename it, how can we fix that?
The solution is to wrap everything in a self executing function and create a reference to the constructor
Dog = (function(){ // $this refers to the constructor var $this = function (name) { // Look, no hardcoded reference to this class's name $this.base.constructor.call(this, name); }; extend(Animal, $this, { getWordsToSay: function(){ return "Ruff Ruff"; } }); return $this; })();
With this final approach, renaming the class or changing its parent requires changing a single place (for each change).
Update
This article was written a while ago, when Object.create wasn't as well supported. The extend function could be simplified to be
function extend(base, sub, methods) { sub.prototype = Object.create(base.prototype); sub.prototype.constructor = sub; sub.base = base.prototype; // Copy the methods passed in to the prototype for (var name in methods) { sub.prototype[name] = methods[name]; } // so we can define the constructor inline return sub; }
Hello, Juan!
ReplyDeleteI get one mistake in your code:
Animal.prototype = {
sayMyName = function() {
'=' must be replaced with ':'
Also I want ask you a question: why i can't change method if it declared as 'this.method =' instead of 'Animal.prototype.method = '?
http://jsfiddle.net/m7m9E/
ps. It is important to decclare method in constuctor caus of using some "private" variables.
With best regards,
Dmitry
Hello Dmitry, sorry it took me so long to reply. I've fixed the mistake you pointed out, thanks. If I understand it right, you would like to declare methods from the constructor. That is fine, but you need to understand that methods declared from the constructor modify the object directly. In your example, your base (Animal) defines getWordsToSay on the object itself. And your base class declares getWordsToSay on the prototype. Therefore, when you create an instance of Dog, when the Animal constructor is run, it creates getWordsToSay on the object itself and the one you attached to Dog.prototype can't be reached. However, if you define getWordsToSay from Dog's constructor, after running Animal's constructor, then you're overwriting it also directly on the object and you should achieve the desired effect.
ReplyDeleteI think the lesson is, if you want something that is overridable, you should not set it from the constructor, you should set it on the prototype.
http://jsfiddle.net/mendesjuan/m7m9E/1/
Just out of curiosity: Why do you use "sub.base = base.prototype;" instead of "sub.prototype.base = base.prototype;" in your "extend" function? If you use the latter, you can directly use "this.base.constructor" in the constrcutor do not need to create a complicated function object to avoid naming the subclass in its constructor.
ReplyDelete@shiin but that would still require naming the class twice: once in its function constructor definition and again as an argument to 'extends'.
ReplyDeleteWhat I don't understand is how 'sub.base.constructor' is set. According to MDN, '.constructor', "Returns a reference to the Object function that created the instance's prototype." But 'Animal' is not an instance of an Animal, but instead is a Function, so its prototype was created by Function, and therefore its constructor should be 'Function.prototype' (this corresponds with what I'm seeing in Firefox.) So 'base.constructor.call' will not call Animal's constructor, but instead Function's constructor. What I'm using is: 'sub.base = base;' in 'extends' and '$this.base.call(this, arguments)' in the sub class's constructor. Seems to be working.
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor
Thanks for the article. I used it to construct the following method. It allows a much simpler syntax. In the prototype definition, there is a special function called _ctor that it will use for the constructor method.
ReplyDeletevar Base = createClass({
baseAttr: "",
_ctor: function (param1, param2) {
// ...
},
func1: function () {
}
});
var Derived = createClass(Base, {
derivedAttr: "",
_ctor: function (param1) {
this.baseCtor(param1, "hardcoded");
},
func3: function () {
}
});
function createClass(base, prototype) {
// If the prototype parameter is passed as the only argument,
// we need to reverse the base and prototype parameters.
if (typeof (base) == "object") {
prototype = base;
base = null;
}
var constructor = prototype._ctor;
// If the passed in prototype does not have a constructor, we
// need to define a default one.
if (constructor == undefined) {
constructor = function () { };
} else {
delete prototype._ctor;
}
// We want to start with a blank prototype and copy the
// bases prototype if a base is specified.
var newPrototype = new function CTor() { };
if (base) {
newPrototype = base.prototype;
// Save a reference to the base constructor so that it
// can be called from the derived class.
newPrototype.baseCtor = base.prototype.constructor;
}
newPrototype.constructor = constructor;
constructor.prototype = newPrototype;
// Copy the new class's prototype to the prototype for
// the constructor.
for (var key in prototype) {
constructor.prototype[key] = prototype[key];
}
return constructor;
}
Hi, thanks for sharing. I noticed that when creating the Animal function prototype you used object syntax rather than Animal.prototype.sayMyName() and Animal.prototype.getWordsToSay(). In both cases the prototype object is created but one is of type Animal and the other is a standard object, is this an issue?
ReplyDelete@Simon Since Animal doesn't extend anything else, we would not be overriding any existing methods. If I used object syntax on `Dog.prototype = {}`, I would be breaking the inheritance of Animal's prototype. That is why I suggested a last method that allows you to specify an object that will augment (not override) the existing prototype. See http://jsfiddle.net/mendesjuan/5bz008jw/ and you'll see that Dog would not be extending Animal
ReplyDeleteGreat article!
ReplyDeleteJust one question. In your article "Update", shouldn't the following line:
sub.prototype = Object.create(sub.prototype);
be
sub.prototype = Object.create(base.prototype);
?
@JazzyJ: Thanks for the correction, it's been applied.
ReplyDeleteBài viết hét sức tuyệt vời
ReplyDeletehttps://ngoctuyenpc.com/man-hinh-may-tinh-24-inch
https://ngoctuyenpc.com/mua-ban-may-tinh-cu-ha-noi
https://ngoctuyenpc.com/mua-ban-may-tinh-laptop-linh-kien-may-tinh-cu-gia-cao-tai-ha-noi
https://ngoctuyenpc.com/cay-may-tinh-cu
ok anh hai
ReplyDeletehttps://bonngamchan.vn/may-massage-chan-hang-nhat-co-tot-nhu-loi-don/
https://bonngamchan.vn/
https://bonngamchan.vn/danh-muc/bon-ngam-chan/
https://bonngamchan.vn/may-ngam-chan/
Điều anh chia sẻ hết sức tuyệt vời
ReplyDeleteDịch vụ vận chuyển chó mèo cảnh Sài Gòn Hà Nội
Chuyên dịch vụ phối giống chó Corgi tại Hà Nội
Phối chó Bull Pháp
ok yes
ReplyDeletemáy xông tinh dầu phun sương bottle cap
may xong phong ngu
may xong huong
may phun suong nao tot
ok a
ReplyDeletemáy tính hà nội
màn hình máy tính
mua máy tính cũ
màn hình máy tính cũ
Roku provides the simplest way to stream entertainment to your TV. On your terms. With thousands of available channels to choose from. visit roku link now. Activate your device by visit url: roku.com/link.
ReplyDeleteSite :- https://rokuclink.com/
Nice Post Buddy.
ReplyDeleteJavascript get element by class
Life Status in Hindi
ReplyDeleteStatus
Thanks for sharing this informative post. Looking forward to read more.
ReplyDeleteBest Web Design and Development Company
Thank you for sharing, I am very happy to know your blog. Here are some blogs I would like to send you. Please don't delete the link, hi
ReplyDeletemo da cong giao
game nổ hũ đổi thưởng
game bắn cá đổi thưởng
làm cavet xe uy tín
Thanks for sharing this informative post. Looking forward to read more.
ReplyDeleteBest Web Design and Development Company in Delhi/NCR
Love Shayari
ReplyDeleteWith today's modern society, the demanding needs of people are increasing. Not only beauty, eating and playing, but choosing a child's bedroom also requires a lot of factors. Because the bedroom is a place to rest, relax, study and sometimes also play a place for your baby. More: Phòng ngủ trẻ em, Giường tầng bé trai, Nội thất trẻ em
ReplyDeleteHi there, just became alert to your blog through Google, and found that it’s really informative. I am gonna watch out for brussels. I’ll be grateful if you continue this in future. Numerous people will be benefited from your writing. Cheers! Unique Dofollow Backlinks
ReplyDeleteVery nice blog and post, I found it informative and useful, thank you for kindly share it with us, best regards and take care
ReplyDeleteSandro from Nomes
This post is so usefull and attracive.keep updating with more information...
ReplyDeleteRPA Training in Mumbai
RPA Training in Ahmedabad
RPA course in Kochi
Robotic Process Automation Training in Trivandrum
RPA Course in Kolkata
mini havanese puppy
ReplyDeletechocolate havanese puppy for sale
thx sir all posting so good i love u for all info
ReplyDeleteVenüsbet
Venüsbet
Grandbetting
Grandbetting
Galabet
Meritking
Asyabahis
good my blog spot nice post info thx sir
ReplyDeleteVenüsbet
Venüsbet
Grandbetting
Grandbetting
Galabet
Meritking
Asyabahis
Hi there
ReplyDeleteVery nice post and blog, keep sharing the best content with us, hope to read more interesting articles like this one around here
take care and regards
Your follower
Salvatore from Macuco Safari - Passeio de Barco
Appreciating the persistence you put into your blog and detailed information you provide. Read: business central manufacturing
ReplyDeleteSMM PANEL
ReplyDeletesmm panel
https://isilanlariblog.com
instagram takipçi satın al
Hırdavatçı burada
Beyazesyateknikservisi.com.tr
Servis
tiktok jeton hile
To begin, write a list of everything your lecturers have given you as research proposal writing advice so you don't forget anything. Second, begin working on research ideas as soon as you're given a job so you can double-check them before submitting them. Finally, rather than attempting to tackle the problem on your own, Buy Research Proposal Online Hobart It helps you save time and energy while also lowering your risk of making mistakes. With SourceEssay, university experts are available to assist with college papers.
ReplyDeleteОборудование для офисов и минитипографий
ReplyDeleteI came across your blog and wanted to take a moment to express my appreciation for the quality of your content. Your writing is informative, insightful, and engaging, and I find myself returning to your blog time and time again for fresh perspectives on a variety of topics.
ReplyDeleteHow can I connect two computers with an Ethernet cable?