位置: 编程技术 - 正文

JavaScript 继承详解(三)(js继承的方法)

编辑:rootadmin

注:本章中的jClass的实现参考了Simple JavaScript Inheritance的做法。

首先让我们来回顾一下第一章中介绍的例子:

function Person(name) {this.name = name;}Person.prototype = {getName: function() {return this.name;}}function Employee(name, employeeID) {this.name = name;this.employeeID = employeeID;}Employee.prototype = new Person();Employee.prototype.getEmployeeID = function() {return this.employeeID;};var zhang = new Employee("ZhangSan", "");console.log(zhang.getName()); // "ZhangSan"

修正constructor的指向错误

从上一篇文章中关于constructor的描述,我们知道Employee实例的constructor会有一个指向错误,如下所示:

var zhang = new Employee("ZhangSan", "");console.log(zhang.constructor === Employee); // falseconsole.log(zhang.constructor === Object); // true 我们需要简单的修正: function Employee(name, employeeID) {this.name = name;this.employeeID = employeeID;}Employee.prototype = new Person();Employee.prototype.constructor = Employee;Employee.prototype.getEmployeeID = function() {return this.employeeID;};var zhang = new Employee("ZhangSan", "");console.log(zhang.constructor === Employee); // trueconsole.log(zhang.constructor === Object); // false

创建Employee类时实例化Person是不合适的

但另一方面,我们又必须依赖于这种机制来实现继承。 解决办法是不在构造函数中初始化数据,而是提供一个原型方法(比如init)来初始化数据。

// 空的构造函数function Person() {}Person.prototype = {init: function(name) {this.name = name;},getName: function() {return this.name;}}// 空的构造函数function Employee() {}// 创建类的阶段不会初始化父类的数据,因为Person是一个空的构造函数Employee.prototype = new Person();Employee.prototype.constructor = Employee;Employee.prototype.init = function(name, employeeID) {this.name = name;this.employeeID = employeeID;};Employee.prototype.getEmployeeID = function() {return this.employeeID;};这种方式下,必须在实例化一个对象后手工调用init函数,如下: var zhang = new Employee();zhang.init("ZhangSan", "");console.log(zhang.getName()); // "ZhangSan"

如何自动调用init函数?

必须达到两个效果,构造类时不要调用init函数和实例化对象时自动调用init函数。看来我们需要在调用空的构造函数时有一个状态标示。

// 创建一个全局的状态标示 - 当前是否处于类的构造阶段var initializing = false;function Person() {if (!initializing) {this.init.apply(this, arguments);}}Person.prototype = {init: function(name) {this.name = name;},getName: function() {return this.name;}}function Employee() {if (!initializing) {this.init.apply(this, arguments);}}// 标示当前进入类的创建阶段,不会调用init函数initializing = true;Employee.prototype = new Person();Employee.prototype.constructor = Employee;initializing = false;Employee.prototype.init = function(name, employeeID) {this.name = name;this.employeeID = employeeID;};Employee.prototype.getEmployeeID = function() {return this.employeeID;};// 初始化类实例时,自动调用类的原型函数init,并向init中传递参数var zhang = new Employee("ZhangSan", "");console.log(zhang.getName()); // "ZhangSan"但是这样就必须引入全局变量,这是一个不好的信号。

如何避免引入全局变量initializing?

我们需要引入一个全局的函数来简化类的创建过程,同时封装内部细节避免引入全局变量。

// 当前是否处于创建类的阶段var initializing = false;function jClass(baseClass, prop) {// 只接受一个参数的情况 - jClass(prop)if (typeof (baseClass) === "object") {prop = baseClass;baseClass = null;}// 本次调用所创建的类(构造函数)function F() {// 如果当前处于实例化类的阶段,则调用init原型函数if (!initializing) {this.init.apply(this, arguments);}}// 如果此类需要从其它类扩展if (baseClass) {initializing = true;F.prototype = new baseClass();F.prototype.constructor = F;initializing = false;}// 覆盖父类的同名函数for (var name in prop) {if (prop.hasOwnProperty(name)) {F.prototype[name] = prop[name];}}return F;};使用jClass函数来创建类和继承类的方法: var Person = jClass({init: function(name) {this.name = name;},getName: function() {return this.name;}});var Employee = jClass(Person, {init: function(name, employeeID) {this.name = name;this.employeeID = employeeID;},getEmployeeID: function() {return this.employeeID;}});var zhang = new Employee("ZhangSan", "");console.log(zhang.getName()); // "ZhangSan"OK,现在创建类和实例化类的方式看起来优雅多了。 但是这里面还存在明显的瑕疵,Employee的初始化函数init无法调用父类的同名方法。

