javascript继承之学习笔记
今天记录一下学习javascript的继承。
继承基本上是基于“类”来说的,而javascript中并不存在真正的类,所以就出现了各种模拟“类”的行为,然后就堂而皇之的使用起了类的概念。这里不谈“类”的概念对js学习造成了多大的困难,只说一下普通意义上继承的6种方式。
1、原型链继承
这是js开发人员一开始接触js,最容易学会的,所以说他就是最简单的继承的方法:
function Super(val) {
   this.val = val;
   this.arr = [1];
 }
 Super.prototype.fn = function () {
   console.log(this.arr)
 }
 function Subtype(val, name) {}
 Subtype.prototype = new Super();
 var b1 = new Subtype(12);
 console.log(b1.val)				// undefined
 var b2 = new Subtype();
 b1.fn();        			// [1]  
 b1.arr.push(2);  
console.log(b2.arr);        //  [1,2]
很容易的构造了一个子类Subtype,继承了父类的所有属性和共享方法。
但是缺点也很明显:
1、子类无法传入初始参数,对于这里而言,就是Supertype无法为内部属性val传递一个合理的值;b1实例化的时候,为val传入参数12,但是b1.val却取不到该值。
2、如果属性值不是一个标量基本类型,是一个对象,则会出现如果子类实例对该对象进行修改,由于对象属性所存储的仅仅是一个引用,而不是其真实值,所以会导致修改一个实例的对象属性的值,导致另一个实例的该对象引用的值跟着变化。这里就是对实例b1的属性arr添加值操作的话,实例2引用的arr会同事变化,因为本质上他们所指向的是一个值。
为了修改上述问题,则引申出了第二种继承方式:
2、借用构造函数
     function Super(val) {
        this.val = val;
        this.arr = [1];
        this.fn = function () {
          console.log('prototype');
        }
      }
      function Subtype() {
        Super.apply(this, arguments);
      }
      var sub1 = new Subtype('s', 'zhongg');
      var sub2 = new Subtype('sdsdds', 'fsdajfsldaf');
      sub1.arr.push(5);
      console.log(sub2.arr);
很明显,修改了上述原型链继承方式的两个存在问题,但是却又产生了一个新的问题,函数不能公用的问题,由于子类是直接调用的父类函数,这就与原型prototype没有任何关系了,从而会导致定义在原型prototype上的任何函数都不起作用,这么一来,所有需要用到的函数就必须定义在父类函数内部。当子类比较多,实例化很多的时候,这就会造成每一个实例化都实例化了相同的函数,会占用大量内存。
那么必然的,既然目前的解决方案还不完美,肯定会有更完美的解决方案:
3、组合继承
      function Super(val) {
        this.val = val;
        this.arr = [1];
      }
      Super.prototype.fn = function () {
        console.log(this.val + 'prototype');
      }
      function Subtype(val, name) {
        Super.apply(this, arguments);
      }
      Subtype.prototype = new Super();
      var sub1 = new Subtype('值', '键值对');
      var sub2 = new Subtype('值2', '键值对2');
      sub1.fn();
组合继承,顾名思义,就是组合了其他的继承方式而成,这里其实就是组合了原型链继承和借用构造函数继承。
已经基本上完美了,综合了前两种继承方式的所有优点。既然说是基本上完美,那肯定还是有点瑕疵的,这里的瑕疵就是调用了两次父类函数,一次直接调用,一次new调用,导致生成了两份的实例属性,对于内存而言也是一种浪费。
到ES5时候,出现了一种可以真正完美解决继承所有缺点的继承方式:
4、寄生组合继承
     function Super(val) {
        this.val = val;
        this.arr = [1];
      }
      Super.prototype.fn = function () {
        console.log(10 + this.val);
      }
      function Subtype(val, name) {
        Super.apply(this, arguments);
        this.name = name;
      }
      Subtype.prototype = Object.create(Super.prototype);
      Subtype.prototype.fun = function () {
        console.log("fnu:" + this.name);
      }
      Subtype.prototype.constructor = Subtype;
      var sub1 = new Subtype('zhi', '名字是什么比较好呢?');
      var sub2 = new Subtype('again', 'nameto');
      console.log(sub1.constructor)
      sub1.fun();
      sub1.fn();
      sub1.arr.push(3);
