提到JavaScript“面向对象编程”,主要就是封装和继承,这里主要依据阮一峰及其他博客的系列文章做个总结。

继承机制的设计思想

  • 所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。

  • 由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像”继承”了prototype对象一样。

封装

主要介绍了如何”封装”数据和方法,以及如何从原型对象生成实例。

(1) 封装:把属性和方法封装成一个对象。

(2)如何根据某一规格(原型)生成对象:构造函数。相较于原始方法(用字面量一个一个手动生成对象),其优点在于:

  • 减少代码重复,将对象普遍具有的属性先用构造函数定义好,使用构造函数生成对象时传参即可为产生的对象的属性赋值;

  • 体现生成的对象与原型对象之间的联系。

  • 构造函数具有的prototype属性的优点:将所有对象具有的值相同的属性,挂在prototype上,无论生成多少个对象,它们都共用这一份prototype,在内存中只有一份,减少不必要的资源浪费。isPrototypeOf, hasOwnProperty()检测是本地属性还是原型属性,in遍历对象的所有属性,包括原型属性。

【注意】:构造函数中的属性是本地属性,每个对象都维护各自的值,prototype中的属性是引用属性,所有对象共有一份。原型属性与实例属性同名时,原型属性会被实例属性覆盖

构造函数的继承 (五种方法)

方法一:构造函数绑定

在子对象构造函数Cat()中加一行:

1
Animal.apply(this, arguments);

优点:可以实现多继承(call/apply多个对象);并且,创建子类实例时,可以向父类传递参数。

缺点:只能继承父类的实例属性和方法,不能继承父类的原型属性/方法。(这里引申出【组合继承】:在本方法基础上,加上方法二的原型继承,即call+prototype方法,组合继承既可以继承原型属性/方法,又可以继承实例属性/方法,而且还可传参)。

方法二:prototype模式(又称原型链继承,注意需要对constructor修正)

将父类的实例作为子类的原型:如果”猫”的prototype对象,指向一个Animal的实例,那么所有”猫”的实例,就能继承Animal了。

1
2
Cat.prototype = new Animal(); //它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。包括改写Cat.prototype.constructor为Animal
Cat.prototype.constructor = Cat; //如果不改回去,会导致继承链的混乱

【注意】:

  • 任何一个prototype对象(cat.prototype)都有一个constructor属性,指向它的构造函数。

  • 更重要的是,每一个实例(cat1)也有一个constructor属性,默认调用prototype对象的constructor属性。

=>

【普适性的规则】:如果替换了prototype对象,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数:

1
2
o.prototype = {};
o.prototype.constructor = o;

缺点:无法实现多继承;创建子类实例时,无法向父类构造函数传参。

方法三:直接继承prototype

优点是 效率比较高(不用执行和建立Animal的实例了),比较省内存。

缺点是 Cat.prototypeAnimal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype

1
2
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat; //这样导致`Animal.prototype.const 大专栏  JavaScript的封装和继承ructor`也被改掉了。下面的方法四针对这个问题用空函数作中介做了改进。

方法四:利用空对象作为中介 (又称寄生组合继承,对方法三的改良):完美

1
2
3
4
5
6
7
function (Child, Parent) {
  var F = function(){};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
  Child.uber = Parent.prototype; //在子对象上打开一条通道,可以直接调用父对象的方法,纯属备用性质,实现继承的完备性
}

方法五:拷贝继承:把Parent.prototype的所有属性和方法,拷贝进Child.prototype

1
2
3
4
5
6
7
8
9
function (Child, Parent) {
var p = Parent.prototype;
  var c = Child.prototype;
  for (var i in p) {
    c[i] = p[i];
  }
  c.uber = p;
}

拷贝继承支持多继承。

非构造函数的继承

继承某个具体的对象。

方法一:object()(实质上还是原型继承)

1
2
3
4
5
function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

方法二:浅拷贝(只能拷贝数据全是基本类型的对象)

存在问题:如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。

方法三:深拷贝

递归调用浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object' ) {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}

优点:支持多继承。

缺点:效率较低,内存占用高(因为要拷贝父类的属性);无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)。

