Lodash 源码阅读-defineProperty
概述
defineProperty 是一个内部工具函数,用于安全地获取和使用 Object.defineProperty 方法。它通过尝试获取并测试原生 defineProperty 方法来确保其可用性,如果不可用则返回 undefined。这个函数在 Lodash 中主要用于定义对象属性的特性。
前置学习
依赖函数
- getNative:获取对象的原生方法
技术知识
- Object.defineProperty:定义对象属性的方法
- 属性描述符:对象属性的配置选项
- 错误处理:try-catch 的使用
- 立即执行函数:IIFE 的使用
源码实现
var defineProperty = (function () {
try {
var func = getNative(Object, "defineProperty");
func({}, "", {});
return func;
} catch (e) {}
})();
实现思路
defineProperty 函数的主要目的是安全地获取和使用 Object.defineProperty 方法。它通过以下步骤实现:
- 使用
getNative获取原生的Object.defineProperty方法 - 尝试使用该方法定义一个空对象的属性
- 如果成功则返回该方法,如果失败则返回 undefined
这种实现方式确保了返回的是可用的 defineProperty 方法,避免了在不支持的环境中出错。
源码解析
实现细节:
var defineProperty = (function () {
try {
var func = getNative(Object, "defineProperty");
func({}, "", {});
return func;
} catch (e) {}
})();
- 使用立即执行函数(IIFE)创建闭包
- 使用
getNative获取原生的defineProperty方法 - 尝试使用该方法定义一个空对象的属性
- 如果成功则返回该方法,如果失败则返回 undefined
- 使用 try-catch 处理可能的错误
应用场景
- 定义不可枚举属性:
// 定义不可枚举的属性
const obj = {};
if (defineProperty) {
defineProperty(obj, "hidden", {
value: "secret",
enumerable: false,
});
}
console.log(Object.keys(obj)); // []
console.log(obj.hidden); // 'secret'
- 定义只读属性:
// 定义只读属性
const config = {};
if (defineProperty) {
defineProperty(config, "apiKey", {
value: "12345",
writable: false,
});
}
config.apiKey = "67890"; // 静默失败
console.log(config.apiKey); // '12345'
- 定义 getter/setter:
// 定义 getter/setter
const person = {};
if (defineProperty) {
let _age = 0;
defineProperty(person, "age", {
get: function () {
return _age;
},
set: function (value) {
_age = value > 0 ? value : 0;
},
});
}
person.age = 25;
console.log(person.age); // 25
person.age = -10;
console.log(person.age); // 0
- 兼容性处理:
// 处理不支持 defineProperty 的环境
function definePropertySafe(obj, key, descriptor) {
if (defineProperty) {
defineProperty(obj, key, descriptor);
} else {
obj[key] = descriptor.value;
}
}
总结
通过学习 defineProperty 函数,我们可以看到以下设计原则:
-
安全性:确保获取的是可用的
defineProperty方法。 -
健壮性:处理不支持
defineProperty的环境。 -
兼容性:提供回退机制处理不兼容的情况。
-
错误处理:使用 try-catch 优雅地处理可能的错误。
-
代码复用:将通用的属性定义逻辑抽象出来。