什么以单一职责
函数的唯一性就是函数的单一职责。
单一职责原则(SRP:Single responsibility principle)又称单一功能原则,面向对象五大原则之一
最初的出处是鲍勃大叔 于《敏捷软件开发:原则、模式与实践》一书(来自百度百科)
A class should have only one reason to change
单一职责原则,简答的说,就是函数或者方法只做一件事情。如在原生JS提供的API中:
- concat():只负责连接两个或更多的数组,并返回结果。不会涉及删除数组的操作
- toFixed(): toFixed 只把 Number 类型的值四舍五入为指定小数位数的数字。不会执行其它操作
- animate(): 只负责动画,不帮你添加dom,不能帮你触发事件
- css(): 只负责样式,而不能解析innerHtml
怎么做,举个栗子
基础
let students=[
{
id: 5,
name: '守候',
sex: '男',
age: '',
},
{
id: 2,
name: '浪迹天涯',
sex: '男',
age: ''
},
{
id: 5,
name: '守候',
sex: '',
age: ''
},
{
id: 3,
name: '鸿雁',
sex: '',
age: '20'
}
]
function handle(arr) {
// 数组去重
let _arr=[], _arrIds=[]
for(let i = 0; i < arr.length; i++){
if(_arrIds.indexOf(arr[i].id) === -1){
_arrIds.push(arr[i].id)
_arr.push(arr[i])
}
}
// 遍历替换
_arr.map(item => {
for(let key in item){
if(item[key] === ''){
item[key] = '--'
}
}
})
return _arr
}
console.log(handle(students))
复制代码
这个也是绝大多数人都这么写,但是这个时候数据如果不会出现重复的时候,需要去掉去重的操作的时候,你咋弄,删除掉函数中的数组去重的方法么?那下面遍历替换就需要重新写逻辑。耽误时间,浪费生命!
如果这个时候加需求了,需要商户删除掉age大于20岁的数据,你又在函数后面加上判断么?如果之后需要排序呢?需求越来越多,这个函数只会越来越长,又臭又长会便秘的!!!
如果这个时候在别的页面地方也需要同样的方法, 你怎么复用呢?接着改是么?那么你的工作效率怎么提高呢?
而如果遵循单一职责的话,从函数/方法的角度出发的话,整个函数就做了两件事:
- 数组去重
- 遍历替换
const handle = {
// 数组去重
removeRepeat(arr) {
let _arr = [], _arrIds = []
for(let i = 0; i < arr.length; i++) {
if(_arrIds.indexOf(arr[i].id) === -1){
_arrIds.push(arr[i].id)
_arr.push(arr[i])
}
}
return _arr
},
// 遍历替换
setInfo(arr) {
arr.map(item => {
for(let key in item) {
if (item[key] === '') {
item[key] = '--'
}
}
})
return arr
},
// 根据id排序
so rtForId(arr){
return arr.sort((item1, item2) => item1.id-item2.id)
}
}
// 去重
students = handle.removeRepeat(students)
// 设置信息
students = handle.setInfo(students)
console.log(students)
复制代码
如果这个时候需要链式操作,可以仿造JQ的写法来写。
let ec=(function () {
let handle=function (obj) {
this.obj=JSON.parse(JSON.stringify(obj))
}
handle.prototype={
/**
* @description 去重
*/
unique() {
//根据id数组去重
let _arr = [], _arrIds = []
for(let i = 0; i < this.obj.length; i++) {
if(_arrIds.indexOf(this.obj[i].id) === -1) {
_arrIds.push(this.obj[i].id)
_arr.push(this.obj[i])
}
}
this.obj = _arr
return this
},
/**
* @description 设置保密信息
*/
setInfo(){
this.obj.map(item => {
for(let key in item) {
if(item[key] === '') {
item[key] = '--'
}
}
})
return this
},
sortForId() {
this.obj.sort((item1,item2) => item1.id - item2.id)
return this
},
/**
* @description 返回处理结果
* @return {Array|*}
*/
end() {
return this.obj
}
}
// 暴露构造函数接口
return function (obj) {
return new handle(obj)
}
})()
// 根据id去重和设置'--'
students=ec(students).unique().setInfo().end();
console.log(students)
复制代码
这段代码都看不懂话,去重学JS吧。
扫描二维码关注公众号,回复: 13630787 查看本文章
关于实现链式调用,这个肯定是会增加代码的,如果调用的方法并不是一些常用,通用的方法的话,只是处理一些特殊格式的数据的方法(如上实例),不建议花费时间,实现链式调用,普通调用就好。如果是一些常用的函数的封装,就建议使用链式调用。
进阶
下面是w3c的实栗:
将商品添加到购物车,触发添加到购物车的操作直接是window.onload之后直接运行,所以下面用来匿名函数。
function Product(id, description) {
this.getId = function () {
return id;
};
this.getDescription = function () {
return description;
};
}
function Cart(eventAggregator) {
let items = [];
this.addItem = function (item) {
items.push(item);
};
}
(function () {
let products = [new Product(1, "Star Wars Lego Ship"),
new Product(2, "Barbie Doll"),
new Product(3, "Remote Control Airplane")],
cart = new Cart();
function addToCart() {
let productId = $(this).attr('id');
let product = $.grep(products, function (x) {
return x.getId() == productId;
})[0];
cart.addItem(product);
let newItem = $('<li></li>').html(product.getDescription()).attr('id-cart', product.getId()).appendTo("#cart")
}
products.forEach(function (product) {
let newItem = $('<li></li>').html(product.getDescription())
.attr('id', product.getId())
.dblclick(addToCart)
.appendTo("#products")
});
})();
复制代码
该代码声明了2个function分别用来描述product和cart,而匿名函数的职责是更新屏幕和用户交互,这还不是一个很复杂的例子,但匿名函数里却包含了很多不相关的职责,让我们来看看到底有多少职责:
首先,有product的集合的声明 其次,有一个将product集合绑定到#product元素的代码,而且还附件了一个添加到购物车的事件处理 第三,有Cart购物车的展示功能 第四,有添加product item到购物车并显示的功能
function Event(name) {
var handlers = [];
this.getName = function () {
return name;
};
this.addHandler = function (handler) {
handlers.push(handler);
};
this.removeHandler = function (handler) {
for (var i = 0; i < handlers.length; i++) {
if (handlers[i] == handler) {
handlers.splice(i, 1);
break;
}
}
};
this.fire = function (eventArgs) {
handlers.forEach(function (h) {
h(eventArgs);
});
};
}
function EventAggregator() {
var events = [];
function getEvent(eventName) {
return $.grep(events, function (event) {
return event.getName() === eventName;
})[0];
}
this.publish = function (eventName, eventArgs) {
var event = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(event);
}
event.fire(eventArgs);
};
this.subscribe = function (eventName, handler) {
var event = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(event);
}
event.addHandler(handler);
};
}
function Product(id, description) {
this.getId = function () {
return id;
};
this.getDescription = function () {
return description;
};
}
function Cart(eventAggregator) {
var items = [];
this.addItem = function (item) {
items.push(item);
eventAggregator.publish("itemAdded", item);
};
}
function CartController(cart, eventAggregator) {
eventAggregator.subscribe("itemAdded", function (eventArgs) {
var newItem = $('<li></li>').html(eventArgs.getDescription()).attr('id-cart', eventArgs.getId()).appendTo("#cart");
});
eventAggregator.subscribe("productSelected", function (eventArgs) {
cart.addItem(eventArgs.product);
});
}
function ProductRepository() {
var products = [new Product(1, "Star Wars Lego Ship"),
new Product(2, "Barbie Doll"),
new Product(3, "Remote Control Airplane")];
this.getProducts = function () {
return products;
}
}
function ProductController(eventAggregator, productRepository) {
var products = productRepository.getProducts();
function onProductSelected() {
var productId = $(this).attr('id');
var product = $.grep(products, function (x) {
return x.getId() == productId;
})[0];
eventAggregator.publish("productSelected", {
product: product
});
}
products.forEach(function (product) {
var newItem = $('<li></li>').html(product.getDescription())
.attr('id', product.getId())
.dblclick(onProductSelected)
.appendTo("#products");
});
}
(function () {
var eventAggregator = new EventAggregator(),
cart = new Cart(eventAggregator),
cartController = new CartController(cart, eventAggregator),
productRepository = new ProductRepository(),
productController = new ProductController(eventAggregator, productRepository);
})();
复制代码
完整的内容可以上w3c去看。
为什么要遵循单一职责
其实可以不遵循的。因为在项目中,比如React的开发当中,一个功能很少会被复用到,所以为了减少代码的复杂程度,是可以违背单一职责的。虽然在维护上面会增加难度,但是在使用当中,其他组员可以不用关心内部的实现,直接进行调用。
如果项目非常的小,而公司没有内部通用的方法库或者组件库的话,可以不用拆分和精炼到这种程度。如果预感到后期需求会加得很多的话,那么在前期就应该对项目进行重构了。