菜?🥬就多练! - 每天思考一个NPM库(Day 3)

131 阅读3分钟

这是「🥬狗变形记」系列更新的第三天,今天给大家介绍一下让一个函数“模仿”另一个函数的NPM库 - 「mimic-fn」

NPM:www.npmjs.com/package/mim…

先来看一看文档里的介绍:

mimic-fn能够让一个函数具备另一函数所用的名字和其他属性

看到简介,是不是觉得有点疑惑,函数的name我们都知道,那其他属性是包括哪些呢?🤔️以及mimic-fn主要做了什么呢? 在这里,我就直接给大家揭晓答案:

  • 复制通过Reflect.ownKeys()获取到的属性

上图就是最简单函数得到的结果,其实实际场景中还可能存在其他自定义的一些属性

  • 对toString方法进行了额外处理
  • 改变了函数的原型指向

结合上面说到的mimic-fn做的事情,我们还是老规矩,先看下整体执行流程:

结合图片看源码里面每一步是怎么实现的:

整体流程:

function mimicFunction(to, from, {ignoreNonConfigurable = false} = {}) {
	const {name} = to;

  // 1. 处理属性
	for (const property of Reflect.ownKeys(from)) {
		copyProperty(to, from, property, ignoreNonConfigurable);
	}

  // 2. 改变原型指向
	changePrototype(to, from);
  // 3. 改变toString
	changeToString(to, from, name);

	return to;
}

其中每一步:

  1. 对Reflect.ownKeys获取到的属性处理:
const copyProperty = (to, from, property, ignoreNonConfigurable) => {
  // 边界
	if (property === 'length' || property === 'prototype') {
		return;
	}

  // 边界
	if (property === 'arguments' || property === 'caller') {
		return;
	}

	const toDescriptor = Object.getOwnPropertyDescriptor(to, property);
	const fromDescriptor = Object.getOwnPropertyDescriptor(from, property);

  // 其实就是获取了属性描述符,做了一些判断
  if (!canCopyProperty(toDescriptor, fromDescriptor) && ignoreNonConfigurable) {
		return;
	}

	Object.defineProperty(to, property, fromDescriptor);
};
  1. 改变原型指向
Object.setPrototypeOf(to, fromPrototype);
  1. 改变toString
const changeToString = (to, from, name) => {
  // with注释
	const withName = name === '' ? '' : `with ${name.trim()}() `;
	const newToString = wrappedToString.bind(null, withName, from.toString());

	Object.defineProperty(newToString, 'name', toStringName);
  // 设置to函数的toString属性
	Object.defineProperty(to, 'toString', {...toStringDescriptor, value: newToString});
};

这里实际上就是用with包括了被模仿函数的toString执行后的结果,加上一行with注释

🤔️:这里我其实是对改变toString的作用不明确的,按理一个函数复制另外一个函数的属性,应该实际执行逻辑是不同,那toString的结果其实是函数的执行逻辑,这里有明白的小伙伴可以告诉我...

那我学到了什么呢?

  1. 关于Object对象上的各种用法:
  • Object.keys:返回一个由一个给定对象的自身可枚举属性组成的数组(不包括Symbol)
  • Object.getOwnPropertyNames() 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组
  • Object.getOwnPropertySymbols 返回一个给定对象自身的所有 Symbol 属性的数组
  • Reflect.ownKeys:返回一个由目标对象自身的属性键组成的数组。它的返回值等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
  1. 属性描述符
  • 数据属性描述符:value、writable
  • 访问器/设置器属性描述符:get、set
  • configurable:指定对象的属性描述可以被改变或者属性可被删除
  • enumerable 可枚举

一些思考:

其实阅读源码的初衷就是为了“观看”分析别人写代码时候的考虑,选择,运用基础知识的能力,虽然mimic-func涉及到的知识都比较基础,但其中很多边界情况的判断,以及对基础知识的运用,也让自己更加明白:细节决定成败。⛽️GET UP!

🌊总结:

💪持续阅读好的代码库,思考学习好的代码,把自己的成长分享出来。

😊如果有一点点对你学习、工作、思考的帮助,我也会觉得很开心~