JS中,子类调用超类函数

概述

  JS虽然并不直接具备面向对象的特性,但仍可以通过prototype来模拟面向对象的继承和多态等特性。和大多数面向对象语言(例如C++,Java等)相比,JS来实现面向对象仍稍显繁琐和抽象,需要对JS的prototype模型有深入的理解。

  在开发过程中,有时候会遇到这样一个问题:如果在子类中“覆盖”了超类的某个方法,但仍需要在子类方法中调用一次超类方法,这时候应该怎么做?如果是Java,一个简单的super关键字即可解决问题,但如果是JS呢?

  解决问题的最基本方法可以是:在子类中,使用超类类型,通过apply关键字,以当前类实例引用执行一次超类方法。如下:

定义类A

// 定义类A
function A(a) {
	this.a = a;
}
// 为类A定义show方法
A.prototype.show = function() {
		alert("A: " + this.a);
	};

定义类B并从A继承

// 定义类B
function B(a, b) {
	// 调用A的构造函数
	A.apply(this, arguments);
	this.b = b;
}
// 链接A的原型
B.prototype = new A();

实例化类B对象并调用show方法

var b = new B(100, 200);
b.show();

  此时,会执行定义在A中的show方法(显示A.a的值),表示类B已经从类A中继承了show方法

类B覆盖show方法

// 覆盖show方法
B.prototype.show = function() {
		A.prototype.show.apply(this, arguments);
		alert("B: " + this.b);
	};

// 执行覆盖后的方法
b.show();

  在B的prototype中重新定义show方法,即可以认为B类覆盖了A类的show方法。注意 A.prototype.show.apply(this, arguments) 这一句,实际上是利用了JS的原型特性,在B类对象中(以B类对象)执行了一次A类的show方法。

  JS特殊的动态语言特性使得“覆盖”这个语义可以随时发生,甚至可以通过操作prototype来取消”覆盖“语义,这也正是JS的灵活和强大之处。

更进一步

  通过上面的例子,可以发现,JS的”继承“,”覆盖“和”调用超类方法“虽然不难理解,但写起来仍较为繁琐,下面的代码可以简化这个流程。(以下代码灵感部分来源于ExtJS库,部分参考自prototype库)

namespace

// 定义根命名空间
ALV = {};

// 定义注册命名空间的方法
ALV.namespace = function(ns) {
		var root = window;
		var parts = ns.split(".");
		for (var i = 0; i < parts.length; i++) {
			var p = parts[i];
			if (!root[p]) {
				root[p] = {};
			}
			root = root[p];
		}
	};

合并对象的apply方法

// 合并对象成员
ALV.apply = function(obja, objb, def) {
		if (def) {
			ALV.apply(obja, def);
		}
		if (obja && objb && typeof objb === 'object') {
			for (var o in objb) {
				obja[o] = objb[o];
			}
		}
	};

定义类的方法

// 原型定义
ALV.define = function(clazz, config) {
		var parts = clazz.split(".");
		var root = window;
		for (var i = 0; i < parts.length - 1; i++) {
			root = root[parts[i]];
		}
		var cn = parts[parts.length - 1];
		if (!root[cn]) {
			root[cn] = function() {};
		}
		clazz = root[cn];
		// 将proto对象的成员赋值给类的原型
		ALV.apply(clazz.prototype, config);
		return clazz;
	};

定义子类并继承超类的方法

// 定义继承的方法
ALV.extend = function(base, child, proto) {
		// 将超类原型赋值给类的原型
		var c = ALV.define(child);
		if (base && typeof base === "function") {
			c.prototype = new base();
		}
		if (proto && typeof proto == "object") {
			ALV.apply(c.prototype, proto);
		}

		// 调用超类方法
		c.prototype.callParent = function(args) {
				var m;
				for (var o in this) {
					if (this[o] === this.callParent.caller) {
						m = o;
					}
				}
				var method = base.prototype[m];
				if (method && typeof method === "function") {
					method.apply(this, args);
				}
			};
	};

  上述代码中,子类的 prototype 链接到了超类对象上,完成了 prototype 的继承,而 callParent 方法中,通过对当前类调用方法的查找,找到方法名(m变量),再在超类的 prototype 中找到同名方法,利用超类方法的 apply 操作,在子类对象上完成对超类方法的调用。

测试代码

// 定义命名空间
ALV.namespace("Alvin.test");
// 定义超类
ALV.define("Alvin.test.A", {
		a: 100,
		show: function() {
				alert("A: " + this.a);
			}
	});
// 定义子类
ALV.extend(Alvin.test.A, "Alvin.test.B", {
		a: 100,
		b: 200,
		show: function() {
				this.callParent(arguments);
				alert("B: " + this.b);
			}
	});
// 实例化B类对象
var b = new Alvin.test.B();
b.show();

  从测试代码中可以看到,Alvin.test.B 类继承了 Alvin.test.A 类,且覆盖了其中的 show 方法,在 B 类的 show 方法中,this.callParent(arguments) 调用完成了对 A 类show方法的调用。这样 B 类就可以自然的访问超类中的指定方法而无需关心超类究竟使用什么名称。

