单一职责原则

就一个类而言,应该仅有一个引起它变化的原因。在 JavaScript 中,需要用到类的场景并不太多,单一职责原则更多地是被运用在对象或者方法级别上,因此本节我们的讨论大多基于对象和方法。

单一职责原则(SRP)的职责被定义为“引起变化的原因”。如果我们有两个动机去改写一个方法,那么这个方法就具有两个职责。每个职责都是变化的一个轴线,如果一个方法承担了过多的职责,那么在需求的变迁过程中,需要改写这个方法的可能性就越大。

此时,这个方法通常是一个不稳定的方法,修改代码总是一件危险的事情,特别是当两个职责耦合在一起的时候,一个职责发生变化可能会影响到其他职责的实现,造成意想不到的破坏,这种耦合性得到的是低内聚和脆弱的设计。

因此,SRP 原则体现为:一个对象(方法)只做一件事情。

设计模式中的SRP原则

SRP 原则在很多设计模式中都有着广泛的运用,例如代理模式、迭代器模式、单例模式和装饰者模式。

代理模式

通过增加虚拟代理的方式,把预加载图片的职责放到代理对象中,而本体仅仅负责往页面中添加 img 标签,这也是它最原始的职责。

myImage 负责往页面中添加 img 标签:

const myImage = (function () {
	const imgNode = document.createElement('img');
	document.body.appendChild(imgNode);
	return {
		setSrc: function (src) {
			imgNode.src = src;
		}
	}
})();

proxyImage 负责预加载图片,并在预加载完成之后把请求交给本体 myImage

const proxyImage = (function () {
	const img = new Image;
	img.onload = function () {
		myImage.setSrc(this.src);
	}
	return {
		setSrc: function (src) {
			myImage.setSrc('file:// /C:/Users/svenzeng/Desktop/loading.gif');
			img.src = src;
		}
	}
})();
proxyImage.setSrc('http:// imgcache.qq.com/music/photo/000GGDys0yA0Nk.jpg');

把添加 img 标签的功能和预加载图片的职责分开放到两个对象中,这两个对象各自都只有一个被修改的动机。在它们各自发生改变的时候,也不会影响另外的对象。

迭代器模式

我们有这样一段代码,先遍历一个集合,然后往页面中添加一些 div,这些 divinnerHTML 分别对应集合里的元素:

const appendDiv = function (data) {
	for (let i = 0, l = data.length; i < l; i  ) {
		const div = document.createElement('div');
		div.innerHTML = data[i];
		document.body.appendChild(div);
	}
};
appendDiv([1, 2, 3, 4, 5, 6]);

这其实是一段很常见的代码,经常用于 ajax 请求之后,在回调函数中遍历 ajax 请求返回的数据,然后在页面中渲染节点。

appendDiv 函数本来只是负责渲染数据,但是在这里它还承担了遍历聚合对象 data 的职责。 我们想象一下,如果有一天 api 返回的 data 数据格式从 array 变成了 object,那我们遍历 data 的代码就会出现问题,必须改成 for ( let i in data )的方式,这时候必须去修改 appendDiv 里的代码,否则因为遍历方式的改变,导致不能顺利往页面中添加 div 节点。

我们有必要把遍历 data 的职责提取出来,这正是迭代器模式的意义,迭代器模式提供了一种方法来访问聚合对象,而不用暴露这个对象的内部表示。

当把迭代聚合对象的职责单独封装在 each 函数中后,即使以后还要增加新的迭代方式,我们只需要修改 each 函数即可,appendDiv 函数不会受到牵连,代码如下:

const each = function (obj, callback) {
	let value,
		i = 0,
		length = obj.length,
		isArray = isArraylike(obj); // isArraylike 函数这里未实现
	if (isArray) { // 迭代类数组
		for (; i < length; i  ) {
			callback.call(obj[i], i, obj[i]);
		}
	} else {
		for (i in obj) { // 迭代 object 对象
			value = callback.call(obj[i], i, obj[i]);
		}
	}
	return obj;
};
const appendDiv = function (data) {
	each(data, function (i, n) {
		const div = document.createElement('div');
		div.innerHTML = n;
		document.body.appendChild(div);
	});
};
appendDiv([1, 2, 3, 4, 5, 6]);
appendDiv({ a: 1, b: 2, c: 3, d: 4 });

单例模式

