当前位置: 首页 > 编程日记 > 正文

JavaScript继承详解(四)

文章截图 - 更好的排版

在本章中,我们将分析Douglas Crockford关于JavaScript继承的一个实现 - Classical Inheritance in JavaScript。
Crockford是JavaScript开发社区最知名的权威,是JSON、JSLint、JSMin和ADSafe之父,是《JavaScript: The Good Parts》的作者。
现在是Yahoo的资深JavaScript架构师,参与YUI的设计开发。 这里有一篇文章详细介绍了Crockford的生平和著作。
当然Crockford也是我等小辈崇拜的对象。

调用方式

首先让我们看下使用Crockford式继承的调用方式:
注意:代码中的method、inherits、uber都是自定义的对象,我们会在后面的代码分析中详解。

        // 定义Person类function Person(name) {this.name = name;}// 定义Person的原型方法Person.method("getName", function() {return this.name;});  // 定义Employee类function Employee(name, employeeID) {this.name = name;this.employeeID = employeeID;}// 指定Employee类从Person类继承Employee.inherits(Person);// 定义Employee的原型方法Employee.method("getEmployeeID", function() {return this.employeeID;});Employee.method("getName", function() {// 注意,可以在子类中调用父类的原型方法return "Employee name: " + this.uber("getName");});// 实例化子类var zhang = new Employee("ZhangSan", "1234");console.log(zhang.getName());   // "Employee name: ZhangSan"

这里面有几处不得不提的硬伤:

  • 子类从父类继承的代码必须在子类和父类都定义好之后进行,并且必须在子类原型方法定义之前进行。
  • 虽然子类方法体中可以调用父类的方法,但是子类的构造函数无法调用父类的构造函数。
  • 代码的书写不够优雅,比如原型方法的定义以及调用父类的方法(不直观)。

