javascript 面向对象全新理练之原型继承

首先创建一个父类的实例化对象,然后将该对象赋给子类的 prototype 属性。
这样,父类中的所有公有实例成员都会被子类继承。并且用 instanceof 运算符判断时,子类的实例化对象既属于子类,也属于父类。

然后将子类本身赋值给它的 prototype 的 constructor 属性。(注意:这里赋值的时候是没有 () 的!)
这一步是为了保证在查看子类的实例化对象的 constructor 属性时,看到的是子类的定义,而不是其父类的定义。

接下来,通过对 o.method1() 调用的结果我们会看到,子类继承来的公有实例方法中,如果调用了私有实例字段或者私有实例方法,则所调用的这些私有实例成员是属于父类的。

同样,通过对 o.method2() 调用的结果我们看到,子类中定义的实例方法,如果调用了私有实例字段或者私有实例方法,则所调用的这些私有实例成员是属于子类的。

通过对 o.method() 调用的结果我们看到,定义在父类原型上的方法,会被子类继承。
通过对 o.method3() 调用的结果我们看到,子类中定义的实例方法是不能访问父类中定义的私有实例成员的。
最后,通过对 subClass.staticMethod() 调用的结果我们看到,静态成员是不会被继承的。

2.4 调用继承法
调用继承的本质是,在子类的构造器中,让父类的构造器方法在子类的执行上下文上执行,父类构造器方法上所有通过 this 方式操作的内容实际上都都是操作的子类的实例化对象上的内容。因此,这种做法仅仅为了减少重复代码的编写。


代码如下:

function parentClass() {
// private field
var x = "I'm a parentClass field!";
// private method
function method1() {
alert(x);
alert("I'm a parentClass method!");
}
// public field
this.x = "I'm a parentClass object field!";
// public method
this.method1 = function() {
alert(x);
alert(this.x);
method1();
}
}
parentClass.prototype.method = function () {
alert("I'm a parentClass prototype method!");
}

parentClass.staticMethod = function () {
alert("I'm a parentClass static method!");
}

function subClass() {
// inherit
parentClass.call(this);

// private field
var x = "I'm a subClass field!";
// private method
function method2() {
alert(x);
alert("I'm a subClass method!");
}
// public field
this.x = "I'm a subClass object field!";
// public method
this.method2 = function() {
alert(x);
alert(this.x);
method2();
}
this.method3 = function() {
method1();
}
}

// test
var o = new subClass();

alert(o instanceof parentClass); // false
alert(o instanceof subClass); // true

alert(o.constructor); // function subClass() {...}

o.method1(); // I'm a parentClass field!
// I'm a subClass object field!
// I'm a parentClass field!
// I'm a parentClass method!
o.method2(); // I'm a subClass field!
// I'm a subClass object field!
// I'm a subClass field!
// I'm a subClass method!

o.method(); // Error!!!
o.method3(); // Error!!!
subClass.staticMethod(); // Error!!!

上面这个例子很好的反映出了如何利用调用继承法来实现继承。
利用调用继承的关键只有一步操作:
就是在子类定义时,通过父类的 call 方法,将子类的 this 指针传入。使父类方法在子类上下文中执行。
这样,父类中的所有在父类内部通过 this 方式定义的公有实例成员都会被子类继承。
用 instanceof 运算符判断时,子类的实例化对象只属于子类,不属于父类。
查看子类的实例化对象的 constructor 属性时,看到的是子类的定义,不是其父类的定义。
接下来,通过对 o.method1() 和 o.method2() 调用的结果跟原型继承法的调用结果是相同的,所说明的问题也是一样的,这里不再重复。
通过对 o.method() 调用的结果我们看到,定义在父类原型上的方法,不会被子类继承。
通过对 o.method3() 调用的结果我们看到,子类中定义的实例方法同样不能访问父类中定义的私有实例成员的。
最后,通过对 subClass.staticMethod() 调用的结果我们看到,静态成员同样不会被继承的。
最后,还有一点,在这个例子中没有体现出来,就是通过调用继承法,可以实现多继承。也就是说,一个子类可以从多个父类中继承通过 this 方式定义在父类内部的所有公有实例成员。
作为一种弱类型语言,javascript 提供了丰富的多态性,javascript 的多态性是其它强类型面向对象语言所不能比的。
多态
重载和覆盖
先来说明一下重载和覆盖的区别。重载的英文是 overload,覆盖的英文是 override。发现网上大多数人把 override 当成了重载,这个是不对的。重载和覆盖是有区别的。
重载的意思是,同一个名字的函数(注意这里包括函数)或方法可以有多个实现,他们依靠参数的类型和(或)参数的个数来区分识别。
而覆盖的意思是,子类中可以定义与父类中同名,并且参数类型和个数也相同的方法,这些方法的定义后,在子类的实例化对象中,父类中继承的这些同名方法将被隐藏。
重载
javascript 中函数的参数是没有类型的,并且参数个数也是任意的,例如,尽管你可以定义一个:


