JavaScript 代理对象

摘要: 本教程将讲解 ES6 中的 JavaScript 代理对象。

什么是 JavaScript 代理对象

JavaScript 代理对象是一个 对象,它包裹另一个对象(目标对象)并拦截目标对象的基本操作。

基本操作可以是属性查找、赋值、枚举、函数调用等等。

创建代理对象

要创建新的代理对象,可以使用以下语法

let proxy = new Proxy(target, handler);
Code language: JavaScript (javascript)

在此语法中

  • target – 是要包裹的对象。
  • handler – 是一个包含方法的对象,用于控制target的行为。handler 对象内部的方法称为陷阱。

一个简单的代理示例

首先,定义一个名为user的对象

const user = {
    firstName: 'John',
    lastName: 'Doe',
    email: '[email protected]',
}
Code language: JavaScript (javascript)

其次,定义一个handler对象

const handler = {
    get(target, property) {
        console.log(`Property ${property} has been read.`);
        return target[property];
    }
}
Code language: JavaScript (javascript)

第三,创建一个proxy对象

const proxyUser = new Proxy(user, handler);
Code language: JavaScript (javascript)

proxyUser对象使用user对象来存储数据。proxyUser可以访问user对象的所有属性。

JavaScript Proxy

第四,通过proxyUser对象访问user对象的firstNamelastName属性

console.log(proxyUser.firstName);
console.log(proxyUser.lastName);
Code language: CSS (css)

输出

Property firstName has been read.
John
Property lastName has been read.
Doe

当您通过proxyUser对象访问user对象的属性时,handler对象中的get()方法会被调用。

第五,如果修改原始对象user,更改会反映在proxyUser

user.firstName = 'Jane';
console.log(proxyUser.firstName);
Code language: JavaScript (javascript)

输出

Property firstName has been read.
Jane

类似地,proxyUser对象的更改也会反映在原始对象(user)中

proxyUser.lastName = 'William';
console.log(user.lastName);
Code language: JavaScript (javascript)

输出

William    

代理陷阱

get()陷阱

当通过代理对象访问target对象的属性时,会触发get()陷阱。

在前面的示例中,当proxyUser对象访问user对象的属性时,会打印出一条消息。

通常,您可以在访问属性时在get()陷阱中开发自定义逻辑。

例如,您可以使用get()陷阱为目标对象定义计算属性。计算属性是根据现有属性的值计算其值的属性。

user对象没有fullName属性,您可以使用get()陷阱根据firstNamelastName属性创建fullName属性

const user = {
    firstName: 'John',
    lastName: 'Doe'
}

const handler = {
    get(target, property) {
        return property === 'fullName' ?
            `${target.firstName} ${target.lastName}` :
            target[property];
    }
};

const proxyUser = new Proxy(user, handler);

console.log(proxyUser.fullName);
Code language: JavaScript (javascript)

输出

John Doe

set()陷阱

set()陷阱控制设置target对象属性时的行为。

假设用户的age必须大于18。为了执行此约束,您可以开发一个set()陷阱,如下所示

const user = {
    firstName: 'John',
    lastName: 'Doe',
    age: 20
}

const handler = {
    set(target, property, value) {
        if (property === 'age') {
            if (typeof value !== 'number') {
                throw new Error('Age must be a number.');
            }
            if (value < 18) {
                throw new Error('The user must be 18 or older.')
            }
        }
        target[property] = value;
    }
};

const proxyUser = new Proxy(user, handler);
Code language: JavaScript (javascript)

首先,将用户的age设置为字符串

proxyUser.age = 'foo';
Code language: JavaScript (javascript)

输出

Error: Age must be a number.
Code language: JavaScript (javascript)

其次,将用户的年龄设置为16

proxyUser.age = '16';    
Code language: JavaScript (javascript)

输出

The user must be 18 or older.

第三,将用户的年龄设置为21

proxyUser.age = 21;

没有发生错误。

apply()陷阱

handler.apply()方法是函数调用的陷阱。语法如下

let proxy = new Proxy(target, {
    apply: function(target, thisArg, args) {
        //...
    }
});
Code language: JavaScript (javascript)

请参见以下示例

const user = {
    firstName: 'John',
    lastName: 'Doe'
}

const getFullName = function (user) {
    return `${user.firstName} ${user.lastName}`;
}


const getFullNameProxy = new Proxy(getFullName, {
    apply(target, thisArg, args) {
        return target(...args).toUpperCase();
    }
});

console.log(getFullNameProxy(user)); // 
Code language: JavaScript (javascript)

输出

JOHN DOE

更多陷阱

以下是一些其他陷阱

  • construct – 拦截new运算符的使用
  • getPrototypeOf – 拦截对[[GetPrototypeOf]]的内部调用
  • setPrototypeOf – 拦截对Object.setPrototypeOf的调用
  • isExtensible – 拦截对Object.isExtensible的调用
  • preventExtensions – 拦截对Object.preventExtensions的调用
  • getOwnPropertyDescriptor – 拦截对Object.getOwnPropertyDescriptor的调用

在本教程中,您学习了 JavaScript 代理对象,它用于包裹另一个对象,以更改该对象的根本行为。

本教程是否有帮助?