首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

标准库中的新对象和类型-4

标准库中的新对象和类型-4

在 ECMAScript 6 中使用代理要设置代理,首先需要识别目标,该目标也被认为是我们想拦截其方法或属性的对象。在本例中,我们将拦截 Person                对象。接下来,我们需要识别想在调用方与目标之间插入的行为。在 ECMAScript                的说法中,这称为处理函数。每个处理函数可定义一个或多个以下方法:
  • get() 和 set()                    分别拦截对对查找和设置属性值的任何尝试。(请记住,对象的函数也是属性。)
  • apply() 拦截对函数的任何调用
  • has() 拦截 in 运算符
  • ownKeys() 拦截 Object 方法                    getOwnPropertyNames()
  • getPrototypeOf()、setPrototypeOf()、isExtensible()、preventExtensions()、defineProperty()                    和 getOwnPropertyDescriptor() 拦截同名的 Object                    方法
  • deleteProperty() 拦截 delete 运算符
我们将首先捕获任何对 Person(目标)对象上的属性进行获取和设置的请求。为此,我们创建一个处理函数对象来分别提供                set 和 get 方法:
清单 9.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var firstNameS = Symbol('firstName');
var lastNameS = Symbol('lastName');
var ageS = Symbol('age');
  
class Person {
  constructor(firstName, lastName, age) {
    this[firstNameS] = firstName;
    this[lastNameS] = lastName;
    this[ageS] = age;
  }
};

var ted = new Person('Ted', 'Neward', 45);

ted.sayHowdy = function() {
  console.log(this[firstNameS] + ' says howdy!');
};

ted.waveGoodbye = function(msg) {
  return '' + msg + ' Buh-bye!';
};

let handler = {};
handler.get = function(target, propName) {
  console.log('\nhandler.get() accessed property', propName,
    'of class', target.constructor.name);
  return target[propName];
}
handler.set = function(target, propName, newValue) {
  console.log('\nhandler.set() changed property', propName,
    'of class', target.constructor.name, 'to', newValue);
  target[propName] = newValue;
}

ted = new Proxy(ted, handler);

ted.sayHowdy();
console.log(ted.waveGoodbye('See you next time!'));

ted.favoriteColor = 'green';
console.log('Ted\'s favorite color is', ted.favoriteColor);




请注意,set                处理函数不仅将消息记录到控制台,还会执行属性分配工作。这么做是有必要的,因为处理函数拥有完全控制权:如果您没有将属性分配给目标,就不会设置该属性。get                处理函数也是如此,它必须返回目标的属性值。 如果您没有分配属性,返回的属性将是空的(或 undefined)。
最后一步是在目标和处理函数周围连接一个 Proxy 对象。我们通过以下代码将 Proxy                对象捕获回原始变量中:
ted = new Proxy(ted, handler);
在某些场景中,您想坚持使用原始变量,以便无需通过拦截器即可访问目标。但是,在大多数时候,将会使用 Proxy                作为静默处理器,这样,使用目标对象的客户端甚至不会知道它们与目标之间存在任何对象。
如果在处理函数就位后,将 waveGoodbye() 和 sayHowdy()                添加到对象,将调用处理函数来执行属性设置操作。这是因为,从技术上讲,waveGoodbye 和                sayHowdy 是函数类型的属性。运行此代码,可以看到只要访问一个属性或函数,就会调用处理函数的                get 和 set 函数。尝试将 sayHowdy() 和                sayGoodbye() 函数移到处理函数的定义后面,然后再次运行该代码。
请注意,我们调用了 get() 处理函数。访问该方法意味着获取该方法(以便调用它),然后(对于                sayHowdy)获取该方法中引用的所有属性的值。创建 favoriteColor 属性时会调用                set() 方法,即使在创建处理函数时该属性不存在也是如此。与您预期的一样,访问这个新属性会触发                get() 方法。
函数上的代理处理函数讲得更清楚一点,无论该属性是如何定义的,始终会调用 get 处理函数。为了演示这一点,我们将在                Person 类中定义一个名为 eat() 的方法。
如果调用了 Person 类型对象的 eat() 方法,ECMAScript                的第一个任务就是将属性名称“eat”解析为生成一个函数的属性。首先,它将获取该函数,然后立即调用它。如果我们想了解被调用函数的更多细节,我们需要在找到和返回该函数后,将一个新处理函数插入到调用过程中。最简单的方法是返回一个包装了原始函数的函数:
清单 10.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
var firstNameS = Symbol('firstName');
var lastNameS = Symbol('lastName');
var ageS = Symbol('age');
  
class Person {
  constructor(firstName, lastName, age) {
    this[firstNameS] = firstName;
    this[lastNameS] = lastName;
    this[ageS] = age;
  }
   
  eat() {
    console.log('Chomp, chomp');
  }
};

var ted = new Person('Ted', 'Neward', 45);

let handler = {};
handler.get = function(target, propName) {
  let original = target[propName];
  if (typeof original === 'function') {
    console.log('\nhandler.get() invoked function',
      propName, 'of class', target.constructor.name);
    return (...args) => {
      console.log('Executing', propName, 'with', args);
      // use handler method apply() to invoke the function
      return original.apply(target, args);
    }
  } else {
    console.log('\nhandler.get() accessed property', propName,
      'of class', target.constructor.name);
    return target[propName];
  }
}
handler.set = function(target, propName, newValue) {
  console.log('\nhandler.set() changed property', propName,
    'of class', target.constructor.name, 'to', newValue);
  target[propName] = newValue;
}

ted = new Proxy(ted, handler);

ted.eat();




该过程可能看起来很复杂,其实不然。如果被访问的属性不是函数,只需获取结果并返回它。如果该属性是函数,那么可以创建一个函数字面常量并返回该常量。返回的函数字面常量将调用原始函数。在函数对象上使用                ECMAScript 的 apply 方法,可确保我们将正确的 this                绑定到适当位置。(如果我们使用的是 this 而不是                target,this 将是处理函数,而不是目标。)
返回列表