当然Crockford的实现还支持子类中的方法调用带参数的父类方法,如下例子:

        function Person(name) {this.name = name;}Person.method("getName", function(prefix) {return prefix + this.name;});function Employee(name, employeeID) {this.name = name;this.employeeID = employeeID;}Employee.inherits(Person);Employee.method("getName", function() {// 注意,uber的第一个参数是要调用父类的函数名称,后面的参数都是此函数的参数// 个人觉得这样方式不如这样调用来的直观:this.uber("Employee name: ")return this.uber("getName", "Employee name: ");});var zhang = new Employee("ZhangSan", "1234");console.log(zhang.getName());   // "Employee name: ZhangSan"

代码分析

首先method函数的定义就很简单了:

        Function.prototype.method = function(name, func) {// this指向当前函数,也即是typeof(this) === "function"this.prototype[name] = func;return this;};
要特别注意这里this的指向。当我们看到this时,不能仅仅关注于当前函数,而应该想到当前函数的调用方式。 比如这个例子中的method我们不会通过new的方式调用,所以method中的this指向的是当前函数。

inherits函数的定义有点复杂:

        Function.method('inherits', function (parent) {// 关键是这一段:this.prototype = new parent(),这里实现了原型的引用var d = {}, p = (this.prototype = new parent());// 只为子类的原型增加uber方法,这里的Closure是为了在调用uber函数时知道当前类的父类的原型(也即是变量 - v)this.method('uber', function uber(name) {// 这里考虑到如果name是存在于Object.prototype中的函数名的情况// 比如 "toString" in {} === trueif (!(name in d)) {// 通过d[name]计数,不理解具体的含义d[name] = 0;}        var f, r, t = d[name], v = parent.prototype;if (t) {while (t) {v = v.constructor.prototype;t -= 1;}f = v[name];} else {// 个人觉得这段代码有点繁琐,既然uber的含义就是父类的函数,那么f直接指向v[name]就可以了f = p[name];if (f == this[name]) {f = v[name];}}d[name] += 1;// 执行父类中的函数name,但是函数中this指向当前对象// 同时注意使用Array.prototype.slice.apply的方式对arguments进行截断(因为arguments不是标准的数组,没有slice方法)r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));d[name] -= 1;return r;});return this;});
注意,在inherits函数中还有一个小小的BUG,那就是没有重定义constructor的指向,所以会发生如下的错误:
        var zhang = new Employee("ZhangSan", "1234");console.log(zhang.getName());   // "Employee name: ZhangSan"console.log(zhang.constructor === Employee);    // falseconsole.log(zhang.constructor === Person);      // true

改进建议

根据前面的分析,个人觉得method函数必要性不大,反而容易混淆视线。 而inherits方法可以做一些瘦身(因为Crockford可能考虑更多的情况,原文中介绍了好几种使用inherits的方式,而我们只关注其中的一种), 并修正了constructor的指向错误。

        Function.prototype.inherits = function(parent) {this.prototype = new parent();this.prototype.constructor = this;this.prototype.uber = function(name) {f = parent.prototype[name];return f.apply(this, Array.prototype.slice.call(arguments, 1));};};
调用方式:
        function Person(name) {this.name = name;}Person.prototype.getName = function(prefix) {return prefix + this.name;};function Employee(name, employeeID) {this.name = name;this.employeeID = employeeID;}Employee.inherits(Person);Employee.prototype.getName = function() {return this.uber("getName", "Employee name: ");};var zhang = new Employee("ZhangSan", "1234");console.log(zhang.getName());   // "Employee name: ZhangSan"console.log(zhang.constructor === Employee);   // true

有点意思

在文章的结尾,Crockford居然放出了这样的话:

I have been writing JavaScript for 8 years now, and I have never once found need to use an uber function. The super idea is fairly important in the classical pattern, but it appears to be unnecessary in the prototypal and functional patterns. I now see my early attempts to support the classical model in JavaScript as a mistake.
可见Crockford对在JavaScript中实现面向对象的编程不赞成,并且声称JavaScript应该按照原型和函数的模式(the prototypal and functional patterns)进行编程。
不过就我个人而言,在复杂的场景中如果有面向对象的机制会方便的多。
但谁有能担保呢,即使像jQuery UI这样的项目也没用到继承,而另一方面,像Extjs、Qooxdoo则极力倡导一种面向对象的JavaScript。 甚至Cappuccino项目还发明一种Objective-J语言来实践面向对象的JavaScript。

相关文章:

C语言:在屏幕上输出信息

#include<stdio.h>int main(){printf ("This is a C program.\n");printf("welcome to bit\n");return 0;}结果&#xff1a;This is a C program.welcome to bitPress any key to continue转载于:https://blog.51cto.com/yaoyaolx/1715542

Colly源码解析——框架

Colly是一个使用golang实现的数据抓取框架&#xff0c;我们可以使用它快速搭建类似网络爬虫这样的应用。本文我们将剖析其源码&#xff0c;以探析其中奥秘。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; Collector是Colly的核心结构体&#xff0c;其中包含了…

未经任何测试的源代码开放

未经任何测试的源代码开放 http://files.cnblogs.com/TextEditor/TextBoxEx.rar 这个代码只是一个Demo. 请将一个Vb.net的代码放在C盘下面&#xff0c;并且改名为Test.txt&#xff0c;然后使用菜单的Open来打开文件。 有任何问题&#xff0c;请在这里留言。 C#的上色还没有完成…

助力企业抗疫,360金融推出免费AI语音机器人

复工潮来临之际&#xff0c;为帮助各大企业进行高效的内部防疫宣传、员工行程信息收集以及快速生成公司内部防疫排班表&#xff0c;360金融针对复工企业的需求痛点推出了AI语音机器人&#xff0c;以助力企业更高效的防疫、抗疫。 针对复工企业的需求痛点&#xff0c;360金融人…

实现strncat

函数原型char *strncat(char *front,char *back,size_t count)参数说明back为源字符串&#xff0c;front为目的字符串&#xff0c;count为指定的back中的前count个字符。 所在库名#include <string.h>函数功能把back所指字符串的前count个字符添加到front结尾处&a…

Colly源码解析——结合例子分析底层实现

通过《Colly源码解析——框架》分析&#xff0c;我们可以知道Colly执行的主要流程。本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 递归深度 以下例子截取于Basic c : colly.NewCollecto…

无限路由 DI-624+A 详细介绍

无线路由器硬件安装设置图解1、确认宽带线路正常&#xff1a;无线宽带路由器可以让您将家中的计算机共享高速宽带网络连结至互联网&#xff1b;但在此之前&#xff0c;您必须先具备一部基于以太网络的Cable/DSL Modem(使用RJ-45 接头)&#xff0c;并确定您的宽带网络在只有连接…

教你如何编写第一个爬虫

2019年不管是编程语言排行榜还是在互联网行业&#xff0c;Python一直备受争议&#xff0c;到底是Java热门还是Python热门也是一直让人争吵的话题。随着信息时代的迭代更新&#xff0c;人工智能的兴起&#xff0c;Python编程语言也随之被人们广泛学习&#xff0c;Python数据分析…

【BZOJ】3542: DZY Loves March

题意 \(m * m\)的网格&#xff0c;有\(n\)个点。\(t\)个询问&#xff1a;操作一&#xff1a;第\(x\)个点向四个方向移动了\(d\)个单位。操作二&#xff1a;询问同行同列其他点到这个点的曼哈顿距离和。强制在线。&#xff08;\(n \le 10^5&#xff0c;m \le 10^{18}\)&#xff…

Gin源码解析和例子——路由

Gin是一个基于golang的net包实现的网络框架。从github上&#xff0c;我们可以看到它相对于其他框架而言&#xff0c;具有优越的性能。本系列将从应用的角度来解析其源码。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 本文我们将分析其路由的原理。先看个例…

一文讲透推荐系统提供web服务的2种方式

作者丨gongyouliu编辑丨zandy来源 | 大数据与人工智能&#xff08;ID: ai-big-data&#xff09;推荐系统是一种信息过滤技术&#xff0c;通过从用户行为中挖掘用户兴趣偏好&#xff0c;为用户提供个性化的信息&#xff0c;减少用户的找寻时间&#xff0c;降低用户的决策成本&am…

jQuery遍历json数组怎么整。。。

{"options":"[{\"text\":\"王家湾\",\"value\":\"9\"},{\"text\":\"李家湾\",\"value\":\"10\"},{\"text\":\"邵家湾\",\"value\":\"13\…

述说C#中的值类型和引用类型的千丝万缕

关于值类型和引用类型方面的博客和文章可以说是汗牛充栋了&#xff0c;今天无意中又复读了一下这方面的知识&#xff0c;感觉还是有许多新感悟的&#xff0c;就此时间分享一下&#xff1a; CLR支持两种类型&#xff1a;值类型和引用类型&#xff0c;看起来FCL的大多数类型是引用…

Gin源码解析和例子——中间件(middleware)

在《Gin源码解析和例子——路由》一文中&#xff0c;我们已经初识中间件。本文将继续探讨这个技术。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; Gin的中间件&#xff0c;本质是一个匿名回调函数。这和绑定到一个路径下的处理函数本质是一样的。 再以Engin…

DNS简单配置

DNS的原理就不说了&#xff0c;这里只是做个简单的配置&#xff0c;也是方便自己记忆&#xff0c;在这里还要十分感谢redking老大的教程&#xff01;要安装的bind* 、caching-nameserver 包1、/var/named/chroot/etc/named.conf这个文件需要自己创建options { listen-on…

关系抽取论文整理,核方法、远程监督的重点都在这里

来源 | CSDN 博客作者 | Matt_sh&#xff0c;编辑 | Carol来源 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;本文是个人阅读文章的笔记整理&#xff0c;没有涉及到深度学习在关系抽取中的应用。笔记中一部分来自个人解读&#xff0c;一部分来自原文&#xff0…

freemarker内建函数介绍

Sequence的内置函数1.sequence?first 返回sequence的第一个值。2.sequence?last 返回sequence的最后一个值。3.sequence?reverse 将sequence的现有顺序反转&#xff0c;即倒序排序4.sequence?size 返回sequence的大小5.sequence?sort 将sequence中的对象转化为字符串后顺序…

PowerBuilder 11.x 的重要进步和不足

PowerBuilder 11&#xff08;以下简称PB&#xff09;出来有一段时间了&#xff0c;但很多用户对PB11的到底有哪些进步还不是很清楚&#xff0c;由于对PB11缺乏了解和信心&#xff0c;目前用PB11做出像样应用的用户不多&#xff0c;这确实非常遗憾&#xff0c;这里我讲一下我对P…

超赞的PyTorch资源大列表,GitHub标星9k+,中文版也上线了

点击阅读原文&#xff0c;快速报名&#xff01;作者 | 红色石头来源 | AI有道&#xff08;ID: redstonewill&#xff09;自 2017 年 1 月 PyTorch 推出以来&#xff0c;其热度持续上升。PyTorch 能在短时间内被众多研究人员和工程师接受并推崇是因为其有着诸多优点&#xff0c;…

C++拾取——Linux下实测布隆过滤器(Bloom filter)和unordered_multiset查询效率

布隆过滤器是一种判定元素是否存在于集合中的方法。其基本原理是使用哈希方法将数据映射到一个很长的向量上。在维基百科上&#xff0c;它被称为“空间效率和查询时间都远远超过一般的算法”的方法。由于它只保存散列的数据&#xff0c;所以对于很长的数据有着良好的压缩特性&a…

递归思想解决输出目录下的全部文件

刚刚了解了下递归思想 递归就是在方法内调用本方法 下面说一个实际的应用 输出目录下的全部文件&#xff0c;当目录中还有目录时&#xff0c;则进入目录输出里面的文件 import java.io.*; class ShowFile{public static void showfile(File files){if(files.isDirectory()){Fi…

实战之网马解密之shellcode篇

今天上卡卡社区发现里面发了个网马解密的链接,呵呵 顺便试试看能解出来不.呵呵. 相信各位已经对网马有点了解了吧.一般网马都是加密了的.关于什么是网马以及怎么防止网马也不是本文的重点.本文是实战shellcode网马解密.以后的博文会放出常见的网马及其解密.以及常见的解密工具的…

机器学习中的线性回归,你理解多少?

作者丨algorithmia编译 | 武明利&#xff0c;责编丨Carol来源 | 大数据与人工智能&#xff08;ID: ai-big-data&#xff09;机器学习中的线性回归是一种来源于经典统计学的有监督学习技术。然而&#xff0c;随着机器学习和深度学习的迅速兴起&#xff0c;因为线性&#xff08;多…

Golang反射机制的实现分析——reflect.Type类型名称

现在越来越多的java、php或者python程序员转向了Golang。其中一个比较重要的原因是&#xff0c;它和C/C一样&#xff0c;可以编译成机器码运行&#xff0c;这保证了执行的效率。在上述解释型语言中&#xff0c;它们都支持了“反射”机制&#xff0c;让程序员可以很方便的构建一…

设计模式----组合模式UML和实现代码

2019独角兽企业重金招聘Python工程师标准>>> 一、什么是组合模式&#xff1f; 组合模式(Composite)定义&#xff1a;将对象组合成树形结构以表示‘部分---整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性. 类型&#xff1a;结构型模式 顺口…

Golang反射机制的实现分析——reflect.Type方法查找和调用

在《Golang反射机制的实现分析——reflect.Type类型名称》一文中&#xff0c;我们分析了Golang获取类型基本信息的流程。本文将基于上述知识和经验&#xff0c;分析方法的查找和调用。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 方法 package mainimpor…

太狠!33岁年薪50万:“复工第一天,谢谢裁掉我!” 网友:有底气!

最近脉脉一则帖子炸锅了&#xff1a;某HR发帖称公司以按时下班为由裁员。这种情况下很多人都慌了&#xff0c;大家纷纷把“副业救国”奉为神律。可是你有没有认真的想过&#xff0c;为什么现在大家都需要副业&#xff1a;意外裁员后&#xff0c;房贷能够按时还上不至于“回收”…

SEO内部链接优化的技巧

内部链接是搜索引擎优化中的重要因素之一。思亿欧做的SEO调查发现&#xff0c;国内大部分网站都没有怎么做内部链接优化。这可能是网站管理员并不知晓SEO或者是对内部链接优化不够重视。 内部链接的设计不能是单纯的为了SEO的目的而作内部链接&#xff0c;同时要注意规划一个良…

Ubuntu 15.10安装ns2.35+nam

2019独角兽企业重金招聘Python工程师标准>>> Step1: 更新系统sudo apt-get update #更新源列表sudo apt-get upgrade #更新已经安装的包sudo apt-get dist-upgrade #更新软件&#xff0c;升级系统Step2:安装ns2需要的几个包sudo apt-get install build-essentialsu…

bug诞生记——不定长参数隐藏的类型问题

这个bug的诞生源于项目中使用了一个开源C库。由于对该C库API不熟悉&#xff0c;一个不起眼的错误调用&#xff0c;导致一系列诡异的问题。最终经过调试&#xff0c;我们发现发生了内存覆盖问题。为了直达问题根节&#xff0c;我将问题代码简化如下&#xff08;转载请指明出于br…