OOP in JS - Inheritance
Summary
- You cause a class to inherit using
ChildClassName.prototype = new ParentClass();. - You need to remember to reset the
constructorproperty for the class usingChildClassName.prototype.constructor=ChildClassName. - You can call ancestor class methods which your child class has overridden using the
Function.call()method. - Javascript does not support protected methods.
Example
To jump right into it, following is a sample showing inheritance between two classes:
function Mammal(name){
this.name=name;
this.offspring=[];
}
Mammal.prototype.haveABaby=function(){
var newBaby=new Mammal("Baby "+this.name);
this.offspring.push(newBaby);
return newBaby;
}
Mammal.prototype.toString=function(){
return '[Mammal "'+this.name+'"]';
}
Cat.prototype = new Mammal(); // Here's where the inheritance occurs
Cat.prototype.constructor=Cat; // Otherwise instances of Cat would have a constructor of Mammal
function Cat(name){
this.name=name;
}
Cat.prototype.toString=function(){
return '[Cat "'+this.name+'"]';
}
var someAnimal = new Mammal('Mr. Biggles');
var myPet = new Cat('Felix');
alert('someAnimal is '+someAnimal); // results in 'someAnimal is [Mammal "Mr. Biggles"]'
alert('myPet is '+myPet); // results in 'myPet is [Cat "Felix"]'
myPet.haveABaby(); // calls a method inherited from Mammal
alert(myPet.offspring.length); // shows that the cat has one baby now
alert(myPet.offspring[0]); // results in '[Mammal "Baby Felix"]'
The someAnimal & myPet above inner logical struct:

Using the .constructor property
Look at the last line in the above example. The baby of a Cat should be a Cat, right? While the haveABaby() method worked, that method specifically asks to create a new Mammal. While we could make a new haveABaby() method for the Cat subclass like this.offspring.push(new Cat("Baby "+this.name)), it would be better to have the ancestor class make an object of the correct type.
Every object instance in JS has a property named constructor that points to its parent class. For example, someAnimal.constructor==Mammmal is true. Armed with this knowledge, we can remake the haveABaby() method like this:
Mammal.prototype.haveABaby=function(){
var newBaby=new this.constructor("Baby "+this.name);
this.offspring.push(newBaby);
return newBaby;
}
...
myPet.haveABaby(); // Same as before: calls the method inherited from Mammal
alert(myPet.offspring[0]); // Now results in '[Cat "Baby Felix"]'