JS中,子类调用超类函数

时间: 2024-10-07 04:58:13

JS中,子类调用超类函数的相关文章

C++面试题1:构造函数和虚构函数中能否调用虚函数?

C++面试题1:构造函数和虚构函数中能否调用虚函数? 构造函数跟虚构函数里面都可以调用虚函数,编译器不会报错. C++ primer中说到最好别用 由于类的构造次序是由基类到派生类,所以在构造函数中调用虚函数,虚函数是不会呈现出多态的 类的析构是从派生类到基类,当调用继承层次中某一层次的类的析构函数时意味着其派生类部分已经析构掉,所以也不会呈现多态 因此如果在基类中声明的纯虚函数并且在基类的析构函数中调用之,编译器会发生错误. class Base { public: Base() { Fuct

JS中的自执行函数

本来规划的是2013年,狠狠的将JS学习下,谁知计划赶不上变化,计划泡汤了.13年的我对JS来说可以说是属于跟风,对它的理解和认识也仅仅是皮毛而已,也是因为要完成<ArcGIS API for JavaScipt开发教程>而临阵磨枪. 在接触JS一段时间后,觉得还是比较灵活的,灵活的前提是要更深入的了解,就像两个陌生的人,相处的时间长了,了解的时间长了,难免会产生感情一样.对于JS也开始产生了感情,这种感情体现在工作中,体现在周围的环境中. 目前很多开发者纷纷加入JS的阵营,看来这已经不是跟风

Java:子类调用超类方法的一种特殊情况

在Java的子类中,可以通过super来明确调用超类(也即父类)的方法.但当所调用的超类的方法(1)中又调用了其它的方法(2)时,由于Java默认动态绑定,所以方法(2)调用的是子类中的方法.如下,示例(1)是一般的子类调用超类方法(即所调用的超类中的方法不再调用其它的需要动态绑定的方法),示例(2)是特殊的子类调用超类方法. 示例(1): package MyTest; import java.util.*; public class MyTest { public static void m

JS中构造函数与普通函数的区别及JS构造函数、原型和实例的关系

JS中构造函数与普通函数的区别: https://www.cnblogs.com/cindy79/p/7245566.html JS构造函数.原型和实例的关系: https://blog.csdn.net/u012443286/article/details/78823955 JavaScript构造函数及原型对象: https://blog.csdn.net/a153375250/article/details/51083245 原文地址:https://www.cnblogs.com/jim

详解 JS 中 new 调用函数原理

JavaScript 中经常使用构造函数创建对象(通过 new 操作符调用一个函数),那在使用 new 调用一个函数的时候到底发生了什么?先看几个例子,再解释背后发生了什么. 1)看三个例子 1.1 无 return 语句 构造函数最后没有 return 语句,这也是使用构造函数时默认情况,最后会返回一个新对象,如下: function Foo(age) { this.age = age; } var o = new Foo(111); console.log(o); 这是常见的使用构造函数创建

#python#子类调用父类函数的方法

Python中的子类中的__init__()函数会覆盖父类的函数,一些情况往往需要在子类里调用父类函数. 如下例程里,???处是需要调用父类函数的地方,接下来结合例程具体介绍. 1 1 # -*- coding:utf-8 -*- 2 2 class Student: 3 3 def __init__(self,name): 4 4 self.name=name 5 5 def ps(self): 6 6 print('I am %s'%self.name) 7 7 8 8 class Scor

js中(function(){…})()立即执行函数写法理解

javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解. ( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法,最初我以为是一个括号包裹匿名函数,再在后面加个括号调用函数,最后达到函数定义后立即执行的目的,后来发现加括号的原因并非如此.要理解立即执行函数,需要先理解一些函数的基本概念.

[转]Java中子类调用父类构造方法的问题分析

在Java中,子类的构造过程中,必须调用其父类的构造函数,是因为有继承关系存在时,子类要把父类的内容继承下来,通过什么手段做到的? 答案如下:    当你new一个子类对象的时候,必须首先要new一个父类的对像出来,这个父类对象位于子类对象的内部,所以说,子类对象比父类对象大,子类对象里面包含了一个父类的对象,这是内存中真实的情况.构造方法是new一个对象的时候,必须要调的方法,这是规定,要new父类对象出来,那么肯定要调用其构造方法,所以: 第一个规则:子类的构造过程中,必须调用其父类的构造方

linux 下的动态库制作 以及在python 中如何调用 c 函数库

动态库: 动态库又称动态链接库英文为DLL,是Dynamic Link Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件.动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数.函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译.链接并与使用它们的进程分开存储的函数.DLL 还有助于共享数据和资源.多个应用程序可同时访问内存中单个DLL 副本的内容.DLL 是一个包含可由多个程序同时使用的代码和数据的库.Wind