chunk: file.slice(cur, cur + size) }); cur += size; } return chunkList } function saveFileInfoInLocalStorage(file, chunkList) { localStorage.setItem(file, file.name); localStorage.setItem(chunkListLength, chunkList.length); }
* 自顶向下地书写函数,人们都是习惯自顶向下读代码,如,为了执行A,需要执行B,为了执行B,需要执行C。如果把A、B、C混在一个函数就很难读了。(看前一个的例子)。
* 不使用布尔值来作为参数,遇到这种情况时,一定可以拆分函数。
// bad
function createFile(name, temp) {
if (temp) {
fs.create(./temp/${name});
} else {
fs.create(name);
}
}
// good function createFile(name) { fs.create(name); }
function createTempFile(name) {
createFile(./temp/${name});
}
* 避免副作用。
+ 常见就是陷阱就是对象之间共享了状态,使用了可变的数据类型,比如对象和数组。对于可变的数据类型,使用**immutable**等库来高效克隆。
+ 避免用可变的全局变量。
+ 副作用的缺点:出现不可预期的异常,比如用户对购物车下单后,网络差而不断重试请求,这时如果添加新商品到购物车,就会导致新增的商品也会到下单的请求中。
+ 集中副作用:遇到不可避免的副作用时候,比如读写文件、上报日志,那就在一个地方集中处理副作用,不要在多个函数和类处理副作用。
+ 其它注意的地方:
// bad:注意到cart是引用类型! const addItemToCart = (cart, item) => { cart.push({ item, date: Date.now() }); }; // good const addItemToCart = (cart, item) => { return [...cart, { item, date: Date.now() }]; };
* 封装复杂的判断条件,提高可读性。
// bad if (!(obj => obj != null && typeof obj[Symbol.iterator] === 'function')) { throw new Error('params is not iterable') }
// good const isIterable = obj => obj != null && typeof obj[Symbol.iterator] === 'function'; if (!isIterable(promises)) { throw new Error('params is not iterable') }
* 在方法中有多条件判断时候,为了提高函数的可扩展性,考虑下是不是可以使用能否使用多态性来解决。
// 地图接口可能来自百度,也可能来自谷歌 const googleMap = { show: function (size) { console.log('开始渲染谷歌地图', size)); } };
const baiduMap = { render: function (size) { console.log('开始渲染百度地图', size)); } };
// bad: 出现多个条件分支。如果要加一个腾讯地图,就又要改动renderMap函数。 function renderMap(type) { const size = getSize(); if (type === 'google') { googleMap.show(size); } else if (type === 'baidu') { baiduMap.render(size); } }; renderMap('google')
// good:实现多态处理。如果要加一个腾讯地图,不需要改动renderMap函数。 // 细节:函数作为一等对象的语言中,作为参数传递也会返回不同的执行结果,也是“多态性”的体现。 function renderMap (renderMapFromApi) { const size = getSize(); renderMapFromApi(size); } renderMap((size) => googleMap.show(size));
其他
* 如果用了**TS**,没必要做多余类型判断。
### 注释
1. 一般代码要能清晰的表达意图,只有遇到复杂的逻辑时才注释。
// good:由于函数名已经解释不清楚函数的用途了,所以注释里说明。 // 在nums数组中找出 和为目标值 target 的两个整数,并返回它们的数组下标。 const twoSum = function(nums, target) { let map = new Map() for (let i = 0; i < nums.length; i++) { const item = nums[i]; const index = map.get(target - item) if (index !== undefined){ return [index, i] } map.set(item, i) } return [] };
// bad:加了一堆废话 const twoSum = function(nums, target) { // 声明map变量 let map = new Map() // 遍历 for (let i = 0; i < nums.length; i++) { const item = nums[i]; const index = map.get(target - item) // 如果下标为空 if (index !== undefined){ return [index, i] } map.set(item, i) } return [] };
1. 警示作用,解释此处不能修改的原因。
// hack: 由于XXX历史原因,只能调度一下。 setTimeout(doSomething, 0)
1. TODO注释,记录下应该做但还没做的工作。另一个好处,提前写好命名,可以帮助后来者统一命名风格。
class Comment { // todo: 删除功能后期实现 delete() {} }
1. 没用的代码直接删除,不要注释,反正**git**提交历史记录可以找回。
// bad: 如下,重写了一遍两数之和的实现方式 // const twoSum = function(nums, target) { // for(let i = 0;i<nums.length;i++){ // for(let j = i+1;j<nums.length;j++){ // if (nums[i] + nums[j] === target) { // return [i,j] // } // } // } // };
const twoSum = function(nums, target) { let map = new Map() for (let i = 0; i < nums.length; i++) { const item = nums[i]; const index = map.get(target - item) if (index !== undefined){ return [index, i] } map.set(item, i) } return [] };
1. 避免循规式注释,不要求每个函数都要求jsdoc,jsdoc一般是用在公共代码上。
// bad or good? /** * @param {number[]} nums * @param {number} target * @return {number[]} */ const twoSum = function(nums, target) {}
### 对象
* 多使用**getter**和**setter**(getXXX和setXXX)。好处:
+ 在**set**时方便验证。
+ 可以添加埋点,和错误处理。
+ 可以延时加载对象的属性。
// good function makeBankAccount() { let balance = 0; function getBalance() { return balance; } function setBalance(amount) { balance = amount; } return { getBalance, setBalance }; } const account = makeBankAccount(); account.setBalance(100);
* 使用私有成员。对外隐藏不必要的内容。
// bad const Employee = function(name) { this.name = name; }; Employee.prototype.getName = function getName() { return this.name; }; const employee = new Employee(John Doe); delete employee.name; console.log(employee.getName()); // undefined // good function makeEmployee(name) { return { getName() { return name; } }; }
### 类
**solid**
* **单一职责原则 (SRP)** - 保证“每次改动只有一个修改理由”。因为如果一个类中有太多功能并且您修改了其中的一部分,则很难预期改动对其他功能的影响。
// bad:设置操作和验证权限放在一起了 class UserSettings { constructor(user) { this.user = user; } changeSettings(settings) { if (this.verifyCredentials()) { // ... } } verifyCredentials() { // ... } } // good: 拆出验证权限的类 class UserAuth { constructor(user) { this.user = user; } verifyCredentials() { // ... } } class UserSettings { constructor(user) { this.user = user; this.auth = new UserAuth(user); } changeSettings(settings) { if (this.auth.verifyCredentials()) { // ... } } }
* **开闭原则 (OCP)** - 对扩展放开,但是对修改关闭。在不更改现有代码的情况下添加新功能。比如一个方法因为有switch的语句,每次出现新增条件时就要修改原来的方法。这时候不如换成多态的特性。
// bad: 注意到fetch用条件语句了,不利于扩展 class AjaxAdapter extends Adapter { constructor() { super(); this.name = ajaxAdapter; } } class NodeAdapter extends Adapter { constructor() { super(); this.name = nodeAdapter; } } class HttpRequester { constructor(adapter) { this.adapter = adapter; } fetch(url) { if (this.adapter.name === ajaxAdapter) { return makeAjaxCall(url).then(response => { // transform response and return }); } else if (this.adapter.name === nodeAdapter) { return makeHttpCall(url).then(response => { // transform response and return }); } } } function makeAjaxCall(url) { // request and return promise } function makeHttpCall(url) { // request and return promise } // good class AjaxAdapter extends Adapter { constructor() { super(); this.name = ajaxAdapter; } request(url) { // request and return promise } } class NodeAdapter extends Adapter { constructor() { super(); this.name = nodeAdapter; } request(url) { // request and return promise } } class HttpRequester { constructor(adapter) { this.adapter = adapter; } fetch(url) { return this.adapter.request(url).then(response => { // transform response and return }); } }
* **里氏替换原则 (LSP)**
+ 如果S是T的子类,则T的**对象**可以替换为S的对象,而不会破坏程序。
+ 所有引用其父类**对象方法**的地方,都可以透明的替换为其子类对象。
+ 也就是,保证任何父类对象出现的地方,用其子类的对象来替换,不会出错。下面的例子是经典的正方形、长方形例子。
+ 两个定义
// bad: 用正方形继承了长方形 class Rectangle { constructor() { this.width = 0; this.height = 0; } setColor(color) { // ... } render(area) { // ... } setWidth(width) { this.width = width; } setHeight(height) { this.height = height; } getArea() { return this.width * this.height; } } class Square extends Rectangle { setWidth(width) { this.width = width; this.height = width; } setHeight(height) { this.width = height; this.height = height; } } function renderLargeRectangles(rectangles) { rectangles.forEach(rectangle => { rectangle.setWidth(4); rectangle.setHeight(5); const area = rectangle.getArea(); // BAD: 返回了25,其实应该是20 rectangle.render(area); }); } const rectangles = [new Rectangle(), new Rectangle(), new Square()];// 这里替换了 renderLargeRectangles(rectangles); // good: 取消正方形和长方形继承关系,都继承Shape class Shape { setColor(color) { // ... } render(area) { // ... } } class Rectangle extends Shape { constructor(width, height) { super(); this.width = width; this.height = height; } getArea() { return this.width * this.height; } } class Square extends Shape { constructor(length) { super(); this.length = length; } getArea() { return this.length * this.length; } } function renderLargeShapes(shapes) { shapes.forEach(shape => { const area = shape.getArea(); shape.render(area); }); } const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; renderLargeShapes(shapes);
* **接口隔离原则 (ISP)** - 定义是客户不应被迫使用对其而言无用的方法或功能。常见的就是让一些参数变成可选的。
// bad class Dog { constructor(options) { this.options = options; } run() { this.options.run(); // 必须传入 run 方法,不然报错 } } const dog = new Dog({}); // Uncaught TypeError: this.options.run is not a function dog.run() // good class Dog { constructor(options) { this.options = options; } run() { if (this.options.run) { this.options.run(); return; } console.log('跑步'); } }
* **依赖倒置原则(DIP)** - 程序要依赖于抽象接口(可以理解为入参),不要依赖于具体实现。这样可以减少耦合度。
// bad class OldReporter { report(info) { // ... } } class Message { constructor(options) { // ... // BAD: 这里依赖了一个实例,那你以后要换一个,就麻烦了 this.reporter = new OldReporter(); } share() { this.reporter.report('start share'); // ... } } // good
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!