利用的是ES5当中的Object.create(...),《你不知道的javascript》中对Object.create(..)的解释:Object.create(..)会创建一个新对象并把他关联到我们指定的对象。这里就是创建一个新的对象 Subtype.prototype 并把他关联到 Super.prototype,这里也会产生另外一个小问题,需要进行修补。就是Object.create(..)创建的对象没有constructor属性,如果需要constructor属性的话,那么在Subtype.prototype创建之后,需要手动修补,Subtype.prototype.constructor = Subtype。这么一来,继承上产生的各种问题都真正完美了。
由于Object.create(...)是在ES5中定义的,所以这个方案提出之前,其实利用的是Object.create(...)的polyfill的方案,类似于:
    if (!Object.create) {
        Object.create = function (o) {
          function F (){};
          F.prototype = o;
          return new F();
        }
      }
但是由于该方案出现的时间比较晚,虽然此方案是真正完美的方案,但是并没有上述的组合继承的方案使用广泛。事实上,组合继承的方案也基本上能够保证我们的日常开发了。
另外还有两种继承方案,可是已经不太像继承了:
5、原型式继承
      function Super(val) {
        this.val = val;
        this.arr = [1];
      }
      Super.prototype.fn = function () {
        console.log(10 + this.val);
      }
      var sup = new Super('父类');
      var sub = Object.create(sup);
      sub.func1= function(){}; 
    // 后面就是为子类sub添加各种需要的方法或者属性
仔细比较一下,其实原型式继承和原型链继承还是比较相似的,区别在于原型链继承的 Subtype.prototype = new Super();而原型式继承为 var sup = new Super('父类'); var sub = Object.create(sup);应该能够看出区别所在了。
6、寄生继承
最后一种继承方式真心的不是很想说,没什么可说的,其实就是把原型式继承的子类继承部分封装成函数来实现而已。
     function Super(val) {
        this.val = val;
        this.arr = [1];
      }
      Super.prototype.fn = function () {
        console.log(10 + this.val);
      }
      function Subtype(o){
        var clone = Object.create(o);
        clone.func1= function(){};
        //  添加各种属性
      }
      var sub = Subtype(new Super());
在日常开发中,一般使用第三种组合继承的方式,如果想要求更高一点的话,可以使用第四种寄生组合继承的方式。
javascript继承之学习笔记的更多相关文章
- ArcGIS API for JavaScript 4.2学习笔记[0] AJS4.2概述、新特性、未来产品线计划与AJS笔记目录
		
放着好好的成熟的AJS 3.19不学,为什么要去碰乳臭未干的AJS 4.2? 4.2全线基础学习请点击[直达] 4.3及更高版本的补充学习请关注我的博客. ArcGIS API for JavaScr ...
 - JavaScript 权威指南-学习笔记(一)
		
本文所有教程及源码.软件仅为技术研究.不涉及计算机信息系统功能的删除.修改.增加.干扰,更不会影响计算机信息系统的正常运行.不得将代码用于非法用途,如侵立删! ## JavaScript 权威指南-学 ...
 - 7 种 Javascript 常用设计模式学习笔记
		
7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...
 - ArcGIS API for JavaScript 4.2学习笔记[1] 显示地图
		
ArcGIS API for JavaScript 4.2直接从官网的Sample中学习,API Reference也是从官网翻译理解过来,鉴于网上截稿前还没有人发布过4.2的学习笔记,我就试试吧. ...
 - 《你不知道的 JavaScript 上卷》 学习笔记
		