如何调用父类的同名方法?

我们可以通过为实例化对象提供一个base的属性,来指向父类(构造函数)的原型,如下:

// 当前是否处于创建类的阶段var initializing = false;function jClass(baseClass, prop) {// 只接受一个参数的情况 - jClass(prop)if (typeof (baseClass) === "object") {prop = baseClass;baseClass = null;}// 本次调用所创建的类(构造函数)function F() {// 如果当前处于实例化类的阶段,则调用init原型函数if (!initializing) {// 如果父类存在,则实例对象的base指向父类的原型// 这就提供了在实例对象中调用父类方法的途径if (baseClass) {this.base = baseClass.prototype;}this.init.apply(this, arguments);}}// 如果此类需要从其它类扩展if (baseClass) {initializing = true;F.prototype = new baseClass();F.prototype.constructor = F;initializing = false;}// 覆盖父类的同名函数for (var name in prop) {if (prop.hasOwnProperty(name)) {F.prototype[name] = prop[name];}}return F;};调用方式: var Person = jClass({init: function(name) {this.name = name;},getName: function() {return this.name;}});var Employee = jClass(Person, {init: function(name, employeeID) {// 调用父类的原型函数init,注意使用apply函数修改init的this指向this.base.init.apply(this, [name]);this.employeeID = employeeID;},getEmployeeID: function() {return this.employeeID;},getName: function() {// 调用父类的原型函数getNamereturn "Employee name: " + this.base.getName.apply(this);}});var zhang = new Employee("ZhangSan", "");console.log(zhang.getName()); // "Employee name: ZhangSan"

目前为止,我们已经修正了在第一章手工实现继承的种种弊端。 通过我们自定义的jClass函数来创建类和子类,通过原型方法init初始化数据, 通过实例属性base来调用父类的原型函数。

唯一的缺憾是调用父类的代码太长,并且不好理解, 如果能够按照如下的方式调用岂不是更妙:

