es 6 javascriptのProxyの実例の方法

12456 ワード

1 get()
getメソッドは、ある属性の読み取り動作をブロックするために使用されます.上記ではもう一つの例がありますが、次は他のブロック読取動作の例です.
var person = {
	name: "    "
};
var proxy = new Proxy(person, {
	get: function(target, property) {
		if(property in target) {
			return target[property];
		} else {
			throw new ReferenceError("Property \"" + property + "\" does not exist.");
		}
	}
});
proxy.name // "    "
proxy.age //        
上のコードによると、訪問先のオブジェクトに存在しない属性があると、エラーが発生します.このブロッキング関数がないと、存在しない属性にアクセスし、undefinedに戻ります.
get方法は継承できる.
let proto = new Proxy({}, {
	get(target, propertyKey, receiver) {
		console.log('GET ' + propertyKey);
		return target[propertyKey];
	}
});
let obj = Object.create(proto);
obj.xxx // "GET xxx"
上のコードでは、ブロック動作はProttypeオブジェクトの上に定義されているので、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);
		}
	};
	let target = [];
	target.push(...elements);
	return new Proxy(target, handler);
}
let arr = createArray('a', 'b', 'c');
arr[-1] // c
上のコードでは、配列の位置パラメータは−1であり、配列の最後のメンバが出力されます.
Proxyを利用して、読み取り属性の操作(get)を、ある関数を実行することに変え、属性のチェーン操作を行うことができます.
var pipe = (function() {
	return function(value) {
		var funcStack = [];
		var oproxy = new Proxy({}, {
			get: function(pipeObject, fnName) {
				if(fnName === 'get') {
					return funcStack.reduce(function(val, fn) {
						return fn(val);
					}, value);
				}
				funcStack.push(window[fnName]);
				return oproxy;
			}
		});
		return oproxy;
	}
}());
var double = n => n * 2;
var pow = n => n * n;
var reverseInt = n => n.toString().split("").reverse().join("") | 0;
pipe(3).double.pow.reverseInt.get; // 63
上のコードはProxyを設定してから、関数名のチェーンを使用する効果があります.
以下の例は、getブロックを利用して、様々なDOMノードを生成する汎用関数domを実現することである.
const dom = new Proxy({}, {
	get(target, property) {
		return function(attrs = {}, ...children) {
			const el = document.createElement(property);
			for(let prop of Object.keys(attrs)) {
				el.setAttribute(prop, attrs[prop]);
			}
			for(let child of children) {
				if(typeof child === 'string') {
					child = document.createTextNode(child);
				}
				el.appendChild(child);
			}
			return el;
		}
	}
});
const el = dom.div({},
	'Hello, my name is ',
	dom.a({
		href: '//example.com'
	}, 'Mark'),
	'. I like:',
	dom.ul({},
		dom.li({}, 'The web'),
		dom.li({}, 'Food'),
		dom.li({}, '…actually that\'s it')
	)
);
document.body.appendChild(el);
2セット()
setメソッドは、ある属性の割当動作をブロックするために使用されます.Personオブジェクトが一つのage属性を持つと仮定すると、この属性は200以下の整数であるべきであり、Proxyを使用して、ageの属性値が要求に適合することを保証することができる.
let validator = {
	set: function(obj, 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');
			}
		}
		//     age      ,    
		obj[prop] = value;
	}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' //    
person.age = 300 //    
上のコードでは、保存関数setが設定されているため、要求されたage属性の割り当てに適合していない場合、エラーが発生します.set法を用いて、DOMはオブジェクトが変化するたびに自動的に更新されるデータバインディングも可能である.
場合によっては、オブジェクトの上に内部属性を設定します.属性名の最初の文字は下線の先頭を使います.これらの属性は外部で使うべきではないことを表します.getとset方法を組み合わせると、これらの内部属性が外部に読み書きされるのを防ぐことができます.
var handler = {
	get(target, key) {
		invariant(key, 'get');
		return target[key];
	},
	set(target, key, value) {
		invariant(key, 'set');
		return true;
	}
};

function invariant(key, action) {
	if(key[0] === '_') {
		throw new Error(`Invalid attempt to ${action} private "${key}" property`);
	}
}
var target = {};
var proxy = new Proxy(target, handler);
proxy._prop
	// Error: Invalid attempt to get private "_prop" property
proxy._prop = 'c'
	// Error: Invalid attempt to set private "_prop" property
	// Error: Invalid attempt to set private "_prop" property