代码如下:

function add(a, b) {
return a + b;
}

这样的函数,但是你仍然可以再调用它是带入任意多个参数,当然,参数类型也是任意的。至于是否出错,那是这个函数中所执行的内容来决定的,javascript 并不根据你指定的参数个数和参数类型来判断你调用的是哪个函数。
因此,要定义重载方法,就不能像强类型语言中那样做了。但是你仍然可以实现重载。就是通过函数的 arguments 属性。例如:


代码如下:

function add() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}

这样你就实现了任意多个参数加法函数的重载了。
当然,你还可以在函数中通过 instanceof 或者 constructor 来判断每个参数的类型,来决定后面执行什么操作,实现更为复杂的函数或方法重载。总之,javascript 的重载,是在函数中由用户自己通过操作 arguments 这个属性来实现的。
覆盖
实现覆盖也很容易,例如:


代码如下:

function parentClass() {
this.method = function() {
alert("parentClass method");
}
}
function subClass() {
this.method = function() {
alert("subClass method");
}
}
subClass.prototype = new parentClass();
subClass.prototype.constructor = subClass;

var o = new subClass();
o.method();

这样,子类中定义的 method 就覆盖了从父类中继承来的 method 方法了。
你可能会说,这样子覆盖是不错,但 java 中,覆盖的方法里面可以调用被覆盖的方法(父类的方法),在这里怎么实现呢?也很容易,而且比 java 中还要灵活,java 中限制,你只能在覆盖被覆盖方法的方法中才能使用 super 来调用次被覆盖的方法。我们不但可以实现这点,而且还可以让子类中所有的方法中都可以调用父类中被覆盖的方法。看下面的例子:


代码如下:

function parentClass() {
this.method = function() {
alert("parentClass method");
}
}
function subClass() {
var method = this.method;
this.method = function() {
method.call(this);
alert("subClass method");
}
}
subClass.prototype = new parentClass();
subClass.prototype.constructor = subClass;

var o = new subClass();
o.method();

你会发现,原来这么简单,只要在定义覆盖方法前,定义一个私有变量,然后把父类中定义的将要被覆盖的方法赋给它,然后我们就可以在后面继续调用它了,而且这个是这个方法是私有的,对于子类的对象是不可见的。这样跟其它高级语言实现的覆盖就一致了。

最后需要注意,我们在覆盖方法中调用这个方法时,需要用 call 方法来改变执行上下文为 this(虽然在这个例子中没有必要),如果直接调用这个方法,执行上下文就会变成全局对象了。

(0)

