51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

前端开发:JavaScript中的代理和生成器

500.jpg

在较低级别使用JavaScript时,对这些构造有扎实的了解将非常方便。在本文中,我们将介绍一些适合这些构造的用例,它们肯定会为您带来一些便捷。

代理服务器

简单来说,代理服务器是控制对另一个对象的访问的对象。根据MDN文档【https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy】:

Proxy对象用于定义基本操作的自定义行为(例如,属性查找,赋值,枚举,函数调用等)。

该描述大致转化为一种思想,我们可以拦截对对象和函数的基本操作(例如,函数调用,属性查找,赋值,枚举等),并在运行时执行我们自己的自定义操作!太好了,是吗?

在继续之前,有三个与代理对象相关的关键术语:

  • 处理程序  - 包含陷阱的占位符对象。

  • 陷阱  - 提供属性访问的方法。这类似于操作系统中陷阱的概念。

  • 目标  - 代理虚拟化的对象。

如果使用正确,代理对象将强大地补充其目标对象。

创建代理

我们可以使用Proxy构造函数创建一个JavaScript Proxy --- new Proxy().Proxy构造函数接受两个对象作为参数。第一个是代理虚拟化的目标对象,第二个是处理程序对象,其中包含一组称为"陷阱"的方法。这些陷阱将控制对目标对象的属性访问:

const proxy = new Proxy(targetObject, handler);

我们可以通过将目标对象和空处理程序传递给Proxy构造函数来创建一个简单的Proxy对象:

const targetObj = {name: 'Target'};
const proxy = new Proxy(targetObj, {});

proxy.name; // returns 'Target'

我们上面定义的Proxy对象目前对目标对象没有任何作用。它只是将对" name"属性的请求传递给目标对象。为了在代理服务器上为其目标定义一个或多个自定义行为,我们将需要声明一个或多个处理程序。

这种处理程序之一是get陷阱处理程序。下面的示例截取对目标对象的属性的" getter"调用:

const data = {
  firtName: 'Bryan',
  lastName: 'John'
};

const handler = {
  get(target, prop) {
    return prop in target ? target[prop] : 'This property doesn’t exist, sorry';
  }
};

const proxy = new Proxy(data, handler);

console.log(proxy.firstName); // Returns 'Bryan'
console.log(proxy.age); // Returns 'No such property'

在上面的代码中,我们有handler一个包含get陷阱的对象。该get陷阱截获请求访问的目标对象,由代理对象进行,并返回(如果可用)或请求的财产"这个属性不存在,对不起",如果它不是。

如果我们想拦截在对象上设置属性的调用,则需要使用set陷阱。

让我们看一个更有用的例子。我们将使用set陷阱检查actualPay对象的属性是否已设置。如果存在此物业,我们将从所支付的金额中扣除3%作为交易费,并将新值分配给该actualPay物业:

const transaction = {};

const handler = {
  set(target, prop, value) {
    if(prop === 'actualPay' && typeof value === "number" && value > 0) {
      value = value * 0.97;
    }
    target[prop] = value;
  }
};

const proxy = new Proxy(transaction, handler);
proxy.actualPay = 1000;
console.log(proxy.actualPay); //Returns '970'

用例范例

代理的好处是您不需要事先知道或定义属性。这与ES5获取器/设置器相反,后者要求事先提供属性:

const data = {
  _firstName: 'John',
  _lastName: 'Doe',

  get firstName() {
    console.log('getting the firstname: ', this._firstName);
  },

  get lastName() {
    console.log('getting the lastname: ', this._lastName);
  },
};

data.firstName; //logs -> getting the firstname: John
data.lastName; //logs -> getting the firstname: Doe

在上面的示例中,我们getters为firstname和lastname属性定义了。然而,如果我们增加了新的特性- age-我们需要定义一个新的getter - get age()-上data对象访问属性:

data.age = 23; // adds a new property -- age
console.log(data.age); // logs 23 but doesn't automatically have a getter

使用代理,我们可以简单地get为所有访问对象属性的请求注册一个陷阱,包括那些在编写时没有声明的请求:

const proxyObj = new Proxy({
    firstName: 'John',
    lastName: 'Doe',
}, {
  get(targetObj, property) {
    console.log(`getting the ${property} property: ${targetObj[property]}`);
  }
});

