Window.Reflect 全面解析

Reflect 是 ES6 引入的一个内置对象,它提供了一系列用于拦截 JavaScript 操作的方法。这些方法与 Proxy 处理器方法一一对应,使得元编程更加方便和规范。

1. Reflect 的基本概念

为什么要引入 Reflect?

  • 统一操作方式:将一些 隐式操作 变为 显式方法 调用
  • 标准化元编程:为 Proxy 提供对应的默认行为
  • 更好的错误处理:方法返回布尔值而非抛出错误
  • 函数式编程支持:所有操作都是函数调用

基本特性

console.log(typeof Reflect); // "object"
console.log(Reflect.toString()); // "[object Reflect]"

// Reflect 不可扩展、不可配置、不可写
console.log(Object.isExtensible(Reflect)); // false

2. Reflect 的静态方法

2.1 对象基本操作

Reflect.get() - 获取属性值

const obj = { x: 1, y: 2 };

// 传统方式
console.log(obj.x); // 1

// Reflect 方式
console.log(Reflect.get(obj, 'x')); // 1

// 支持 getter
const objWithGetter = {
  _value: 42,
  get value() {
    return this._value;
  }
};
console.log(Reflect.get(objWithGetter, 'value')); // 42

// 指定 receiver(this 值)
const receiver = { _value: 100 };
console.log(Reflect.get(objWithGetter, 'value', receiver)); // 100

Reflect.set() - 设置属性值

const obj = { x: 1 };

// 传统方式
obj.x = 2;

// Reflect 方式
Reflect.set(obj, 'x', 3);
console.log(obj.x); // 3

// 设置新属性
Reflect.set(obj, 'y', 10);
console.log(obj.y); // 10

// 设置 setter
const objWithSetter = {
  _value: 0,
  set value(val) {
    this._value = val * 2;
  },
  get value() {
    return this._value;
  }
};

Reflect.set(objWithSetter, 'value', 5);
console.log(objWithSetter.value); // 10

// 返回布尔值表示是否设置成功
const frozenObj = Object.freeze({ x: 1 });
console.log(Reflect.set(frozenObj, 'x', 2)); // false

Reflect.has() - 检查属性存在性

const obj = { x: 1, [Symbol('secret')]: 2 };

console.log(Reflect.has(obj, 'x')); // true
console.log(Reflect.has(obj, 'y')); // false
console.log(Reflect.has(obj, Symbol('secret'))); // true

// 等同于
console.log('x' in obj); // true

Reflect.deleteProperty() - 删除属性

const obj = { x: 1, y: 2, z: 3 };

// 传统方式
delete obj.x;

// Reflect 方式
Reflect.deleteProperty(obj, 'y');

console.log(obj); // { z: 3 }

// 返回布尔值表示是否删除成功
const sealedObj = Object.seal({ x: 1 });
console.log(Reflect.deleteProperty(sealedObj, 'x')); // false

2.2 对象扩展和属性描述

Reflect.defineProperty() - 定义属性

const obj = {};

// 传统方式
Object.defineProperty(obj, 'x', {
  value: 1,
  writable: false,
  enumerable: true
});

// Reflect 方式
const success = Reflect.defineProperty(obj, 'y', {
  value: 2,
  writable: true,
  enumerable: true
});

console.log(success); // true
console.log(obj); // { x: 1, y: 2 }

// 失败案例
const frozenObj = Object.freeze({});
console.log(Reflect.defineProperty(frozenObj, 'x', { value: 1 })); // false

Reflect.getOwnPropertyDescriptor() - 获取属性描述符

const obj = {};
Object.defineProperty(obj, 'x', {
  value: 1,
  writable: false,
  enumerable: true
});

const descriptor = Reflect.getOwnPropertyDescriptor(obj, 'x');
console.log(descriptor);
// {
//   value: 1,
//   writable: false,
//   enumerable: true,
//   configurable: false
// }

Reflect.isExtensible() - 检查可扩展性

const obj = { x: 1 };
const sealedObj = Object.seal({});
const frozenObj = Object.freeze({});

console.log(Reflect.isExtensible(obj)); // true
console.log(Reflect.isExtensible(sealedObj)); // false
console.log(Reflect.isExtensible(frozenObj)); // false

Reflect.preventExtensions() - 阻止扩展

const obj = { x: 1 };

console.log(Reflect.isExtensible(obj)); // true
Reflect.preventExtensions(obj);
console.log(Reflect.isExtensible(obj)); // false

obj.y = 2; // 静默失败(严格模式会报错)
console.log(obj); // { x: 1 }

2.3 原型操作

Reflect.getPrototypeOf() - 获取原型

class Animal {}
class Dog extends Animal {}

const dog = new Dog();

console.log(Reflect.getPrototypeOf(dog) === Dog.prototype); // true
console.log(Reflect.getPrototypeOf(Dog.prototype) === Animal.prototype); // true

