概述

《你不知道的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. 安装好ubuntu双系统启动时卡死解决办法

    问题描述:在安装完ubuntu双系统后,第一次启动ubuntu系统时,卡死在启动界面(或者黑屏),这大概都是由于显卡驱动的原因,具体不在这里阐述,通过以下方法能成功解决,据我个人经验,这可能是诸多方法 ...

  2. 关于 web 页面 占满全屏

    页面一般可以分成三部分,头部,底部,中间内容部分. 一般不用考虑中间高度部分,因为可以靠内容撑开,然后让底部到达底部.但是当中间内容太少时,底部就会顶不到底部. 方法1.中间部分给一个最小高度(min ...

  3. Halcon常用算子02

    threshold:阈值分割       minGray<=g<=maxGray select_shape:选取特定区域(Region) regiongrowing:区域生长法分割图像获得 ...

  4. 特大数字之和,返回结果是字符串(考虑到数字特别大,如果相加会产生e)

    自己做的,没有整理代码,还是做出来了: 做这个题时,最总要的一步思路就是,先让长度一致,然后从个位开始,每一个与每一个数字相加,如果大于10,则下一次另外两个数相加时加1 function add(a ...

  5. Linux mysql 联表查询

    在rhce考试题中,第21.22题为数据库查询题 题目: 在system1上创建一个Maria DB数据库,名为Contacts,要求: 数据库应该包含来自数据库users.mdb的内容,数据库只能被 ...

  6. JS判断手机端是否安装某应用

    方法一(网页上判断) if (navigator.userAgent.match(/(iPhone|iPod|iPad);?/i)) {   var loadDateTime = new Date() ...

  7. GPL_LGPL

    LGPL 与GPL的区别   GPL(GNU General Public License) 我们很熟悉的Linux就是采用了GPL.GPL协议和BSD, Apache Licence等鼓励代码重用的 ...

  8. FortiGate部分用户上网慢,丢包严重

    1.现状: 如图,出口internet有2条联通线路分别为liant_218和liant_61,在防火墙上使用WAN LLB,基于源IP: 2.现象: 使用liant_218的用户上网正常,使用lia ...

  9. mybatis的基础Dao

    话不多说,直接贴代码吧,因为很多博客都需要用到这个基础dao,怕大家不好查询. 这个基类主要是使用了泛型,这样我就不必为每一个实体都写一个dao,大大节省了时间.其中sqlSessionTemplat ...

  10. Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.

    Error starting ApplicationContext. To display the conditions report re-run your application with 'de ...