proxyObj.firstName; //Returns -> getting the firstName: John
proxyObj.lastName;// Returns -> getting the lastName property: Doe
proxyObj.age = 23; 
console.log(proxyObj.age);// Returns -> getting the age property: 23

在上面的示例中,我们能够使用Proxy记录对象上所有属性的值。

代理可以满足更多的用例,例如,我们可以创建一个自定义对象验证器,该验证器检查对象的属性以确保只能将预期类型设置为值。

我们还可以创建一个自定义身份验证系统,以确保授权客户端对目标执行操作。可能性是无止境!

代理的其他一些可能的用例是:

  • 条件缓存

  • 属性查询扩展

  • 价值修正

  • 调试

生成器

当调用一个函数时,JavaScript引擎开始从函数顶部到底部执行代码。这种执行模型称为" 运行到完成",当您希望函数按定义运行时,它非常有用。

但是,有时您希望暂停函数的执行,运行其他代码段,然后从上次中断的地方继续执行。发电机就是这个愿望的答案!

什么是生成器?

简而言之,生成器是一种可以在中途停止然后从停止处继续运行的功能。

让我们考虑一个类比-想象一下,您正在一天的待办事项列表中工作,而老板礼貌地要求您立即进行其他工作。我敢打赌,按照以下五个步骤,您的下一个操作将是:

  • 隐式记住您在"待办事项"列表上的位置。

  • 抱怨老板的事,随意踢椅子。

  • 处理老板刚刚分配给您的任务。

  • 返回您的办公桌,然后从您在"待办事项"列表上停下来的位置继续。

  • 检查您的手腕,看看是否是下午5点,您错过了新的副业项目,该项目肯定将成为一家市值十亿美元的公司。

您的行为就像一个生成器函数!我的意思是,除了第2点和第5点。生成器函数可以暂停其执行,运行其他操作,并记住执行时在何处暂停,然后从那里继续。

生成器函数是ES6构造,可以简化JavaScript应用程序的异步控制流,同时隐式维护其内部状态。调用生成器函数时,在执行该函数的代码之前,它首先创建一个称为生成器的迭代器对象。根据ECMAScript规范,此生成器对象:

是生成器函数的实例,并同时符合Iterator和Iterable接口。

Iterable协议在指定如何使用for..of构造迭代对象中的值时提供了很大的灵活性。迭代器协议为对象中的值定义了一种标准方法。可以使用.next()本文稍后将看到的方法来实现迭代。

信息:异步/等待构造基于生成器。

使用生成器

生成器函数是使用function*语法创建的。它的值是通过调用next()方法生成的,可以使用yield关键字暂停执行。每次调用该函数时,它都会返回一个新的Generator对象,该对象可以迭代一次:

function* getCurrency() {
  console.log('the generator function has started');
  const currencies = ['NGN', 'USD', 'EUR', 'GBP', 'CAD'];
  for (const currency of currencies) {
    yield currency;
  }
  console.log('the generator function has ended');
}

const iterator = getCurrency();

console.log(iterator.next()); 
// logs -> 'the generator function has started' 
// {value: 'NGN', done: false}

console.log(iterator.next());
// {value: 'USD', done: false}

console.log(iterator.next());
//{value: 'EUR', done: false}

console.log(iterator.next());
//{value: 'GBP', done: false}

console.log(iterator.next());
//{value: 'CAD', done: false}

console.log(iterator.next());
// the generator function has ended
// {value: undefined, done: true}

在上面的示例中,生成器函数getCurrency()使用yield关键字发送数据。next()在生成器对象上调用该方法将返回其yield值和一个布尔值--- done--- true在迭代生成器函数的所有值之后变为Boolean ,如上例中的上一次迭代所示。

我们还可以使用该next()方法将数据传递给生成器函数

function* displayCurrrency() {
  console.log(`currency info to be sent into the generator function: ${yield}`);
}

const iterator = displaycurrency();
iterator.next(); //this starts the generator function
iterator.next('US dollars');
// logs -> currency info to be sent into the generator function: US dollars

在上面的示例中,next('US dollars')将数据发送到生成器函数,并用" yield美元""替换" 关键字。

总结

我们已经了解了JavaScript代理以及它们如何控制和自定义JavaScript对象的行为。我们还看到,生成器对象可以将数据发送进出其生成器函数。

如果使用正确,这些ES6构造可以大大改善代码体系结构和设计。

赞(0)
未经允许不得转载:工具盒子 » 前端开发:JavaScript中的代理和生成器