// 等同于
console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true

Reflect.setPrototypeOf() - 设置原型

const obj = { x: 1 };
const prototype = { y: 2 };

const success = Reflect.setPrototypeOf(obj, prototype);
console.log(success); // true
console.log(Reflect.getPrototypeOf(obj) === prototype); // true
console.log(obj.y); // 2(通过原型链访问)

// 失败案例
const frozenObj = Object.freeze({});
console.log(Reflect.setPrototypeOf(frozenObj, {})); // false

2.4 函数操作

Reflect.apply() - 调用函数

function greet(name, punctuation) {
  return `Hello, ${name}${punctuation}`;
}

// 传统方式
console.log(greet.call(null, 'Alice', '!')); // "Hello, Alice!"

// Reflect 方式
console.log(Reflect.apply(greet, null, ['Alice', '!'])); // "Hello, Alice!"

// 更复杂的例子
const numbers = [1, 2, 3, 4, 5];
const max = Reflect.apply(Math.max, null, numbers);
console.log(max); // 5

// 绑定 this
const person = {
  name: 'Alice',
  greet() {
    return `Hello, I'm ${this.name}`;
  }
};

const anotherPerson = { name: 'Bob' };
console.log(Reflect.apply(person.greet, anotherPerson, [])); // "Hello, I'm Bob"

Reflect.construct() - 构造函数

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  introduce() {
    return `I'm ${this.name}, ${this.age} years old`;
  }
}

// 传统方式
const person1 = new Person('Alice', 25);

// Reflect 方式
const person2 = Reflect.construct(Person, ['Bob', 30]);

console.log(person2.introduce()); // "I'm Bob, 30 years old"

// 使用不同的原型
function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function() {
  return `${this.name} makes a sound`;
};

function Dog(name) {
  Animal.call(this, name);
}

const dog = Reflect.construct(Animal, ['Buddy'], Dog);
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // false
console.log(dog.speak()); // "Buddy makes a sound"

2.5 属性枚举

Reflect.ownKeys() - 获取所有自有键

const obj = {
  x: 1,
  y: 2,
  [Symbol('z')]: 3
};

Object.defineProperty(obj, 'hidden', {
  value: 'secret',
  enumerable: false
});

console.log(Reflect.ownKeys(obj));
// ['x', 'y', 'hidden', Symbol(z)]

// 对比其他方法
console.log(Object.keys(obj)); // ['x', 'y'] (仅可枚举的字符串键)
console.log(Object.getOwnPropertyNames(obj)); // ['x', 'y', 'hidden'] (所有字符串键)
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(z)] (所有 Symbol 键)

3. Reflect 与 Proxy 的配合使用

3.1 基本代理模式

const target = {
  name: 'Alice',
  age: 25
};