JavaScript的封装和继承的更多相关文章

  1. Javascript面向对象(封装、继承)

    Javascript 面向对象编程(一):封装 作者:阮一峰 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.但是,它又不是一种真正的面向对象编程( ...

  2. Javascript面向对象特性实现封装、继承、接口详细案例——进级高手篇

    Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...

  3. JavaScript 定义类的最佳写法——完整支持面向对象(封装、继承、多态),兼容所有浏览器,支持用JSDuck生成文档

    作者: zyl910 [TOC] 一.缘由 由于在ES6之前,JavaScript中没有定义类(class)语法.导致大家用各种五花八门的办法来定义类,代码风格不统一.而且对于模拟面向对象的三大支柱& ...

  4. Javascript面向对象特性实现封装、继承、接口详细案例

    Javascript面向对象特性实现(封装.继承.接口) Javascript作为弱类型语言,和Java.php等服务端脚本语言相比,拥有极强的灵活性.对于小型的web需求,在编写javascript ...

  5. javascript面向对象编程,带你认识封装、继承和多态

    原文链接:点我 周末的时候深入的了解了下javascript的面向对象编程思想,收获颇丰,感觉对面向对象编程有了那么一丢丢的了解了~很开森 什么是面向对象编程 先上一张图,可以对面向对象有一个大致的了 ...

  6. 浅谈JavaScript的面向对象和它的封装、继承、多态

    写在前面 既然是浅谈,就不会从原理上深度分析,只是帮助我们更好地理解... 面向对象与面向过程 面向对象和面向过程是两种不同的编程思想,刚开始接触编程的时候,我们大都是从面向过程起步的,毕竟像我一样, ...

  7. Jser 设计模式系列之面向对象 - 接口封装与继承

    GOF在<设计模式>中说到:面向接口编程,而非面向实现编程 鉴于此,这个概念可见一斑! JS却不像其他面向对象的高级语言(C#,Java,C++等)拥有内建的接口机制,以确定一组对象和另一 ...

  8. 深入浅出JS的封装与继承

    JS虽然是一个面向对象的语言,但是不是典型的面向对象语言.Java/C++的面向对象是object - class的关系,而JS是object - object的关系,中间通过原型prototype连 ...

  9. JavaScript对寄生组合式继承的理解

    有关JavaScript的几种继承方式请移步JavaScript的几种继承方式 原型链的缺陷 SubType.prototype = new SuperType(); 这样做的话,SuperType构 ...

随机推荐

  1. Spark宽依赖、窄依赖

    在Spark中,RDD(弹性分布式数据集)存在依赖关系,宽依赖和窄依赖. 宽依赖和窄依赖的区别是RDD之间是否存在shuffle操作. 窄依赖 窄依赖指父RDD的每一个分区最多被一个子RDD的分区所用 ...

  2. CPA-计划

    平时周一到周五上班晚上8点到12点,周末6-8个小时,然后没有节假日,一次差不多可以3.4科 审计 看150页  3小时,看完,做题 2天时间,5门课程,12小时考试,没想到能完整地挺过来.感觉税法战 ...

  3. G. Petya and Graph(经典项目与项目消耗问题)(网络流)

    题:https://codeforces.com/contest/1082/problem/G 题意:给定有边权和点权的图,问你选一些边,然sum边-sum点最大(点权被多次用为公共点只会减一次) 分 ...

  4. 关于前端JS的总结

    简介 JavaScript是一种计算机编程语言,可以像等其他编程语言那样定义变量,执行循环等.主要执行在浏览器上,为HTML页面提供动态效果,而且JavaScript是一种脚本语言,它的代码是解释执行 ...

  5. Flume(三) —— 断点续传 与 事务

    断点续传 # Name the components on this agent a1.sources = r1 a1.sinks = k1 a1.channels = c1 # Describe / ...

  6. Python_实战爬虫

    # -*- coding: utf-8 -*-__auther__ = "jiachaojun"__time__ = '2020/1/12 11:03'import request ...

  7. Nginx_安全2

    Nginx与安全有关的配置 隐藏版本号 http {    server_tokens off;} 经常会有针对某个版本的nginx安全漏洞出现,隐藏nginx版本号就成了主要的安全优化手段之一,当然 ...

  8. java 之断言

    今天用idea的智能提示冒出一个assert关键字,愣是没看懂!!!还是太菜了.上网查了一下,这个关键字是断言. 什么是断言? 我也说不清楚,反正就是对jvm的操作.java的错误分为两种,一种叫er ...

  9. mac环境下创建bash_profile文件并写入内容 更改php环境变量

    1. 启动终端Terminal 2. 进入当前用户的home目录 输入cd ~ 3. 创建.bash_profile 输入touch .bash_profile 4. 编辑.bash_profile文 ...

  10. docker容器中安装中文字体

    在项目中用到pdf导出功能,需要安装中文字体,项目使用docker部署,为了方便决定在将字体安装在镜像中. 1.在dockerfile文件中添加字体copy语句(本次用是的宋体,字体源文件放在dock ...