在JavaScript单例模式实现过一个惰性单例,最开始的代码是这样的:

const createLoginLayer = (function () {
	let div;
	return function () {
		if (!div) {
			div = document.createElement('div');
			div.innerHTML = '我是登录浮窗';
			div.style.display = 'none';
			document.body.appendChild(div);
		}
		return div;
	}
})();

现在我们把管理单例的职责和创建登录浮窗的职责分别封装在两个方法里,这两个方法可以独立变化而互不影响,当它们连接在一起的时候,就完成了创建唯一登录浮窗的功能,下面的代码显然是更好的做法:

const getSingle = function (fn) { // 获取单例
	let result;
	return function () {
		return result || (result = fn.apply(this, arguments));
	}
};
const createLoginLayer = function () { // 创建登录浮窗
	const div = document.createElement('div');
	div.innerHTML = '我是登录浮窗';
	document.body.appendChild(div);
	return div;
};
const createSingleLoginLayer = getSingle(createLoginLayer);
const loginLayer1 = createSingleLoginLayer();
const loginLayer2 = createSingleLoginLayer();
alert(loginLayer1 === loginLayer2); // 输出: true 

何时应该分离职责

SRP 原则是所有原则中最简单也是最难正确运用的原则之一。

要明确的是,并不是所有的职责都应该一一分离。

一方面,如果随着需求的变化,有两个职责总是同时变化,那就不必分离他们。比如在 ajax 请求的时候,创建 xhr 对象和发送 xhr 请求几乎总是在一起的,那么创建 xhr 对象的职责和发送 xhr 请求的职责就没有必要分开。

另一方面,职责的变化轴线仅当它们确定会发生变化时才具有意义,即使两个职责已经被耦合在一起,但它们还没有发生改变的征兆,那么也许没有必要主动分离它们,在代码需要重构的时候再进行分离也不迟。

违反 SRP 原则

在人的常规思维中,总是习惯性地把一组相关的行为放到一起,如何正确地分离职责不是一件容易的事情。

一方面,我们受设计原则的指导,另一方面,我们未必要在任何时候都一成不变地遵守原则。在实际开发中,因为种种原因违反 SRP 的情况并不少见。比如 jQueryattr 等方法,就是明显违反 SRP 原则的做法。jQueryattr 是个非常庞大的方法,既负责赋值,又负责取值,这对于 jQuery 的维护者来说,会带来一些困难,但对于 jQuery 的用户来说,却简化了用户的使用。

在方便性与稳定性之间要有一些取舍。具体是选择方便性还是稳定性,并没有标准答案,而是要取决于具体的应用环境。

SRP 原则的优缺点

SRP 原则的优点是降低了单个类或者对象的复杂度,按照职责把对象分解成更小的粒度,这有助于代码的复用,也有利于进行单元测试。当一个职责需要变更的时候,不会影响到其他的职责。

但 SRP 原则也有一些缺点,最明显的是会增加编写代码的复杂度。当我们按照职责把对象分解成更小的粒度之后,实际上也增大了这些对象之间相互联系的难度。

到此这篇关于JavaScript单一职责原则深入分析的文章就介绍到这了,更多相关JS单一职责原则内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

JavaScript单一职责原则深入分析的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  6. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

  7. ios – 如何使用JSExport导出内部类的方法

    解决方法似乎没有办法将内部类函数导出到javascript.我将内部类移出并创建了独立的类,它起作用了.

  8. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  9. ios – 内存泄漏与UIWebView和Javascript

    清楚地包含一个Javascript文件到我的HTML是使UIWebView泄漏内存.当我重复使用相同的UIWebView对象时,或者每当我有内容实例化一个新的漏洞时,会出现泄漏的事实,导致我认为必须有一些JavaScript文件被loadHTMLString处理,导致泄漏.有人知道如何解决这个问题吗?

  10. iOS应用程序的UI自动化测试如何与乐器和Javascript

    从WWDC2010视频会议中了解iOS应用程序的自动化UI测试,但没有实践.从代码项目project,我们可以有一个例子.这个问题在这里听到有涉及这个的人.任何限制?解决方法我建议从AlexWollmer开始使用thisblogpost.他创建了一个非常有用的JavaScript库:tuneup_jswithtest()函数,它允许测试分离和有用的帮助者以及为自动化仪器编写测试的断言.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部