每天一个npm包:define-properties

457 阅读2分钟

了解到这个库是在object-is中看到使用。顺便发现其文档事例的错误提了PR。

用于对对象一次性定义多个不可枚举属性。并且支持自定义predicate来决定是否对重名key进行重写。

可以看到默认情况下对于同名key是不会进行覆盖重写。

var test = { a: 1, b: 2 }
var test2 = {
	a: 10,
	b: 20,
	c: 30
}
defineProperties(test, test2);

console.log(Object.getOwnPropertyDescriptors(test))

// 
{
  a: { value: 1, writable: true, enumerable: true, configurable: true },
  b: { value: 2, writable: true, enumerable: true, configurable: true },
  c: { value: 30, writable: true, enumerable: false, configurable: true }
}

使用自定义predicate,predicate()为true则进行重写覆盖。

var test = { a: 1, b: 2 }
var test2 = {
	a: 10,
	b: 20,
	c: 30
}
defineProperties(test, test2,{
	a: function () { return false; },
	b: function () { return true; }
});

console.log(Object.getOwnPropertyDescriptors(test))
// 
{
  a: { value: 1, writable: true, enumerable: true, configurable: true },
  b: { value: 2, writable: true, enumerable: true, configurable: true },
  c: { value: 30, writable: true, enumerable: false, configurable: true }
}

源码

'use strict';

// Object.keys的垫片
var keys = require('object-keys');
// 判断是否支持Symbol
var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol';

// 缓存、快捷定义
var toStr = Object.prototype.toString;
var concat = Array.prototype.concat;
var origDefineProperty = Object.defineProperty;

// 判断是否是函数
var isFunction = function (fn) {
	return typeof fn === 'function' && toStr.call(fn) === '[object Function]';
};

// 判罚是否支持属性描述符
// 很多时候我们使用嗅探法判罚API支持与否 使用 try-catch
var arePropertyDescriptorsSupported = function () {
	var obj = {};
	try {
		// 定义为不可枚举、并且value为自身
		origDefineProperty(obj, 'x', { enumerable: false, value: obj });
		// eslint-disable-next-line no-restricted-syntax, no-unreachable-loop
		for (var _ in obj) {
			// 如果可以被for-in遍历 说明origDefineProperty失效、不支持
			return false;
		}
		// 判断
		return obj.x === obj;
	} catch (e) { /* this is IE 8. */
		// fallback
		return false;
	}
};

// 是否支持对象属性描述符
var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported();

// 通过Object.defineProperty方式定义对象属性描述符
var defineProperty = function (object, name, value, predicate) {
	// 如果存在同名key,默认不覆盖、或者predicate()返回false不覆盖
	if (name in object && (!isFunction(predicate) || !predicate())) {
		return;
	}
	// 如果支持属性描述符
	if (supportsDescriptors) {
		// 定义
		origDefineProperty(object, name, {
			configurable: true,
			enumerable: false,
			value: value,
			writable: true
		});
	} else {
		// 优雅降级
		object[name] = value; // eslint-disable-line no-param-reassign
	}
};

var defineProperties = function (object, map) {
	// 获取自定义的predicates对象
	var predicates = arguments.length > 2 ? arguments[2] : {};
	// 类似 Object.keys(map)
	var props = keys(map);
	// 如果支持Symbol、需要通过Object.getOwnPropertySymbols处理Symbol属性
	// 因为 Object.getOwnPropertyNames()获取对象所有自身属性的属性名数组,
	// 但是它不会包括:不可枚举属性、Symbol值作为名称的属性
	if (hasSymbols) {
		props = concat.call(props, Object.getOwnPropertySymbols(map));
	}
	for (var i = 0; i < props.length; i += 1) {
		// 遍历处理。
		// 因此可见 predicates 是有格式要求的、因此你可以理解defineProperty中对predicates有类型判断
		defineProperty(object, props[i], map[props[i]], predicates[props[i]]);
	}
};

defineProperties.supportsDescriptors = !!supportsDescriptors;

module.exports = defineProperties;

好了,今天就到这结束了,下期见。

ps:如果你对node也有兴趣,欢迎关注我公众号:xyz编程日记。