Calling 'super' methods
Let's extend the example now so that when baby kittens are created, they 'mew' right after being born. To do this, we want to write our own customCat.prototype.haveABaby() method, which is able to call the original Mammal.prototype.haveABaby() method:
Cat.prototype.haveABaby=function(){
Mammal.prototype.haveABaby.call(this);
alert("mew!");
}
The above may look a little bit bizarre. Javascript does not have any sort of 'super' property, which would point to its parent class. Instead, you use thecall() method of a Function object, which allows you to run a function using a different object as context for it.
Making your own 'super' property
Rather than having to know that Cat inherits from Mammal, and having to type in Mammal.prototype each time you wanted to call an ancestor method, wouldn't it be nice to have your own property of the cat pointing to its ancestor class? Those familiar with other OOP languages may be tempted to call this property 'super', but JS reserves this word for future use. The word 'parent', while used in some DOM items, is free for the JS language itself, so let's call it parentin this example:
Cat.prototype = new Mammal();
Cat.prototype.constructor=Cat;
Cat.prototype.parent = Mammal.prototype;
...
Cat.prototype.haveABaby=function(){
var theKitten = this.parent.haveABaby.call(this);
alert("mew!");
return theKitten;
}
Spoofing pure virtual classes
Some OOP languages have the concept of a pure virtual class...one which cannot be instantiated itself, but only inherited from. For example, you might have aLivingThing class which Mammal inherited from, but you didn't want someone to be able to make a LivingThing without specifying what type of thing it was. You can do this in JS by making the virtual class an object instead of a function.
The following example shows how this could be used to simulate a pure virtual ancestor:
LivingThing = {
beBorn : function(){
this.alive=true;
}
}
...
Mammal.prototype = LivingThing;
Mammal.prototype.parent = LivingThing; //Note: not 'LivingThing.prototype'
Mammal.prototype.haveABaby=function(){
this.parent.beBorn.call(this);
var newBaby=new this.constructor("Baby "+this.name);
this.offspring.push(newBaby);
return newBaby;
}
With the above, doing something like var spirit = new LivingThing() would result in an error, since LivingThing is not a function, and hence can't be used as a constructor.
Convenient Inheritance
Rather than writing 3 lines every time you want to inherit one class from another, it's convenient to extend the Function object to do it for you:
Function.prototype.inheritsFrom = function( parentClassOrObject ){
if ( parentClassOrObject.constructor == Function )
{
//Normal Inheritance
this.prototype = new parentClassOrObject;
this.prototype.constructor = this;
this.prototype.parent = parentClassOrObject.prototype;
}
else
{
//Pure Virtual Inheritance
this.prototype = parentClassOrObject;
this.prototype.constructor = this;
this.prototype.parent = parentClassOrObject;
}
return this;
}
//
//
LivingThing = {
beBorn : function(){
this.alive = true;
}
}
//
//
function Mammal(name){
this.name=name;
this.offspring=[];
}
Mammal.inheritsFrom( LivingThing );
Mammal.prototype.haveABaby=function(){
this.parent.beBorn.call(this);
var newBaby = new this.constructor( "Baby " + this.name );
this.offspring.push(newBaby);
return newBaby;
}
//
//
function Cat( name ){
this.name=name;
}
Cat.inheritsFrom( Mammal );
Cat.prototype.haveABaby=function(){
var theKitten = this.parent.haveABaby.call(this);
alert("mew!");
return theKitten;
}
Cat.prototype.toString=function(){
return '[Cat "'+this.name+'"]';
}
//
//
var felix = new Cat( "Felix" );
var kitten = felix.haveABaby( ); // mew!
alert( kitten ); // [Cat "Baby Felix"]
Just make sure you call this method immediately after your constructor, before you extend the prototype for the object.
Protected methods?
Some OOP languages have the concept of 'protected' methods—methods that exist in a parent or ancestor class that can only be called by descendants of the object (on each other), but not by external objects. These are not supported in JS. If you need such, you will have to write your own framework, ensuring that each class has a 'parent' or some such property, and walking up the tree to find ancestors and checking whether or not the calling object is the same type. Doable, but not enjoyable.
Quote From:
OOP in JS, Part 2 : Inheritance
Reference:
Understanding JavaScript Function Invocation and "this"
OOP in JS - Inheritance的更多相关文章
- OOP in JS Public/Private Variables and Methods
Summary private variables are declared with the 'var' keyword inside the object, and can only be acc ...
- js inheritance all in one
js inheritance all in one prototype & proto constructor Object.definepropety Object.create() js ...
- JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)
一.面向对象编程(继承) 这篇博客是面向对象编程的第三篇,JS继承.继承顾名思义,就是获取父辈的各种"财产"(属性和方法). 怎么实现继承? 我们的JavaScript比较特别了, ...
- JS OOP -04 JS中的公有成员,私有成员和静态成员
JS中的公有成员,私有成员和静态成员 a.实现类的公有成员 b.实现类的私有成员 c.实现类的静态成员 a.实现类的公有成员 之前定义的任何类型成员都属于公有成员的范畴,该类的任何实例都对外公开这些属 ...
- JS OOP -03 JS类的实现
JS类的实现: a.理解类的实现机制 b.使用prototype对象定义类成员 c.一种JS类的设计模式 a.理解类的实现机制 在JS中可以使用function关键字来定义一个类. 添加类的成员,在函 ...
- 小结JS中的OOP(上)
前言:大家都知道,OOP有三大特性:封装,继承,多态.下面是自己对这三个特性的理解: 封装:把属性与方法整合到某种数据类型中.目的是让类的使用者按类的编写者的意愿去使用类.在封装过程中会一般会做两件事 ...
- JS面向对象思想(OOP)
直接看js好了,模拟创建一个奥运会 function 奥运会Class(主题) { // 删除主题 // delete this.主题; this.主题 = 主题; this.开幕时间; this.闭 ...
- 三言两语之js面向对象初探1
http://www.cnblogs.com/54td/p/5580994.htm 先是有了这个比较简短但是内容比较丰盈的上篇,现在时间比较充沛,所以详细写来.搞前端的同学经常被其他程序员bs, ...
- 关于JS APP
多屏screen, JS如何路由,如何换页,导航.通过JS来实现. 当前页面的逻辑通过JS来实现.HTML DOM, Event, Widget. 核心在于function. JS 不仅仅是DOM, ...
随机推荐
- DateTimePicker.Text不靠谱
DateTimePicker.Text不靠谱 获取时:在DateTimePicker.ValueChanged事件中,获取到的Text有可能是string.Empty!!!,特别当ValueChang ...
- hdu1205(类似 分布垃圾数列)
Problem Description A Fibonacci sequence is calculated by adding the previous two members the sequen ...
- 使用Android网络编程实现简易聊天室
在Java中我们可以利用socket编程实现聊天室,在Android中也一样,因为Android完全支持JDK本身的TCP.UDP网络通信API.我们可以使用ServerSocket.Socket来建 ...
- Ubuntu 下的环境变量配置
网上很多配置jdk环境变量的方法,但是几乎都会下次重启电脑就失效,或者时不时的失效.下面教你一招 JDK环境变量配置如下: 执行命令sudo gedit /etc/environment,在打开的编辑 ...
- C++ 头文件系列(array)
注意,该头文件仅在C++11中标准才开始出现. 简介 与语言内置的数组一样, array类模版支持几乎所有内置数组包含的特性: 顺序的(sequence) 内存连续的(contiguous stora ...
- [Thinkbayes]贝叶斯思维读书笔记-2-火车头问题
今天,我们继续我们的笔记. 作者在第三章继续举了一个例子.火车头问题(读者在此可能会觉得这个问题没有意义,但相信随着深入阅读,这个问题会被解答). 这个举例恰到好处,能够让我深入理解到底应该如何假设, ...
- this和$(this)
this指的是javascript对象而$(this)就是就jquery对象 jQuery.prototype.test=function(){ this.css("color", ...
- eclipse中svn插件的安装和tortoiseSVN的安装
在项目的开发中一般都需要用到版本控制,其中用的比较多的就是svn(subVersion),svn的使用既可以以插件的形式,也可以以客户端的形式使用.今天就介绍一下eclipse中svn插件的2种安装方 ...
- MongoDB应用案例:使用 MongoDB 存储日志数据
线上运行的服务会产生大量的运行及访问日志,日志里会包含一些错误.警告.及用户行为等信息,通常服务会以文本的形式记录日志信息,这样可读性强,方便于日常定位问题,但当产生大量的日志之后,要想从大量日志里挖 ...
- C#Redis列表List
一.前戏 在Redis中,List类型是按照插入顺序排序的字符串链表.和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素.在插入时,如果该键并不存在,Redis将 ...