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, ...
随机推荐
- Go语言Web框架gwk介绍 3
Go语言Web框架gwk介绍 (三) 上一篇忘了ChanResult ChanResult 可以用来模拟BigPipe,定义如下 type ChanResult struct { Wait syn ...
- 第一个windows 小游戏 贪吃蛇
最近用dx尝试做了一个小的贪吃蛇游戏,代码放到github上面:https://github.com/nightwolf-chen/MyFreakout 说一下自己实现的过程: 首先,我把蛇这个抽象成 ...
- LAMP on ubuntu12.04 PHP, Apache2, MySQL, Linux ( with phpmyadmin installed)
there are several procedure which include: 1. Install the packages sudo apt-get install php5 php5-gd ...
- [Android开发常见问题-11] Unable to execute dex: Multiple dex files define 解决方法
最近在开发一个工程,其中用到了一个开源的库项目Android-ViewPagerIndicator. 这个项目是作为一个库出现的,如下图: 这个项目中包含了android-support-v4.jar ...
- hdu 2828 Buy Tickets(线段树)
一道不算复杂的线段树,就是数据处理需要好好想一下. 将输入的所有数据从后往前输入,对于最后一个值,如果它想插入第i个位置,那么他就必须在前面留下i-1个位置.对于倒数第二个人,如果他想插入j位置,那么 ...
- CSUOJ 1299 - Number Transformation II 打表预处理水DP
http://122.207.68.93/OnlineJudge/problem.php?id=1299 第二个样例解释.. 3 6 3->4->6..两步.. 由此可以BFS也可以DP. ...
- Android 性能优化——内存篇
一.android官方一些内存方面的内存tips 1.避免创建不必要的对象. 如尽量避免字符串的加号拼接,可以使用StringBuilder来拼接. 如果需要TextView设置多个字符串片段,可以使 ...
- java中的ArrayList 、List、LinkedList、Collection关系详解
一.基础介绍(Set.List.Map) Set(集):集合中的元素不按特定方式排序,并且没有重复对象.他的有些实现类能对集合中的对象按特定方式排序. List(列表):集合中的元素按索引位置排序,可 ...
- canvas 弹幕效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 解决 jQuery.UI.Resizable aspectRatio在init后无法重新设置
一.背景 在jQuery1.9.x版本之前,存在aspectRatio在Resizable方法init之后,无法再次修改aspectRatio的boolean值. 二.解决方案 // 用于fix j ...