第一部分: 作用域和闭包 一.作用域 1. 作用域:存储变量并且查找变量的规则 2. 源代码在执行之前(编译)会经历三个步骤: 分词/此法分析:将代码字符串分解成有意义的代码块(词法单元) 解析/语法 ...
 - JavaScript权威设计--JavaScript函数(简要学习笔记十一)
		
1.函数调用的四种方式 第三种:构造函数调用 如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内.这和函数调用和方法调用是一致的.但如果构造函数没有形参,JavaScri ...
 - JavaScript权威设计--JavaScript函数(简要学习笔记十)
		
1.函数命名规范 函数命名通常以动词为前缀的词组.通常第一个字符小写.当包含多个单词时,一种约定是将单词以下划线分割,就像"like_Zqz()". 还有一种就是"lik ...
 - ArcGIS API for JavaScript 4.2学习笔记[21] 对3D场景上的3D要素进行点击查询【Query类学习】
		
有人问我怎么这个系列没有写自己做的东西呢? 大哥大姐,这是"学习笔记"啊!当然主要以解读和笔记为主咯. 也有人找我要实例代码(不是示例),我表示AJS尚未成熟,现在数据编辑功能才简 ...
 - JavaScript 对象 之继承对象 学习笔记
		
假设,我们有个这样的需求: 两个种族,每个种族都有 名字.血量(默认200).行为(行为有 跳跃.移动速度 这些属性)等共有属性. 人族能量值比兽人多10%,兽人血量比人族多10%. 职业有战士和法师 ...
 
随机推荐
- Appium Desktop Inspector 安卓真机配置(Windows)
			
本文是基于 Windows环境 通过Appium Desktop 测试真机,首先要确保测试机已经和电脑正确连接(将手机和电脑通过USB数据线连接,手机打开USB调试) 确认电脑与手机是否连接成功的方法 ...
 - ASP.NET AJAX入门系列(3):使用ScriptManagerProxy控件
			
在ASP.NET AJAX中,由于一个ASPX页面上只能有一个ScriptManager控件,所以在有母版页的情况下,如果需要在Master-Page和Content-Page中需要引入不同的脚本时, ...
 - Linux VMware安装VMTools工具
			
安装VMTools工具 2)先启动CentOS并成功登录如下图,发现底部提示且窗口中等大小,准备安装 3)选择虚拟机菜单栏--安装VMware tools 4)光驱自动挂载VMTools 5)右键解压 ...
 - linux od命令详解
			
Linux od命令 Linux od命令用于输出文件内容.od指令会读取所给予的文件的内容,并将其内容以八进制字码呈现出来 将指定文件以八进制形式(默认)转储到标准输出.如果指定了多于一个的文件参数 ...
 - 如何在Visual Studio 2013中连接中国版的Azure
			
http://diaosbook.com/Post/2014/8/23/connect-azure-cn-in-vs2013 VS2013的Server Explorer在第一次连接Azure的时 ...
 - VS2013编译Qt5.2.1 32位静态库debug-and-release版及结果分享
			
1. 下载zip源码,我下载的是qt-everywhere-opensource-src-5.2.1.zip这个文件. 2.安装python 3.解压缩qt-everywhere-opensource ...
 - linux lftp
			
1.登录 lftp 用户名@站点 口令: 例如: lftp jiangzhaowei@192.168.199.73 口令:****** lftp jiangzhaowei@192.168.199.73 ...
 - Azure SQL 数据库仓库Data Warehouse (3) DWU
			
<Windows Azure Platform 系列文章目录> 在笔者的上一篇文章中:Azure SQL 数据库仓库Data Warehouse (2) 架构 介绍了SQL DW的工作节点 ...
 - MySQL锁之三:MySQL的共享锁与排它锁编码演示
			
一.行锁之MySQL 使用SELECT ... FOR UPDATE 做事务写入前的确认 以MySQL 的InnoDB 为例,预设的Tansaction isolation level 为REPEA ...
 - 学习笔记之tmux
			
Home · tmux/tmux Wiki · GitHub https://github.com/tmux/tmux/wiki tmux is a terminal multiplexer. It ...