概述

《你不知道的JavaScript》中有这么一段话:不幸的是,将类和继承的设计模式思维带入Javascript的想法是你所做的最坏的事情,因为语法可能会让你迷惑不已,让你以为真的有类这样的东西存在,实际上原型机制与类的行为特性是完全相反的。

js的原型机制本质上是行为委托机制。行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。

面向类的编程

class Polygon {
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}
} class Rectangle extends Polygon {
constructor(height, width) {
super(height, width);
this.name = 'Rectangle';
}
} class Square extends Rectangle {
constructor(length) {
super(length, length);
this.name = 'Square';
}
}

如上所示,是一个四边形——矩形——正方形的类关系图。看起来设计的很好,但是由于他们的层级关系,他们有这些缺陷:

  1. 每次都要写constructor,有时还要去写prototype。
  2. 如果矩形这个类要增加一个行为,但是这个行为在正方形中不需要怎么办?
  3. 如果要在矩形和正方形之间再增加一层,要怎么办?

用面向类的设计模式,如果遇到上面3个问题,处理起来会很麻烦,这就导致整个架构非常难扩展。原因是在设计整个类的架构的时候,我们已经规定好了每个类的行为以及层级结构,如果后续它们有变动,就会触及到架构问题,所以修改起来会很麻烦。

行为委托

怎么解决上述的问题1呢?方法是利用行为委托。

let polygon = {
init: function(width, height){
this.width = width;
this.height = height;
this.setName();
},
setName: function() {
this.name = 'polygon';
}
} let rectangle = Object.create(polygon); rectangle.setName = function() {
this.name = 'rectangle';
} //初始化一个polygon
const a = Object.create(polygon);
a.init(2,3); //初始化一个rectangle
const b = Object.create(rectangle);
b.init(3,4); //初始化一个rectangle
const c = Object.create(rectangle);
c.init(4,5);

可以看到,利用Object.create方法,使rectangle的prototype委托了polygon对象,从而能够使用polygon对象的各种方法。这样做的优点是,整个过程非常简洁,我们不需要去探究prototype,也不需要去管constructor。

值得一提的是,我们可以通过Object.assign来扩充rectangle对象的方法,而不需要一条条去写rectangle的方法。实例如下:

Object.assign(rectangle, {
setName: function() {
this.name = 'polygon';
},
setWidth: function(width) {
this.width = width;
},
setHeight: function(height) {
this.height = height;
}
});

无类编程

为了解决问题2和3,我们打算降低层级,对所有的类进行“扁平化”管理,即是说,让Polygon作为最底层的类,然后所有的类都继承自它,这样我们就非常好扩展了。实例如下:

class Polygon {
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}
} class Rectangle extends Polygon {
constructor(height, width) {
super(height, width);
this.name = 'Rectangle';
}
} const rectangleMixinPublic = {
rectanglePublic1: function(){ },
rectanglePublic2: function(){ }
} const rectangleMixinPrivate = {
rectanglePrivate1: function(){ },
rectanglePrivate2: function(){ }
} Object.assign(Rectangle.prototype, rectangleMixinPublic, rectangleMixinPrivate); class Square extends Polygon {
constructor(length) {
super(length, length);
this.name = 'Square';
}
} const squareMixinPublic = {
squarePublic1: function(){ },
squarePublic2: function(){ }
} const squareMixinPrivate = {
squarePrivate1: function(){ },
squarePrivate2: function(){ }
} Object.assign(Square.prototype,
rectangleMixinPublic,
rectangleMixinPrivate,
squareMixinPublic,
squareMixinPrivate);

从上面的代码可以看到,通过利用扁平化使类都继承于基类,然后利用Object.assign方法进行扩展prototype,使整个架构非常灵活。

其它

当然,我上面只是举了一个例子来说明这几种编程方式,它们的具体应用还有很多方面,比如行为委托还用于封装行为,无类继承还可以通过函数封装成函数式编程的形式。

