OOP in JS - Inheritance

Summary

  • You cause a class to inherit using ChildClassName.prototype = new ParentClass();.
  • You need to remember to reset the constructor property for the class using ChildClassName.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

时间: 2024-10-11 18:48:55

OOP in JS - Inheritance的相关文章

js中的面向对象

面向对象:不了解原理的情况下,会使用功能.如:电视机,不清楚原理,却知道如何操作.面向对象是一种通用的思想,并非只有在编程使用.面向对象编程简称OOP. js是一个基于原型的面向对象的编程语言,即每个对象有一个原型对象,对象从原型中继承属性和方法. js的原型的关系是递归的.即,对象的原型也是一个对象,而原型的本身可能还有一个原型. js中的对象使用一个new 运算符和一个构造函数来创建 js中可以给对象动态的添加属性和方法 js中创建对象的方法有多种,包括:原型方式,动态原型方式,工厂方式等等

html5 游戏开发

近来想做html5游戏开发些小东西玩一下,因为手边就是笔记本,想怎么玩就怎么玩了,今年可以说是非常重要特殊的一年,感觉有些倒霉,不过,心态最重要,该怎么做的时候就去怎么做吧,日子的24小时是不会变的,不管能活多久. 游戏开发感觉不错,所以就刚看了一个碰撞检测canvas的来做,一段段代码弄下来试,成功运行了,原理也是一些很简单的,不过是 不停地检测 FPS,不停刷新每一帧来达到最终的探测目的: 每一帧里面都去检测范围,是否会碰撞: 有三种检测方法: 范围检测: 光线投影速度向量检测:分离轴定理:

Prototype based langue LUA

Prototype-based programming https://en.wikipedia.org/wiki/Prototype-based_programming Prototype-based programming is a style of object-oriented programming in which behaviour reuse (known as inheritance) is performed via a process of reusing existing

UnityScript快速入门

UnityScript原先被官方称作用于U3D的JavaScript,但是较新一些的文档都已经叫做UnityScript了,虽然JS的语法在UnityScript中几乎都可以使用,但是UnityScript是一种具有静态类型检查且更加OOP的JS,所以完全可以把UnityScript当做一门新语言来学,当然,如果已经学会了JS,那么想学会UnityScript是相当快的.相比跑在浏览器上的JS,UnityScript无论在身为语言的设计上还是在运行能力上都要远远优于前者. 这篇快速入门教程将会着

Domain Driven Design and Development In Practice--转载

原文地址:http://www.infoq.com/articles/ddd-in-practice Background Domain Driven Design (DDD) is about mapping business domain concepts into software artifacts. Most of the writings and articles on this topic have been based on Eric Evans' book "Domain Dr

js oop中的三种继承方法

JS OOP 中的三种继承方法: 很多读者关于js opp的继承比较模糊,本文总结了oop中的三种继承方法,以助于读者进行区分. <继承使用一个子类继承另一个父类,子类可以自动拥有父类的属性和方法.(继承的两方,发生在两个类之间)> 一.通过object实现继承 1:定义父类 function Parent(){} 2:定义子类 funtion Son(){} 3:通过原型给Object对象添加一个扩展方法. Object.prototype.customExtend = function(p

让JS写的更接近OOP

下面这段代码就是利用JS原型对象,来实现的类的继承DEMO $ 为jquery对象 ////公共方法 // $.oop.newClass=function newClass(obj) { // function create() { // if (obj != null) // return obj(); // } // var c = new create(); // return c; // } //人 var person = $.oop.newClass(function () { //

js 设计模式 oop 面向对象编程

最初我们写js代码的时候是这么写 function checkName(){ //验证姓名 } function checkEmail(){ //验证邮箱 } function checkPassword(){ //验证密码 } 这种方式会造成全局变量的严重污染,再过渡到 var checkObject = { checkName : function(){}; checkEmail: function(){}; checkPassword: funcion(){}; } //也可如此写 var

拖拽系列二、利用JS面向对象OOP思想实现拖拽封装

接着上一篇拖拽系列一.JavaScript实现简单的拖拽效果这一篇博客将接着对上一节实现代码利用JS面向对象(OOP)思维对上一节代码进行封装; 使其模块化.避免全局函数污染.方便后期维护和调用:写到这里突然想起一句话“没有任何一个题目是彻底完成的.总还会有很多事情可做......” 我想这句话程序开发大概也适用吧,前端开发人员总是可以结合自己之前学到“拖拽”相关知识,不断扩展.完善.无穷无尽.......     利用匿名函数自执行实现封装 ;(function(){ //do somethi