Lodash源码阅读-defineProperty

104 阅读2分钟

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 方法。它通过以下步骤实现:

  1. 使用 getNative 获取原生的 Object.defineProperty 方法
  2. 尝试使用该方法定义一个空对象的属性
  3. 如果成功则返回该方法,如果失败则返回 undefined

这种实现方式确保了返回的是可用的 defineProperty 方法,避免了在不支持的环境中出错。

源码解析

实现细节:

var defineProperty = (function () {
  try {
    var func = getNative(Object, "defineProperty");
    func({}, "", {});
    return func;
  } catch (e) {}
})();
  • 使用立即执行函数(IIFE)创建闭包
  • 使用 getNative 获取原生的 defineProperty 方法
  • 尝试使用该方法定义一个空对象的属性
  • 如果成功则返回该方法,如果失败则返回 undefined
  • 使用 try-catch 处理可能的错误

应用场景

  1. 定义不可枚举属性
// 定义不可枚举的属性
const obj = {};
if (defineProperty) {
  defineProperty(obj, "hidden", {
    value: "secret",
    enumerable: false,
  });
}
console.log(Object.keys(obj)); // []
console.log(obj.hidden); // 'secret'
  1. 定义只读属性
// 定义只读属性
const config = {};
if (defineProperty) {
  defineProperty(config, "apiKey", {
    value: "12345",
    writable: false,
  });
}
config.apiKey = "67890"; // 静默失败
console.log(config.apiKey); // '12345'
  1. 定义 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
  1. 兼容性处理
// 处理不支持 defineProperty 的环境
function definePropertySafe(obj, key, descriptor) {
  if (defineProperty) {
    defineProperty(obj, key, descriptor);
  } else {
    obj[key] = descriptor.value;
  }
}

总结

通过学习 defineProperty 函数,我们可以看到以下设计原则:

  1. 安全性:确保获取的是可用的 defineProperty 方法。

  2. 健壮性:处理不支持 defineProperty 的环境。

  3. 兼容性:提供回退机制处理不兼容的情况。

  4. 错误处理:使用 try-catch 优雅地处理可能的错误。

  5. 代码复用:将通用的属性定义逻辑抽象出来。