const handler = {
  get(target, property, receiver) {
    console.log(`Getting property: ${property}`);
    return Reflect.get(target, property, receiver);
  },

  set(target, property, value, receiver) {
    console.log(`Setting property: ${property} to ${value}`);
    return Reflect.set(target, property, value, receiver);
  },

  has(target, property) {
    console.log(`Checking existence of: ${property}`);
    return Reflect.has(target, property);
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.name); // 日志 + "Alice"
proxy.age = 30; // 日志
console.log('age' in proxy); // 日志 + true

3.2 验证和拦截示例

const validator = {
  set(target, property, value, receiver) {
    if (property === 'age') {
      if (typeof value !== 'number') {
        throw new TypeError('Age must be a number');
      }
      if (value < 0 || value > 150) {
        throw new RangeError('Age must be between 0 and 150');
      }
    }

    if (property === 'name') {
      if (typeof value !== 'string') {
        throw new TypeError('Name must be a string');
      }
      if (value.length < 2) {
        throw new Error('Name must be at least 2 characters long');
      }
    }

    return Reflect.set(target, property, value, receiver);
  }
};

const person = new Proxy({}, validator);

try {
  person.age = 25; // 成功
  person.name = 'Alice'; // 成功
  person.age = 'old'; // 抛出 TypeError
} catch (error) {
  console.error(error.message);
}

3.3 自动日志记录

function createLoggingProxy(target, name = 'target') {
  return new Proxy(target, {
    get(target, property, receiver) {
      console.log(`[${name}] Getting property: ${String(property)}`);
      const value = Reflect.get(target, property, receiver);

      // 如果值是函数,返回一个代理版本
      if (typeof value === 'function') {
        return function(...args) {
          console.log(`[${name}] Calling method: ${String(property)}`, args);
          const result = Reflect.apply(value, target, args);
          console.log(`[${name}] Method ${String(property)} returned:`, result);
          return result;
        };
      }

      return value;
    },

    set(target, property, value, receiver) {
      console.log(`[${name}] Setting property: ${String(property)} =`, value);
      return Reflect.set(target, property, value, receiver);
    }
  });
}

const obj = createLoggingProxy({
  count: 0,
  increment() {
    this.count++;
    return this.count;
  }
}, 'counter');

obj.increment(); // 详细的调用日志

4. 实际应用场景

4.1 数据绑定和响应式系统

class Observable {
  constructor() {
    this.observers = new Map();
    this._data = new Proxy({}, {
      set: (target, property, value, receiver) => {
        const oldValue = target[property];
        const result = Reflect.set(target, property, value, receiver);

        if (oldValue !== value) {
          this._notify(property, value, oldValue);
        }

        return result;
      }
    });
  }

  set(property, value) {
    this._data[property] = value;
  }

  get(property) {
    return this._data[property];
  }

  observe(property, callback) {
    if (!this.observers.has(property)) {
      this.observers.set(property, new Set());
    }
    this.observers.get(property).add(callback);
  }

  unobserve(property, callback) {
    if (this.observers.has(property)) {
      this.observers.get(property).delete(callback);
    }
  }

  _notify(property, newValue, oldValue) {
    if (this.observers.has(property)) {
      this.observers.get(property).forEach(callback => {
        callback(newValue, oldValue);
      });
    }
  }
}

// 使用示例
const observable = new Observable();

observable.observe('name', (newVal, oldVal) => {
  console.log(`Name changed from ${oldVal} to ${newVal}`);
});

observable.set('name', 'Alice'); // 触发回调
observable.set('name', 'Bob');   // 触发回调

4.2 权限控制系统

class SecureObject {
  constructor(initialData = {}, userPermissions = {}) {
    this._userPermissions = userPermissions;

    return new Proxy(initialData, {
      get: (target, property, receiver) => {
        if (!this._checkPermission('read', property)) {
          throw new Error(`No read permission for property: ${String(property)}`);
        }
        return Reflect.get(target, property, receiver);
      },

      set: (target, property, value, receiver) => {
        if (!this._checkPermission('write', property)) {
          throw new Error(`No write permission for property: ${String(property)}`);
        }
        return Reflect.set(target, property, value, receiver);
      },

      has: (target, property) => {
        if (!this._checkPermission('read', property)) {
          return false; // 隐藏属性
        }
        return Reflect.has(target, property);
      },

      ownKeys: (target) => {
        return Reflect.ownKeys(target).filter(key => 
          this._checkPermission('read', key)
        );
      }
    });
  }

  _checkPermission(action, property) {
    const permissions = this._userPermissions[property] || [];
    return permissions.includes(action) || permissions.includes('*');
  }
}

// 使用示例
const secureData = new SecureObject({
  publicInfo: 'Everyone can see this',
  privateInfo: 'Secret data',
  adminInfo: 'Admin only'
}, {
  publicInfo: ['read', 'write'],
  privateInfo: ['read'],
  adminInfo: ['*'] // 所有权限
});

console.log(secureData.publicInfo); // 正常
// console.log(secureData.privateInfo); // 如果有写权限检查会报错

5. 最佳实践和注意事项

5.1 错误处理

// Reflect 方法返回布尔值,而不是抛出错误
const obj = Object.freeze({ x: 1 });

// 传统方式(会抛出错误)
try {
  Object.defineProperty(obj, 'y', { value: 2 });
} catch (error) {
  console.error('Error:', error.message);
}

// Reflect 方式(返回 false)
const success = Reflect.defineProperty(obj, 'y', { value: 2 });
if (!success) {
  console.log('Failed to define property');
}

5.2 性能考虑

// 直接操作 vs Reflect(性能相当)
const obj = { x: 1 };

// 基准测试
console.time('direct access');
for (let i = 0; i < 1000000; i++) {
  obj.x;
}
console.timeEnd('direct access');

console.time('reflect get');
for (let i = 0; i < 1000000; i++) {
  Reflect.get(obj, 'x');
}
console.timeEnd('reflect get');

5.3 与 Object 方法的对比

const obj = {};

// 定义属性
Object.defineProperty(obj, 'x', { value: 1 }); // 成功时返回对象,失败时抛出错误
Reflect.defineProperty(obj, 'y', { value: 2 }); // 总是返回布尔值

// 获取原型
Object.getPrototypeOf(obj); // 标准方式
Reflect.getPrototypeOf(obj); // 元编程友好

// 应用函数
Function.prototype.apply.call(Math.max, null, [1, 2, 3]); // 复杂
Reflect.apply(Math.max, null, [1, 2, 3]); // 简洁

总结

Reflect 对象为 JavaScript 提供了:

  • 统一的元编程接口
  • 更好的错误处理机制
  • 与 Proxy 的完美配合
  • 函数式编程风格

虽然日常开发中直接使用 Reflect 的场景不多,但在框架开发、库设计和高级编程模式中,Reflect 是不可或缺的工具。