相关推荐

  • javascript prototype的深度探索不是原型继承那么简单第1/3页

    1 什么是prototype JavaScript中对象的prototype属性,可以返回对象类型原型的引用.这是一个相当拗口的解释,要理解它,先要正确理解对象类型(Type)以及原型(prototype)的概念.         前面我们说,对象的类(Class)和对象实例(Instance)之间是一种"创建"关系,因此我们把"类"看作是对象特征的模型化,而对象看作是类特征的具体化,或者说,类(Class)是对象的一个类型(Type).例如,在前面的例子中,p1和

  • 理解Javascript_05_原型继承原理

    prototype与[[prototype]] 在有面象对象基础的前提下,来看一段代码: 复制代码 代码如下: //Animal构造函数 function Animal(name){ this.name = name; } //Animal原型对象 Animal.prototype = { id:"Animal", sleep:function(){ alert("sleep"); } } var dog = new Animal("旺才");

  • JavaScript原型继承_动力节点Java学院整理

    在传统的基于Class的语言如Java.C++中,继承的本质是扩展一个已有的Class,并生成新的Subclass. 由于这类语言严格区分类和实例,继承实际上是类型的扩展.但是,JavaScript由于采用原型继承,我们无法直接扩展一个Class,因为根本不存在Class这种类型. 但是办法还是有的.我们先回顾Student构造函数: function Student(props) { this.name = props.name || 'Unnamed'; } Student.prototyp

  • javascript 原型继承介绍

    暑假还搞了ext4的web desktop,更多的也是javascript的东西.对于javascript,以前就只会document.getElementById()和alert(),现在才开始慢慢深入了解.如果本文有什么不对的地方,请指出. 关于javasript,它是基于对象的,因此,它没有类的概念,所以,如果要实现继承,也就只能是利用javascript的原型机制prototype去实现了.(其实这里是错的,感谢 @记忆的森林 的提示,还可以用apply和call去实现) 因为javas

  • JavaScript原型继承之基础机制分析

    这一语言功能的本质依赖于 JavaScript 特有的原型链(prototype chain)模式. 所以严格意义上说,JavaScript 是基于原型的面向对象语言.也就是说,每个实例对象都具有一个原型.对象从该原型中继承属性和方法. 1.构造函数 利用构造函数,可以简单地创建对象.构造函数内的 this 关键字指向实例对象本身: 复制代码 代码如下: function People(name){ this.name = name; } 使用 new 运算符和构造函数创建实例对象: 复制代码

  • 浅析Javascript原型继承 推荐第1/2页

    JS没有提供所谓的类继承,据说在2.0中要加入这种继承方式,但是要所有浏览器都实现2.0的特性那肯定又得N多年.昨天看了crockford 的一个视频,里面讲解了一下JS的继承方式,按照PPT里面说的,一共分了三类:Prototypal,pseudoclassical,Parasitic Inheritance. 下面主要介绍一下原型继承:When a function object is created, it is given a prototype member which is an o

  • js原型继承的两种方法对比介绍

    在实际的项目中,我们通常都是用构造函数来创建一个对象,再将一些常用的方法添加到其原型对象上.最后要么直接实例化该对象,要么将它作为父类,再申明一个对象,继承该父类. 而在继承的时候有两种常用方式,今天我们就来稍作探讨 复制代码 代码如下: //父类 function Person(name){    this.name = name;}; // 子类 function Student(sex){  Person.apply(this,arguments); //继承父类的构造函数  this.s

  • 浅谈javascript的原型继承

    请看源码: 复制代码 代码如下: function clone(o) { var F = function(){}; F.prototype = o; return new F(); } 首先看ext(4.1的1896行开始)的原型式继承. 复制代码 代码如下: var TemplateClass = function(){}; var ExtObject = Ext.Object = { chain: function (object) { TemplateClass.prototype =

  • JavaScript的原型继承详解

    JavaScript是一门面向对象的语言.在JavaScript中有一句很经典的话,万物皆对象.既然是面向对象的,那就有面向对象的三大特征:封装.继承.多态.这里讲的是JavaScript的继承,其他两个容后再讲. JavaScript的继承和C++的继承不大一样,C++的继承是基于类的,而JavaScript的继承是基于原型的. 现在问题来了. 原型是什么?原型我们可以参照C++里的类,同样的保存了对象的属性和方法.例如我们写一个简单的对象 复制代码 代码如下: function Animal

  • 再谈javascript原型继承

    真正意义上来说Javascript并不是一门面向对象的语言,没有提供传统的继承方式,但是它提供了一种原型继承的方式,利用自身提供的原型属性来实现继承. 原型与原型链 说原型继承之前还是要先说说原型和原型链,毕竟这是实现原型继承的基础. 在Javascript中,每个函数都有一个原型属性prototype指向自身的原型,而由这个函数创建的对象也有一个__proto__属性指向这个原型,而函数的原型是一个对象,所以这个对象也会有一个__proto__指向自己的原型,这样逐层深入直到Object对象的

随机推荐