上のコードでは、読み書きの属性名の最初の文字が下線である限り、すべて誤って投げられ、内部属性の読み書きを禁止する目的があります.
3 appy()
applyメソッドブロック関数の呼び出し、call、apply操作.
var handler = {
	apply(target, ctx, args) {
		return Reflect.apply(...arguments);
	}
};
apply方法は、ターゲットオブジェクト、ターゲットオブジェクトのコンテキストオブジェクト、およびターゲットオブジェクトのパラメータ配列の3つのパラメータを受け入れることができる.
次は一例です.
var target = function() {
	return 'I am the target';
};
var handler = {
	apply: function() {
		return 'I am the proxy';
	}
};
var p = new Proxy(target, handler);
p()
	// "I am the proxy"
上のコードでは、変数pはProxyの例であり、関数として呼び出されると(p()、appy方法によってブロックされ、文字列を返します.
次は別の例です.
var twice = {
	apply(target, ctx, args) {
		return Reflect.apply(...arguments) * 2;
	}
};

function sum(left, right) {
	return left + right;
};
var proxy = new Proxy(sum, twice);
proxy(1, 2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30
上のコードの中で、proxy関数(直接呼び出しまたはコールとappy呼び出し)を実行するたびに、apply方法によってブロックされます.
また、Reflect.applyメソッドを直接呼び出しても、ブロックされます.
Reflect.apply(proxy, null, [9, 10]) // 38
4 has()
hasメソッドは、Has Property操作をブロックするために用いられます.つまり、オブジェクトがある属性を持つかどうかを判断するときに、この方法が有効になります.典型的な動作は、in演算子です.下記の例では、has法を使用して、いくつかの属性を隠します.in演算子によっては発見されません.
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演算子によって発見されない.
元のオブジェクトが拡張機能を設定できない場合、または禁止されている場合、エラーが発生します.
var obj = {
	a: 10
};
Object.preventExtensions(obj);
var p = new Proxy(obj, {
	has: function(target, prop) {
		return false;
	}
});
'a' in p // TypeError is thrown
上のコードの中で、objオブジェクトは拡張を禁止していますが、結果としてhasブロッキングを使うとエラーが発生します.
注意すべきなのは、has方法でブロックされているのはHasOwnProperty操作ではなく、Has方法では、一つの属性が対象自身の属性か、それとも継承の属性かを判断しないということです.
for…in操作内部にもHas Property操作が使用されますので、has方法はfor…inサイクル時にも有効です.
let stu1 = {
	name: 'Owen',
	score: 59
};
let stu2 = {
	name: 'Mark',
	score: 99
};
let handler = {
	has(target, prop) {
		if(prop === 'score' && target[prop] < 60) {
			console.log(`${target.name}      `);
			return false;
		}
		return prop in target;
	}
}
let oproxy1 = new Proxy(stu1, handler);
let oproxy2 = new Proxy(stu2, handler);
for(let a in oproxy1) {
	console.log(oproxy1[a]);
}
// Owen
// Owen     
for(let b in oproxy2) {
	console.log(oproxy2[b]);
}
// Mark
// Mark 99
上のコードの中で、for...inサイクルの時に、hasブロッキングは有効になります.要求に合わない属性はfor...inサイクルの外に排除されます.
5 construct()
constructメソッドはnewコマンドをブロックするために使用されます.以下はブロック対象の書き方です.
var handler = {
	construct(target, args, newTarget) {
		return new target(...args);
	}
};
construct方法は、2つのパラメータを受け入れることができる.
ターゲットオブジェクト
args:関数を構築するパラメータオブジェクト
次は一例です.
var p = new Proxy(function() {}, {
	construct: function(target, args) {
		console.log('called: ' + args.join(', '));
		return {
			value: args[0] * 10
		};
	}
});
new p(1).value
	// "called: 1"
	// 10
construct            ,      。
var p = new Proxy(function() {}, {
	construct: function(target, argumentsList) {
		return 1;
	}
});
new p() //    
6 deleteProperty()
var handler = {
	deleteProperty(target, key) {
		invariant(key, 'delete');
		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オペレータをブロックして、最初の文字が下線である属性を削除するとエラーが発生します.
7 defineProperty()
defineProperty方法はObject.defineProperty操作をブロックしました.
var handler = {
	defineProperty(target, key, descriptor) {
		return false;
	}
};
var target = {};
var proxy = new Proxy(target, handler);
proxy.foo = 'bar'
	// TypeError: proxy defineProperty handler returned false for property '"foo"'
上のコードの中で、defineProperty方法はfalseに戻り、新しい属性を追加するとエラーが発生します.
8 getOwn PropertyDescripter()
getOwnPropertyDescriptor方法はObject.getOwn PropertyDescriptorをブロックして、属性記述オブジェクトまたはundefinedを返します.
var handler = {
	getOwnPropertyDescriptor(target, key) {
		if(key[0] === '_') {
			return;
		}
		return Object.getOwnPropertyDescriptor(target, key);
	}
};
var target = {
	_foo: 'bar',
	baz: 'tar'
};
var proxy = new Proxy(target, handler);
Object.getOwnPropertyDescriptor(proxy, 'wat')
	// undefined
Object.getOwnPropertyDescriptor(proxy, '_foo')
	// undefined
Object.getOwnPropertyDescriptor(proxy, 'baz')
	// { value: 'tar', writable: true, enumerable: true, configurable: true }
上のコードでは、handle.getOwn PropertyDescriptorメソッドは、最初の文字が下線である属性名に対してundefinedを返します.
9 get ProttotypeOf()
get ProttotypeOf方法は、Object.get ProttypeOf()演算子をブロックするために主に使用され、他のいくつかの操作があります.
Object.prototype.__proto__
Object.prototype.isPrototypeOf()
Object.getPrototypeOf()
Reflect.getPrototypeOf()
instance of演算子
次は一例です.
var proto = {};
var p = new Proxy({}, {
	getPrototypeOf(target) {
		return proto;
	}
});
Object.getPrototypeOf(p) === proto // true
上のコードの中で、get ProttypeOfメソッドはObject.get ProttypeOfをブロックして、protoオブジェクトに戻ります.
10 isExtensioble()
isExtensioble方法はObject.isExtenssibleの操作をブロックします.
var p = new Proxy({}, {
	isExtensible: function(target) {
		console.log("called");
		return true;
	}
});
Object.isExtensible(p)
	// "called"
	// true
上のコードはisExtenssible方法を設定しています.Object.isExtenssibleを呼び出した時にcaledを出力します.
この方法には強い制限があります.次の条件を満たせないと、エラーが発生します.
Object.isExtensible(proxy) === Object.isExtensible(target)
//       。
var p = new Proxy({}, {
	isExtensible: function(target) {
		return false;
	}
});
Object.isExtensible(p) //    
11 ownKeys()
ownKeysメソッドはObject.keys()操作をブロックするために使用されます.
let target = {};
let handler = {
	ownKeys(target) {
		return ['hello', 'world'];
	}
};
let proxy = new Proxy(target, handler);
Object.keys(proxy)
	// [ 'hello', 'world' ]
上のコードは、targetオブジェクトのObject.keys()動作をブロックし、予め設定された配列を返す.
次の例は、最初の文字を傍受する属性名です.
let target = {
	_bar: 'foo',
	_prop: 'bar',
	prop: 'baz'
};
let handler = {
	ownKeys(target) {
		return Reflect.ownKeys(target).filter(key => key[0] !== '_');
	}
};
let proxy = new Proxy(target, handler);
for(let key of Object.keys(proxy)) {
	console.log(target[key]);
}
// "baz"
12 prevent Extensions()
prevent Extensions方法はObject.prevent Extensions()をブロックします.この方法はブール値を返さなければなりません.この方法にはObject.isExtensiobleがfalseである場合に限って、proxy.prevent Extensionsはtrueに戻ります.そうでないとエラーが発生します.
var p = new Proxy({}, {
	preventExtensions: function(target) {
		return true;
	}
});
Object.preventExtensions(p) //    
上記のコードの中で、proxy.prevent Extensions方法はtrueに戻りますが、Object.isExtensiobleはtrueに戻りますので、エラーが発生しました.
この問題を防止するために、proxy.prevent Extensionsの方法でObject.prevent Extensionsを呼び出します.
var p = new Proxy({}, {
	preventExtensions: function(target) {
		console.log("called");
		Object.preventExtensions(target);
		return true;
	}
});
Object.preventExtensions(p)
	// "called"
	// true
13 set ProttotypeOf()
set ProttotypeOf方法は主にObject.set ProttotypeOfメソッドをブロックするために用いられます.次は一例です.
var handler = {
	setPrototypeOf(target, proto) {
		throw new Error('Changing the prototype is forbidden');
	}
};
var proto = {};
var target = function() {};
var proxy = new Proxy(target, handler);
proxy.setPrototypeOf(proxy, proto);
// Error: Changing the prototype is forbidden
上のコードの中で、targetのプロトタイプのオブジェクトを修正すると、エラーが発生します.