omit.js 源码浅析

190 阅读3分钟

omit.js 源码浅析

源码地址:omit.js

什么是omit

看到这个名字的我就想到ts中的Omit,再一看omit.js的描述: 创建删除某些字段的对象的浅拷贝副本

ts中的Omit同样是去掉已声明类型中的某些属性;

打开项目的index.d.ts文件一看,果然也是如此;

declare function Omit<T, K extends keyof T>(
  obj: T,
  keys: Array<K>
): Omit<T, K>;

export default Omit;
  • 使用示例

omit.js

var omit = require('omit.js');
omit({ name: 'Benjy', age: 18 }, [ 'name' ]); // => { age: 18 }

typescript内的Omit

type IPerson = {
  name: string,
  age: number, 
  test: string
}
type testType = Omit<IPerson, 'name' | 'age'>

const person: IPerson = {
  name: '张三',
  age: 18,
  test: '遵纪守法是对人的最低道德要求'
}

const errorEg: testType = {
 test: '遵纪守法是对人的最低道德要求',
  // age在此处会报错ts错误
  // Type '{ test: string; age: number; }' is not assignable to type 'testType'. Object literal may only specify known properties, and 'age' does not exist in type 'testType'.(2322)
 age: 18,
}

项目配置

打开本项目的package.json文件看看都引入了什么好东西

{
 // ...
  "scripts": {
    "start": "father doc dev --storybook",
    "build": "father doc build --storybook",
    "compile": "father build",
    "gh-pages": "father doc deploy",
    "prepublishOnly": "npm run compile && np --yolo --no-publish",
    "lint": "eslint .",
    "test": "father test",
    "coverage": "father test --coverage"
  },
 // ...
  "devDependencies": {
    "@umijs/fabric": "^2.2.2",
    "assert": "^1.4.1",
    "eslint": "^7.4.0",
    "father": "^2.29.5",
    "np": "^6.3.1",
    "rc-tools": "^6.3.3"
  }
}
  • @umijs/fabric: 集成各种lint工具

    • 使用官方示例
    // 也算方便了使用方,不用各种重新配置,可以借鉴下用在公司里面
     module.exports = {
       extends: [require.resolve('@umijs/fabric/dist/eslint')],
     
       // in antd-design-pro
       globals: {
         ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
         page: true,
       },
     
       rules: {
         // your rules
       },
     };
    
  • father:集合各种打包姿势的库,看着还集合了测试框架(名字起的很有特点)

  • np:A better npm publish,这东西不错

  • rc-tools:应该也是打包相关的,看commit貌似被father换掉了

核心代码

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返回一个拷贝的对象,并循环fields对象并把obj对应的keydelete 删掉,因为delete对象属性时无论成功与否都不会报错,只会返回true OR false(当对象被freeze后是无法删除成功的)

复习下Object.assign

Object.assign() 方法将所有可枚举Object.propertyIsEnumerable() 返回 true)的自有Object.hasOwnProperty() 返回 true)属性从一个或多个源对象复制到目标对象(eg: Object.assign({}, obj1, obj2)),返回修改后的对象。假如源对象是一个对象的引用,它仅仅会复制其引用值。

测试用例

assert这个库平时用的比较少,查了资料瞄了下

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

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

deepEqual: 深入对比两个对象自身可枚举的属性值,只要key:value匹配即可

notEqual: 浅层比较对象,类似 ==

  • 用例1 ;

    assert.deepEqual(copy, benjy) // => name: 'Benjy' == name: 'Benjy'
    
  • 用例2:copybenjy是两个对象,引用不同确实notEqual

  • 用例3:

    assert.deepEqual(omit(benjy, ['age']), { name: 'Benjy' });
    // omit(benjy, ['age']) => { name: 'Benjy' }
    // name: 'Benjy' == name: 'Benjy'
    
  • 用例4: 其实就是 {}{}deepEqual

实现一个简易的deepOmit方法

按照omit.js的逻辑 简单实现一个深度删除某些字段的deepOmit.js

const deepOmit = (data: any, keys: string[]) => {
  const newData: any = JSON.parse(JSON.stringify(data)) || {}
  const deepOmitFn = (obj: any) => {
    for(let key of keys) {
      delete obj[key]
    }
    Object.values(obj).forEach((value) => {
      if (value && typeof value === 'object') {
        deepOmitFn(value)
      }
    })
  }
  deepOmitFn(newData)
  return newData
}

// test
console.log(deepOmit({name: '222', age: 2, a: 'a', b: {name: 'b-name', age: 22}}, ['name'])) => // {age: 2, a: 'a', b: {age:22}}

unset-value

后续根据omit.js找到unset-value,实现功能更加高级了点,同样也能实现deep-omit功能,只是它只会在原对象上操作而不会返回一个新的对象,具体看示例

var unset = require('unset-value');
var obj = {a: 'b'};
unset(obj, 'a');
console.log(obj);
//=> {}

// delete nested values
var one = {a: {b: {c: 'd'}}};
unset(one, 'a.b');
console.log(one);
//=> {a: {}}

var two = {a: {b: {c: 'd'}}};
unset(two, ['a', 'b', 'c']);
console.log(two);
//=> {a: {b: {}}}

var three = {a: {b: {c: 'd', e: 'f'}}};
unset(three, 'a.b.c');
console.log(three);
//=> {a: {b: {e: 'f'}}}

具体请看github地址

总结

  • Object.assign的复习
  • 了解了assert的一般使用和某些方法的实现
  • 了解了father等库
  • 根据omit找到了另一个类似库unset-value