var Employee = jClass(Person, {init: function(name, employeeID) {// 如果能够这样调用,就再好不过了this.base(name);this.employeeID = employeeID;}});

优化jClass函数

// 当前是否处于创建类的阶段var initializing = false;function jClass(baseClass, prop) {// 只接受一个参数的情况 - jClass(prop)if (typeof (baseClass) === "object") {prop = baseClass;baseClass = null;}// 本次调用所创建的类(构造函数)function F() {// 如果当前处于实例化类的阶段,则调用init原型函数if (!initializing) {// 如果父类存在,则实例对象的baseprototype指向父类的原型// 这就提供了在实例对象中调用父类方法的途径if (baseClass) {this.baseprototype = baseClass.prototype;}this.init.apply(this, arguments);}}// 如果此类需要从其它类扩展if (baseClass) {initializing = true;F.prototype = new baseClass();F.prototype.constructor = F;initializing = false;}// 覆盖父类的同名函数for (var name in prop) {if (prop.hasOwnProperty(name)) {// 如果此类继承自父类baseClass并且父类原型中存在同名函数nameif (baseClass &&typeof (prop[name]) === "function" &&typeof (F.prototype[name]) === "function") {// 重定义函数name - // 首先在函数上下文设置this.base指向父类原型中的同名函数// 然后调用函数prop[name],返回函数结果// 注意:这里的自执行函数创建了一个上下文,这个上下文返回另一个函数,// 此函数中可以应用此上下文中的变量,这就是闭包(Closure)。// 这是JavaScript框架开发中常用的技巧。F.prototype[name] = (function(name, fn) {return function() {this.base = baseClass.prototype[name];return fn.apply(this, arguments);};})(name, prop[name]);} else {F.prototype[name] = prop[name];}}}return F;};此时,创建类与子类以及调用方式都显得非常优雅,请看: var Person = jClass({init: function(name) {this.name = name;},getName: function() {return this.name;}});var Employee = jClass(Person, {init: function(name, employeeID) {this.base(name);this.employeeID = employeeID;},getEmployeeID: function() {return this.employeeID;},getName: function() {return "Employee name: " + this.base();}});var zhang = new Employee("ZhangSan", "");console.log(zhang.getName()); // "Employee name: ZhangSan"

至此,我们已经创建了一个完善的函数jClass, 帮助我们在JavaScript中以比较优雅的方式实现类和继承。

在以后的章节中,我们会陆续分析网上一些比较流行的JavaScript类和继承的实现。 不过万变不离其宗,那些实现也无非把我们这章中提到的概念颠来簸去的“炒作”, 为的就是一种更优雅的调用方式。

推荐整理分享JavaScript 继承详解(三)(js继承的方法),希望有所帮助,仅作参考,欢迎阅读内容。

JavaScript 继承详解(三)(js继承的方法)

文章相关热门搜索词:js中的继承,js继承的三种方法,js里的继承,js继承的方法,js继承的方法,js中的继承,javascript继承原理,js继承的三种方式,内容如对您有帮助,希望把文章链接给更多的朋友!

JavaScript 继承详解(四) ClassicalInheritanceinJavaScript。Crockford是JavaScript开发社区最知名的权威,是JSON、JSLint、JSMin和ADSafe之父,是《JavaScript:TheGoodParts》的作者。现在是Yahoo的资深J

一个cssQuery对象 javascript脚本实现代码 /***@authorSupersha*@QQ:*/varcssQuery={//parent:用于存储当前节点的父节点的引用parent:document,select:function(selectorStr){varselectors=selectorStr.split("");//分隔字符串f

javascript Base类 包含基本的方法 scripttype="text/javascript"functionBase(){}//根抽象类Base.toBase=function(){//将一个对象转化成Base类的实例的方法returnnewBase();}Base.inherit=function(parent){//用于继承Base

标签: js继承的方法

本文链接地址:https://www.jiuchutong.com/biancheng/386480.html 转载请保留说明!

上一篇:JavaScript 继承详解(二)(javascript中继承)

下一篇:JavaScript 继承详解(四)(js中的继承)

  • 企业税号还未开户
  • 展会补贴收入会计处理
  • 发票的抵扣期限怎么填
  • 专项应付款在资产负债表中如何列示
  • 税务证丢失可以注销吗
  • 酒店应不应该为员工提供澡堂
  • 母子公司间提供保洁保安合法吗
  • 建筑材料专票抵扣
  • 销售蔬菜的个体工商怎么开具发票
  • 未认证的进项税账务处理
  • 企业生产设备发生的日常维修费用
  • 收入费用类科目结转至本年利润
  • 预收账款缴纳企税怎么算
  • 个体工商户个税减免优惠政策
  • 企业计提的安全生产费用年末应如何处理
  • 小规模纳税人代理销售税率是多少
  • 微软输入法突然变成繁体字怎么改回来
  • 如何让解决中世纪基督教世界黑暗
  • 个人社保和公积金扣款公司七个人三怎么样
  • 修改hosts文件的软件
  • qcwlicon.exe - qcwlicon是什么进程 有何作用
  • php获取多选框的值
  • newdot.exe - newdot是什么进程 有什么用
  • Win10 Build 19044.1379/19043.1379更新补丁KB5007253预览版推送
  • 如何直接访问ip地址
  • 年度确认收入怎么算
  • 递延所得税的计税基础
  • 以旧换新方式销售货物的,不得扣除旧货价值
  • 二手房印花税怎么算2020
  • 向银行贷款资金成本率计算公式是什么
  • react-use
  • 零星分布的意思
  • 金门大桥和马林大桥区别
  • 待处理财产损益会计分录
  • 其他收益如何核算成本
  • 时序21-21-21-47
  • python进行数学建模
  • 进口固定资产支付的关税
  • js 字符串 中 字符位置
  • 银行汇票和银行承兑汇票的区别
  • 工资费用核算
  • python中模块的概念
  • 帝国cms采集教程
  • 低于净资产增资 纳税
  • 融资租赁首付租金定义
  • 应收账款是否存在的审计程序
  • 什么企业符合高新技术企业
  • 折旧率多少正常
  • mysql数据库简单介绍
  • 提前送了礼物还要发红包吗
  • 外地预缴税款如何查询
  • 高新企业账目有什么不同
  • 已认证进项税转出口退税怎么处理
  • 防伪税控服务费全额抵扣申报表怎么填
  • 现金冲账是什么意思
  • 招待客户住宿属于什么费用
  • 股东的义务是什么意思?
  • 公司购入二手车没有发票怎么入账
  • 跨年冲红发票账务处理需要调整申报表吗
  • 会计账簿的设计原则有哪些
  • kvm虚拟机paused
  • kms激活的弊端
  • Vista下以真正的管理员登陆的设置方法
  • win32k.sys是什么
  • 2014年电脑装win7与win10
  • win8屏幕显示不全
  • 如何设置访问共享文件夹
  • 845主板支持1t硬盘吗
  • 怎么安装最新显卡驱动
  • 红帽企业版更新了吗
  • linux怎么查看硬件信息
  • 人在国外怎么补办身份证
  • Android与OpenCV2.4.4(2013最新)搭建图像处理框架
  • unity多人语音
  • javascript解析
  • android资源文件
  • 详解linux设备
  • jquery树形菜单
  • python基于django的项目
  • 深圳龙岗有几个街道
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设