ES6学习笔记(十)代理器Proxy
Java可以使用面向切面(AOP)的方法来实现某些统一的操作,比如某个操作的前置通知,后置通知等等,这种操作非常方便,其本质便是动态代理,JS的代理Proxy代理该如何使用呢?
某位大神的实现如下:
var objectProxy={
create:function(target,methodHandler){
if(!(target instanceof Object)){
throw new Error("target argument is not object type!");
}
var isFunction = function(o){
return (o instanceof Function);
}
if(!(methodHandler instanceof Object)){
throw new Error("methodHandler is not a valid object!");
}
//前置 后置 错误 返回 四个处理器
if(!methodHandler.before && !methodHandler.after && !methodHandler.error && !methodHandler.returing){
return target;
}
var proxy = {};
for(var i in target){//获取对象属性方法
var targetPrototype = target[i];
if(isFunction(targetPrototype)){
var process = true;
if(methodHandler.filter)
process = methodHandler.filter(i);
if(process){
proxy[i] = function(){
var args = {name:i,args:arguments};
if(methodHandler.before)methodHandler.before.call(target,args);//执行前置处理
var result = null;
try{
result = targetPrototype(arguments);
args["result"] = result;//设置返回值
if(methodHandler.after)methodHandler.after.call(target,args);//执行后置处理
}catch(e){
args["error"] = e;//设置异常信息
if(methodHandler.error)methodHandler.error.call(target,args);//执行异常处理
}
if(methodHandler.returing)
result = methodHandler.returing.call(target,args);//执行返回处理
return result;
}
}
}
}
return proxy;
}
};
window.onload=function(){
var proxy = {prop:"prop name!",func:function(){
alert("process ...!");
return "test proxy!";
}};
var methodHandler = {
filter:function(name){//过滤方法 (true|false) false 表示不处理
return true;
},
before:function(args){//前置
alert("before methodHandler name : "+args.name);
},
after:function(args){//后置
alert("after methodHandler name : "+args.name+",return : "+args.result);
},
error:function(args){//错误
alert("error methodHandler name : "+args.name+",error : "+args.error);
},
returing:function(args){//返回
alert("returing methodHandler name : "+args.name+",error : "+(args.error?args.error:null)+",return : "+args.result);
return "changed : "+args.result;
}
};
proxy = objectProxy.create(proxy,methodHandler);
proxy.func();
}
然后开始学习Proxy
1.概述
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
}); obj.count = 1;//setting count! ++ obj.count;//getting count!, setting count!
上面代码定义了一个空对象{ }的代理对象obj,在代理对象上重定义了属性的读取(get
)和设置(set
)行为。这里暂时先不解释具体的语法,只看运行结果。对设置了拦截行为的对象obj
,去读写它的属性,就会触发定义的拦截操作。
上面代码说明,Proxy 实际上重载(overload)了点运算符(...),即用自己的定义覆盖了语言的原始定义。
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler);
Proxy 对象的所有用法,都是上面这种形式,不同的只是handler
参数的写法。
其中,new Proxy()
表示生成一个Proxy
实例,target
参数表示所要拦截的目标对象,handler
参数也是一个对象,用来定制拦截行为。
下面是另一个拦截读取属性行为的例子。
var proxy = new Proxy({}, {
get: function (target, property) { return 35; }
}); console.log(proxy.name); //
console.log(proxy.age); //
console.log(proxy.salary); //
上面代码中,作为构造函数,Proxy
接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy
的介入,操作原来要访问的就是这个对象;
第二个参数是一个配置对象(不是回调函数),对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。
上面代码中,配置对象有一个get
方法,用来拦截对目标对象属性的访问请求。get
方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35
,所以访问任何属性都得到35
。
注意,要使得Proxy
起作用,必须针对Proxy
实例(上例是proxy
对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。
如果handler
没有设置任何拦截,那就等同于直接通向原对象。
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
target.a = 'b',
console.log(target.a);//b
上面代码中,handler
是一个空对象,没有任何拦截效果,访问proxy
就等同于访问target
。
一个技巧是将 Proxy 对象,设置到object.proxy
属性,从而可以在object
对象上调用。
var object = { proxy: new Proxy(target, handler) };
Proxy 实例也可以作为其他对象的原型对象。
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
}); let obj = Object.create(proxy);
obj.time //
上面代码中,proxy
对象是obj
对象的原型,obj
对象本身并没有time
属性,所以根据原型链,会在proxy
对象上读取该属性,导致被拦截。
同一个拦截器函数,可以设置拦截多个操作。
var handler = {
get: function (target, name) {
if (name === 'prototype') {
return Object.prototype;
}
return 'Hello, ' + name;
}, apply: function (target, thisBinding, args) {
return args[0];
}, construct: function (target, args) {
return {value: args[1]};
}
}; //target是个代理对象,而函数也是对象,所以此处可以传入一个函数作为目标对象
var fproxy = new Proxy(function (x, y) {
return x + y;
}, handler); console.log(fproxy(1, 2));//1,函数调用时执行apply方法,被拦截,只传入了第一个参数 console.log(new fproxy(1, 2));//{ value: 2 },new对象时执行construct构造方法,被拦截,只使用了第二个参数 console.log(fproxy.prototype === Object.prototype);//获取prototype属性,被拦截,返回Object.prototype,所以相等
console.log(fproxy.foo === 'Hello, foo');//获取foo属性,返回固定的'Hello, ' + 属性名
对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
下面是 Proxy 支持的拦截操作一览,一共 13 种。
- get(target, propKey, receiver):拦截对象属性的读取,比如
proxy.foo
和proxy['foo']
。 - set(target, propKey, value, receiver):拦截对象属性的设置,比如
proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值。 - has(target, propKey):拦截
propKey in proxy
的操作,返回一个布尔值。 - deleteProperty(target, propKey):拦截
delete proxy[propKey]
的操作,返回一个布尔值。 - ownKeys(target):拦截
Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。 - getOwnPropertyDescriptor(target, propKey):拦截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。 - defineProperty(target, propKey, propDesc):拦截
Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。 - preventExtensions(target):拦截
Object.preventExtensions(proxy)
,返回一个布尔值。 - getPrototypeOf(target):拦截
Object.getPrototypeOf(proxy)
,返回一个对象。 - isExtensible(target):拦截
Object.isExtensible(proxy)
,返回一个布尔值。 - setPrototypeOf(target, proto):拦截
Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 - apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。 - construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)
。
2.Proxy 实例的方法
下面是上面这些拦截方法的详细介绍。
get()
get()
方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
var person = {
name:'aaa',
} var porxy = new Proxy(person, {
get: function (target, property) {
if (property in target) {
return target[property];
} else {
throw new ReferenceError(`${property} not exsist as person`);
}
}
}); console.log(porxy.name);//aaa
console.log(porxy.age);//ReferenceError: age not exsist as person
上面代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined
。
get
方法可以继承,也就是说可以放在原型上。
var proxy = new Proxy({}, {
get: function (target, property, receiver) {
console.log('GET: ' + property);
return target[property];
}
}); var obj = Object.create(proxy);
obj.foo;//GET: foo
上面代码中,get的拦截操作定义在原型对象上,所以调用实例继承了proxy的obj实例时,拦截生效。
下面的例子使用get
拦截,实现数组读取负数的索引。
function CreateArray(...elements) {
let handler = {
get(target, propKey, receiver){
let index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);//把传入的复数转为对应的正数下标
}
return Reflect.get(target, propKey, receiver);//返回当前get函数
}
}; let target = [];
target.push(...elements);//数组对象数据填充
return new Proxy(target, handler);//返回一个数组代理对象
} let arr = new CreateArray(1,2,3,4,5);
console.log(arr[-2]);//
上面是一个自定义的数组构造函数,获取值时,数组的位置参数是-2
,就会输出数组的倒数第二个成员。
下面是一个get
方法的第三个参数的例子,它总是指向原始的读操作所在的那个对象,一般情况下就是 Proxy 实例。
const proxy = new Proxy({}, {
get: function(target, property, receiver) {
return receiver;
}
});
proxy.getReceiver === proxy // true
上面代码中,proxy
对象的getReceiver
属性是由proxy
对象提供的,所以receiver
指向proxy
对象。
const proxy = new Proxy({}, {
get: function(target, property, receiver) {
return receiver;
}
}); const d = Object.create(proxy);
d.a === d // true
上面代码中,d
对象本身没有a
属性,所以读取d.a
的时候,会去d
的原型proxy
对象找。这时,receiver
就指向d
,代表原始的读操作所在的那个对象。
如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错。
set()
set
方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
//定义一个set处理函数
let validator = {
set(target, prop, value){
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
target[prop] = value;//对于满足条件的age及其他属性,直接赋值
}
} let person = new Proxy({}, validator); person.name = 'aaa';
person.age = 'cbd';
person.age = '201';
上面代码中,由于设置了存值函数set
,任何不符合要求的age
属性赋值,都会抛出一个错误,这是数据验证的一种实现方法。利用set
方法,还可以数据绑定,即每当对象发生变化时,会自动更新 DOM。
有时,我们会在对象上面设置内部属性,属性名的第一个字符使用下划线开头,表示这些属性不应该被外部使用。结合get
和set
方法,就可以做到防止这些内部属性被外部读写。
//get/set处理操作
const handler = {
get(target, key){
invariant(key, 'get');
return target[key];
},
set(target, key, value){
invariant(key, 'set');
target[key] = value;
return true;
}
}; //属性名-get/set校验方法
function invariant(key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attemp to ${action} private "${key}" property`);
}
} const target = {};
const proxy = new Proxy(target, handler);
// proxy._proto;//Error: Invalid attemp to get private "_proto" property proxy._proto = 5;//Error: Invalid attemp to set private "_proto" property
上面代码中,只要读写的属性名的第一个字符是下划线,一律抛错,从而达到禁止读写内部属性的目的。
为了达到一个类似Java的private效果,不,这已经是finally效果了,可谓煞费苦心,这便是JS在封装上的短板吧。
到目前为止发现,target对象 可以是某个对象,对象原型或者方法,而handler对象常用的几个代理方法却是不变的,所以代理的核心思想应该是需要改变的,做的处理过程---handler,并且这个处理对象是可以被重用的。
set
方法的第四个参数receiver
,指的是原始的操作行为所在的那个对象,一般情况下是proxy
实例本身;
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
}
};
const proxy = new Proxy({}, handler);
const myObj = {};
Object.setPrototypeOf(myObj, proxy); myObj.foo = 'bar';
myObj.foo === myObj // true
上面代码中,设置myObj.foo
属性的值时,myObj
并没有foo
属性,因此引擎会到myObj
的原型链去找foo
属性。myObj
的原型对象proxy
是一个 Proxy 实例,设置它的foo
属性会触发set
方法。这时,第四个参数receiver
就指向原始赋值行为所在的对象myObj
。
注意,如果目标对象自身的某个属性,不可写且不可配置,那么set
方法将不起作用。
注意,严格模式下,set
代理如果没有返回true
,就会报错。
'use strict';
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
// 无论有没有下面这一行,都会报错
return false;
}
};
const proxy = new Proxy({}, handler);
proxy.foo = 'bar';
// TypeError: 'set' on proxy: trap returned falsish for property 'foo'
上面代码中,严格模式下,set
代理返回false
或者undefined
,都会报错。
apply()
apply
方法拦截函数的调用、call
和apply
操作。按照动态代理来说,这个拦截操作才是重点。
apply
方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this
)和目标对象的参数数组。
const handler = {
apply(target, ctx, args){
return Reflect.apply(...arguments); //Reflect:反射,下一节学到,相当于另一个Object
}
};
无参数的简单例子
//目标对象是一个函数的时候,apply调用拦截才有用武之地
const target = function () {
return 'I am the target';
} const handler = {
apply(){
return 'I am the proxy'
}
} //对象的代理是个对象,函数的代理是个函数
var proxy = new Proxy(target, handler); console.log(proxy());//I am the proxy
再一个例子
const twice = {
apply(target, ctx, args){
return Reflect.apply(...arguments) * 2;
}
}; function sum(left, right) {
return left + right;
} var proxy = new Proxy(sum, twice); console.log(proxy(1,2));//
console.log(proxy.call(null,3,4));//
console.log(proxy.apply(null,[5,6]));//
//call和apply的第一个参数都是需要调用的函数对象,在函数体内这个参数就是this的值,剩余的参数是需要传递给函数的值,
//call与apply的不同就是call传的值可以是任意的,而apply传的剩余值必须为数组
发现apply的三个参数:目标对象,目标对象的上下文this,参数args没有被使用,他们应该在需要使用的地方才用。
上面代码中,每当执行proxy
函数(直接调用或call
和apply
调用),就会被apply
方法拦截。
另外,直接调用Reflect.apply
方法,也会被拦截。
has()
has
方法用来拦截HasProperty
操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in
运算符。这个拦截应该不常用。
has
方法可以接受两个参数,分别是目标对象、需查询的属性名。
var handler = {
has (target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler);
'_prop' in proxy // false
上面代码中,如果原对象的属性名的第一个字符是下划线,proxy.has
就会返回false
,从而不会被in
运算符发现。
注意:如果原对象不可配置或者禁止扩展,使用has
拦截就会报错,也就是说,如果某个属性不可配置(或者目标对象不可扩展),则has
方法就不得“隐藏”(即返回false
)目标对象的该属性。
注意:has
方法拦截的是HasProperty
操作,而不是HasOwnProperty
操作,即has
方法不判断一个属性是对象自身的属性,还是继承的属性。
另外,虽然for...in
循环也用到了in
运算符,但是has
拦截对for...in
循环不生效。
construct()
construct
方法表示构造函数被调用,用于拦截new
命令,下面是拦截对象的写法。
var handler = {
construct(target, args, newTarget){
return new target(...args);
}
};
construct
方法可以接受三个参数。
target
:目标对象args
:构造函数的参数对象newTarget
:创造实例对象时,new
命令作用的构造函数(下面例子的p
),可选参数
var p = new Proxy(function () {}, {
construct(target, args){
console.log('called: ' + args.join(','));
return {value: args[0] * 10};
}
}); console.log((new p(1)).value); // called: 1
//
构造函数的目标函数是一个匿名空函数,就像空对象一样。
construct
方法返回的必须是一个对象,否则会报错,因为是构造函数。
deleteProperty()
deleteProperty
方法用于拦截delete
操作,如果这个方法抛出错误或者返回false
,当前属性就无法被delete
命令删除。
var handler = {
deleteProperty (target, key) {
invariant(key, 'delete');
delete target[key];
return true;
}
};
function invariant (key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
} var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: Invalid attempt to delete private "_prop" property
上面代码中,deleteProperty
方法拦截了delete
操作符,删除第一个字符为下划线的属性会报错。
注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty
方法删除,否则报错。
关于对象的代理操作符在AOP中应该不常用。在OOP中使用应该较多。
其他方法
defineProperty
方法拦截了Object.defineProperty
操作。
getOwnPropertyDescriptor
方法拦截Object.getOwnPropertyDescriptor()
,返回一个属性描述对象或者undefined
。
getPrototypeOf
方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。
Object.prototype.__proto__
Object.prototype.isPrototypeOf()
Object.getPrototypeOf()
Reflect.getPrototypeOf()
instanceof
isExtensible
方法拦截Object.isExtensible
操作。
ownKeys
方法用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
for...in
循环
preventExtensions
方法拦截Object.preventExtensions()
。该方法必须返回一个布尔值,否则会被自动转为布尔值。
这个方法有一个限制,只有目标对象不可扩展时(即Object.isExtensible(proxy)
为false
),proxy.preventExtensions
才能返回true
,否则会报错。
setPrototypeOf
方法主要用来拦截Object.setPrototypeOf
方法。
3.Proxy.revocable()
Proxy.revocable
方法返回一个可取消的 Proxy 实例。用于一次性限制的代理对象。
let target = {};
let handler = {}; let {proxy, revoke} = Proxy.revocable(target, handler); proxy.foo = 123;
proxy.foo // revoke();
proxy.foo // TypeError: Revoked
Proxy.revocable
方法返回一个对象,该对象的proxy
属性是Proxy
实例,revoke
属性是一个函数,可以取消Proxy
实例。上面代码中,当执行revoke
函数之后,再访问Proxy
实例,就会抛出一个错误。
Proxy.revocable
的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
4.this 问题
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this
关键字会指向 Proxy 代理。
此外,有些原生对象的内部属性,只有通过正确的this
才能拿到,所以 Proxy 也无法代理这些原生对象的属性。
const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler); proxy.getDate();
// TypeError: this is not a Date object.
上面代码中,getDate
方法只能在Date
对象实例上面拿到,如果this
不是Date
对象实例就会报错。这时,this
绑定原始对象,就可以解决这个问题。
const target = new Date('2015-01-01');
const handler = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);//把getDate绑定到target
}
return Reflect.get(target, prop);
}
};
const proxy = new Proxy(target, handler); proxy.getDate() //
所谓bind绑定,就是把某个属性或方法挂在到目标对象上。
5.实例:Web 服务的客户端
Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端。
const service = createWebService('http://example.com/data'); service.employees().then(json => {
const employees = JSON.parse(json);
// ···
});
上面代码新建了一个 Web 服务的接口,这个接口返回各种数据。Proxy 可以拦截这个对象的任意属性,所以不用为每一种数据写一个适配方法,只要写一个 Proxy 拦截就可以了。这便是AOP的思想。
function createWebService(baseUrl) {
return new Proxy({}, {
get(target, propKey, receiver) {
return () => httpGet(baseUrl+'/' + propKey);
}
});
}
同理,Proxy 也可以用来实现数据库的 ORM 层。
总结:Proxy的应用:极顶的接口对象和极底的ORM数据库对象。
ES6学习笔记(十)代理器Proxy的更多相关文章
- ES6学习笔记十:模块的导入、导出
一:模块导入 1) import { 要导入的属性.方法民 } from '模块路径'; 2)该种方法需要有配置文件,指明模块所在路径 import { 要导入的属性.方法民 } from '模块名' ...
- es6学习笔记-class之一概念
前段时间复习了面向对象这一部分,其中提到在es6之前,Javasript是没有类的概念的,只从es6之后出现了类的概念和继承.于是乎,花时间学习一下class. 简介 JavaScript 语言中,生 ...
- python3.4学习笔记(十八) pycharm 安装使用、注册码、显示行号和字体大小等常用设置
python3.4学习笔记(十八) pycharm 安装使用.注册码.显示行号和字体大小等常用设置Download JetBrains Python IDE :: PyCharmhttp://www. ...
- python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例
python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...
- java之jvm学习笔记四(安全管理器)
java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...
- java学习笔记13--比较器(Comparable、Comparator)
java学习笔记13--比较器(Comparable.Comparator) 分类: JAVA 2013-05-20 23:20 3296人阅读 评论(0) 收藏 举报 Comparable接口的作用 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(十) indigo Gazebo rviz slam navigation
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 moveit是书的最后一章,由于对机械臂完全不知,看不懂 ...
- ES6学习笔记<五> Module的操作——import、export、as
import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...
- ES6学习笔记<四> default、rest、Multi-line Strings
default 参数默认值 在实际开发 有时需要给一些参数默认值. 在ES6之前一般都这么处理参数默认值 function add(val_1,val_2){ val_1 = val_1 || 10; ...
- ES6学习笔记<三> 生成器函数与yield
为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...
随机推荐
- 项目集成Hudson+SonarQube出现的一个问题
[ERROR] No plugin found for prefix 'sonar' in the current project and in the plugin groups [org.mort ...
- [CQOI2009] 叶子的颜色 解题报告(树形DP)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1304 Description 给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为 ...
- PostgreSQL Replication之第二章 理解PostgreSQL的事务日志(2)
2.2 XLOG和复制 在本章中,您已经了解到PostgreSQL的事务日志已经对数据库做了所有的更改.事务日志本身被打包为易用的16MB段. 使用这种更改集来复制数据的想法是不牵强的.事实上,这是在 ...
- Navicat for Oracle
1.先解压Navicat for Oracle到任意目录 2.将instantclient-basic-nt-12.1.0.2.0解压到1中目录的instantclient_10_2文件夹下(推荐,可 ...
- nginx直接连接操作memcache
原理: Nginx 要设定一个 key 去查 mem ,如果没查到,要有一个回调 php ,并且 key 传给 php. 配置方式: server{ ...
- d3碰撞源码分析
技术 d3. d3.force.d3.geom.quadtree. d3.geom.quadtree 四叉树的应用:图像处理.空间数据索引.2D中的快速碰撞检测.存储稀疏数据等,游戏编程. 上图中的数 ...
- ARM官方《CMSIS-RTOS教程》之线程Threads
创建线程Creating Threads 一旦RTOS开始运行,就会有很多系统调用来管理和控制活跃的线程.默认情况下,main()函数自动被创建为第一个可运行的线程.在第一个例子里我们使用main() ...
- GenIcam标准(五)
2.8.10.Enumeration, EnumEntry Enumeration节点把一个名称(name)映射到一个索引值(index value),并实现Ienumeration接口.Enumer ...
- HDU 3277 Marriage Match III
Marriage Match III Time Limit: 4000ms Memory Limit: 32768KB This problem will be judged on HDU. Orig ...
- 基于ArcGIS Flex API实现动态标绘(1.2)
动态标绘API 1.2,相较前一版本号(点击进入),该版本号新增对基本标绘符号的支持,包含: 单点.多点.折线.手绘线.多边形.手绘多边形.矩形,并提供对应的编辑功能. 例如以下图所看到的,对多点的编 ...