源码共读第一天,工具函数--omit.js

153 阅读2分钟

源码共读第一天,工具函数--omit.js

本文参加了由公众号@若川视野 发起的每周源码共读活动,   点击了解详情一起参与。 这是源码共读的第36期,链接:juejin.cn/post/711878…

Omit 简介

非常简单的一个库,一句话即可概括:
Utility function to create a shallow copy of an object which had dropped some fields. 直译过来:一个实用的工具函数,用于创建某些被删除了字段的对象的浅拷贝

源码解析

定义一个工具函数,入参有两个

obj:为需要删除属性的原对象
fields:数组形式,元素为被删除的字段

function omit(obj, fields) {
  // eslint-disable-next-line prefer-object-spread
  const shallowCopy = Object.assign({}, obj);
  for (let i = 0; i < fields.length; i += 1) {
    const key = fields[i];
    delete shallowCopy[key];
  }
  return shallowCopy;
}

思考

此处有一个疑问使用 Object.assign()方法,如果针对的是一级对象,进行的是深拷贝。 但是如果是多级对象,多级对象本身其实是个浅拷贝,这里称呼为shallowCopy是否恰当?此外是否应该对多级对象进行递归的深拷贝?恳请大佬解答~

let test = {name:"xiaowang",age:18,sex:"female",family:{father:"lisi",brother:"liwu"}}

let res = omit(test,[])

console.log(test) //{
  name: 'xiaowang',
  age: 18,
  sex: 'female',
  family: { father: 'lisi', brother: 'liwu' }
}
console.log(res) // {
  name: 'xiaowang',
  age: 18,
  sex: 'female',
  family: { father: 'lisi', brother: 'liwu' }
}
console.log(res===test) //false

res.family.father = "wangliu"

console.log(test) //{
  name: 'xiaowang',
  age: 18,
  sex: 'female',
  family: { father: 'wangliu', brother: 'liwu' }
}
console.log(res) // {
  name: 'laowang',
  age: 18,
  sex: 'female',
  family: { father: 'wangliu', brother: 'liwu' }
}
console.log(res===test) //false

单元测试

一个良好的工具函数需要单测保证质量,omit.js使用的是node的内置断言库assert,有strict和legacy两种模式,建议只使用strict模式。

import assert from 'assert';
import omit from '../src/index.js';

describe('omit', () => {
  it('should create a shallow copy', () => {
    const benjy = { name: 'Benjy' };
    const copy = omit(benjy, []);
    assert.deepEqual(copy, benjy);
    assert.notEqual(copy, benjy);
  });

  it('should drop fields which are passed in', () => {
    const benjy = { name: 'Benjy', age: 18 };
    assert.deepEqual(omit(benjy, ['age']), { name: 'Benjy' });
    assert.deepEqual(omit(benjy, ['name', 'age']), {});
  });
});

assert部分源码

这里顺便看下使用到的断言函数的部分源码

  • assert.strictEqual(actual, expected[, message]) strictEqual是严格等于,除了比较值或者对值得引用外,还要比较类型。
// The strict equality assertion tests strict equality, as determined by ===.
// assert.strictEqual(actual, expected, message_opt);
// 这个是严格相等, ===, 如果不严格相等,抛出异常
assert.strictEqual = function strictEqual(actual, expected, message) {
  if (actual !== expected) {
    fail(actual, expected, message, '===', assert.strictEqual);
  }
};
  • assert.notEqual(actual, expected[, message]) 直接测试两个值不相等
// The non-equality assertion tests for whether two objects are not equal
// with != assert.notEqual(actual, expected, message_opt);
// 直接测试不相等,如果相等 == ,抛出异常
assert.notEqual = function notEqual(actual, expected, message) {
  if (actual == expected) {
    fail(actual, expected, message, '!=', assert.notEqual);
  }
};
  • fail函数
// 对AssertionError的进一步封装, 直接抛出AssertionError异常,
// 注意: 如果有异常消息,直接显示异常消息,如果没有,直接显示实际值,期望值和操作符组成的字符串,具体实现在getMessage
function fail(actual, expected, message, operator, stackStartFunction) {
  throw new assert.AssertionError({
    message: message,
    actual: actual,
    expected: expected,
    operator: operator,
    stackStartFunction: stackStartFunction
  });

总结

  • omit.js作为一个开源库,不能仅限于核心代码,测试、打包发布等都要注意,对于项目工程化也有一定的借鉴意义

  • 对单元测试有了进一步的了解,同时学习assert的部分源码,可以借鉴其封装思路