在较低级别使用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构造可以大大改善代码体系结构和设计。