js中的行为委托和无类编程的更多相关文章

  1. 怎么理解js中的事件委托

    怎么理解js中的事件委托 时间 2015-01-15 00:59:59  SegmentFault 原文  http://segmentfault.com/blog/sunchengli/119000 ...

  2. js中var的有或无--重复声明和以后的声明

    js中var的有或无--重复声明和以后的声明 使用var语句多次声明一个变量不仅是合法的,而且也不会造成任何错误. 如果重复使用的一个声明有一个初始值,那么它担当的不过是一个赋值语句的角色. 如果重复 ...

  3. js中的事件委托或是事件代理

    JavaScript(jQuery)中的事件委托 https://www.cnblogs.com/zhoushengxiu/p/5703095.html js中的事件委托或是事件代理详解 https: ...

  4. JS中的事件委托(事件代理)

    一步一步来说说事件委托(或者有的资料叫事件代理) js中事件冒泡我们知道,子元素身上的事件会冒泡到父元素身上. 事件代理就是,本来加在子元素身上的事件,加在了其父级身上. 那就产生了问题:父级那么多子 ...

  5. js中的事件委托(事件代理)详解

    本文转载:https://www.cnblogs.com/liugang-vip/p/5616484.html#!comments js中的事件冒泡.事件委托是js 中一些需要注意的小知识点,这里结合 ...

  6. JS中的单例模式及单例模式原型类的实现

    单例模式 单例模式的定义: 保证一个类只有一个实例,并提供一个访问它的全局访问点 通过一个简单的例子来了解单例模式的作用: class Div { constructor() { return doc ...

  7. js中的事件委托或是事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

  8. js中的事件委托详解

    概述: 那什么叫事件委托呢?它还有一个名字叫事件代理,JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件.那这是什么意思呢?网上的各位 ...

  9. [转] js中的事件委托或是事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

随机推荐

  1. SQL With (递归CTE查询)

    指定临时命名的结果集,这些结果集称为公用表表达式 (CTE).该表达式源自简单查询,并且在单条 SELECT.INSERT.UPDATE 或 DELETE 语句的执行范围内定义.该子句也可用在 CRE ...

  2. centos 7 一键安装gitlab

    # cat /etc/redhat-release CentOS release 6.5 (Final) # strings /lib64/libc.so.6 |grep GLIBC_ 首先升级 如果 ...

  3. oracle数据库名称已被一现有约束条件占用

    使用oracle数据库出现名称已被一现有约束条件占用的错误,我的原因是在同一个库中有一个表使用了外键FK_SNO,自己新建的一个表中也使用了外键FK_SNO,导致出现了错误. 这时改变一下外键FK_S ...

  4. Springboot学习07-数据源Druid

    Springboot学习07-数据源Druid 关键字 Druid 前言 学习笔记 正文 1-Druid是什么 Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池.插件框架和SQL解析器 ...

  5. 10.13 新版本go on~

    上午1.5 终审 and 排期 合同管理那边又是切换选项时各种联动,我第一想法是 好麻烦,不想做这个...第二想法才是给我做吧 锻炼锻炼我 然后 分任务的时候 分给我了,,哈哈 开心 虽然我没想躲 但 ...

  6. 工欲善其事,必先利其器-ecplise配置和优化

    1.eclipse下的编码设置:eclipse 中使用模板新建 JSP,xhtml等 文件时,默认的编码为:ISO-8859-1. ISO-8859-1 编码对于中文的显示是不支持的,如果要支持简体中 ...

  7. Math的方法;Date的方法;

    Math的方法: (1)弧度的π  Math.PI() (2)返回平方根 Math.sqrt() (3)返回x的绝对值 Mathabs(x) (4)返回x的上舍入 Math.ceil(x) (5)返回 ...

  8. MQ消息队列在软件开发中的作中

    MQ的作用是非常之大的. 1.解耦. 当一个大型的系统.比如,商城系统.包括以下的功能: 1.发邮件 2.发短信 3.抽奖 4.搜索等 如果你都用一台服务器,做到一个程序里,代码会非常庞大,不利于维护 ...

  9. java【基础】日期操作

    主要是date类,SimpleDateFormat类以及Calendar类的使用. date表示日期,simpleDateFormat 表示日期格式化,Calendar一般用来做时间的操作,比如加减天 ...

  10. Android app中存储文件的路径

    // 获得缓存文件路径,磁盘空间不足或清除缓存时数据会被删掉,一般存放一些临时文件 // /data/data/<application package>/cache目录 File cac ...