源码共读-omit.js 剔除对象中的属性

240 阅读4分钟

前言

开始

1. Omit.js简介

删除一个对象的某些字段属性

2. 怎么使用

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

3. 源码分析

3.1 解读package.json

image.png

  • package.json描述了该包的一些基本信息、导出入口文件、依赖项等
  • "main"字段指定了当使用require()函数加载模块时,Node.js应该加载哪个文件
  • "module"字段是ES6模块规范的引用,它指定了当使用ES6模块导入语法时(如import时),应该加载哪个文件
3.2 源码解读
function omit(obj, fields) {
  // eslint-disable-next-line prefer-object-spread
  // 创建了一个浅拷贝对象shallowCopy,避免修改原对象
  const shallowCopy = Object.assign({}, obj);
  // 遍历需要删除的字段
  for (let i = 0; i < fields.length; i += 1) {
    const key = fields[i];
    delete shallowCopy[key];
  }
  // 返回处理后的对象
  return shallowCopy;
}

3.2.1 Object.assign方法解读

MDN是这样描述的:Object.assign()  静态方法将一个或者多个源对象中所有可枚举自有属性复制到目标对象,并返回修改后的目标对象

  1. 可枚举的属性, js对象枚举的意义在于通过 for-in 循环或 Object.keys()、Object.values()、Object.entries() 方法可以遍历访问到的属性
  2. 自有属性,该对象自身存在的属性,不是继承父类或者不存在的属性
  3. 注意Object.assign(target, source) === target

image.png

3.3 单元测试
import assert from 'assert';
import omit from '../src';

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

    // benjy和copy深度相等
    assert.deepEqual(copy, benjy);
    // benjy和copy不相等,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']), {});
  });
});

3.3.1 assert.notEqual源码解析

// 6. 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);
  }
};

源码是通过==来判断2个值是否相等 3.3.2 assert.deepEqual源码解析

  1. 找到源码位置, 发现该函数调用了_deepEqual函数
assert.deepEqual = function deepEqual(actual, expected, message) {
  if (!_deepEqual(actual, expected, false)) {
    fail(actual, expected, message, 'deepEqual', assert.deepEqual);
  }
};

2. 阅读_deepEqual函数代码,它做了很多类型判断,最后有个objEquiv方法方法,猜测是上文描述的一般对象比较{ name: 'Benjy' },继续阅读objEquiv方法

// 该方法做了很多类型的判断
function _deepEqual(actual, expected, strict, memos) {
  // 7.1. All identical values are equivalent, as determined by ===.
  if (actual === expected) {
    return true;
  } else if (isBuffer(actual) && isBuffer(expected)) {
    return compare(actual, expected) === 0;

  // 7.2. If the expected value is a Date object, the actual value is
  // equivalent if it is also a Date object that refers to the same time.
  } else if (util.isDate(actual) && util.isDate(expected)) {
    return actual.getTime() === expected.getTime();

  // 7.3 If the expected value is a RegExp object, the actual value is
  // equivalent if it is also a RegExp object with the same source and
  // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
  } else if (util.isRegExp(actual) && util.isRegExp(expected)) {
    return actual.source === expected.source &&
           actual.global === expected.global &&
           actual.multiline === expected.multiline &&
           actual.lastIndex === expected.lastIndex &&
           actual.ignoreCase === expected.ignoreCase;

  // 7.4. Other pairs that do not both pass typeof value == 'object',
  // equivalence is determined by ==.
  } else if ((actual === null || typeof actual !== 'object') &&
             (expected === null || typeof expected !== 'object')) {
    return strict ? actual === expected : actual == expected;

  // If both values are instances of typed arrays, wrap their underlying
  // ArrayBuffers in a Buffer each to increase performance
  // This optimization requires the arrays to have the same type as checked by
  // Object.prototype.toString (aka pToString). Never perform binary
  // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their
  // bit patterns are not identical.
  } else if (isView(actual) && isView(expected) &&
             pToString(actual) === pToString(expected) &&
             !(actual instanceof Float32Array ||
               actual instanceof Float64Array)) {
    return compare(new Uint8Array(actual.buffer),
                   new Uint8Array(expected.buffer)) === 0;

  // 7.5 For all other Object pairs, including Array objects, equivalence is
  // determined by having the same number of owned properties (as verified
  // with Object.prototype.hasOwnProperty.call), the same set of keys
  // (although not necessarily the same order), equivalent values for every
  // corresponding key, and an identical 'prototype' property. Note: this
  // accounts for both named and indexed properties on Arrays.
  } else if (isBuffer(actual) !== isBuffer(expected)) {
    return false;
  } else {
    memos = memos || {actual: [], expected: []};

    var actualIndex = memos.actual.indexOf(actual);
    if (actualIndex !== -1) {
      if (actualIndex === memos.expected.indexOf(expected)) {
        return true;
      }
    }

    memos.actual.push(actual);
    memos.expected.push(expected);

    return objEquiv(actual, expected, strict, memos);
  }
}

3. 在该方法中, 它将对象的key值通过objectKeys取出来存在数组中,再调用sort方法排序,便利所有的key值,再调用原始的_deepEqual来一一比较Obj[key]的值是否相关,终于解惑deepEqual的判断啦~

function objEquiv(a, b, strict, actualVisitedObjects) {
  if (a === null || a === undefined || b === null || b === undefined)
    return false;
  // if one is a primitive, the other must be same
  if (util.isPrimitive(a) || util.isPrimitive(b))
    return a === b;
  if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
    return false;
  var aIsArgs = isArguments(a);
  var bIsArgs = isArguments(b);
  if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
    return false;
  if (aIsArgs) {
    a = pSlice.call(a);
    b = pSlice.call(b);
    return _deepEqual(a, b, strict);
  }
  var ka = objectKeys(a);
  var kb = objectKeys(b);
  var key, i;
  // having the same number of owned properties (keys incorporates
  // hasOwnProperty)
  if (ka.length !== kb.length)
    return false;
  //the same set of keys (although not necessarily the same order),
  ka.sort();
  kb.sort();
  //~~~cheap key test
  for (i = ka.length - 1; i >= 0; i--) {
    if (ka[i] !== kb[i])
      return false;
  }
  //equivalent values for every corresponding key, and
  //~~~possibly expensive deep test
  for (i = ka.length - 1; i >= 0; i--) {
    key = ka[i];
    if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects))
      return false;
  }
  return true;
}

4. 总结感悟

认真的把文章写出来感觉真滴很有收获和满足感,之前懈怠而一直没有去做这一件事情,其实内心是想做的,拖延多了就习惯找理由,然后就可能永远也不会去做,自信之心就会慢慢枯萎。 加油~