JavaScript:尝试ES6中的代理对象
ES6中的代理(Proxy)对象给了我们自定义对象行为的能力,这个"代理"和网络方面的名词"代理服务器"没有任何关系.你可以先从MDN上了解一些基础知识,再回来看本文中给的例子.
索引严格的数组
在JavaScript中,数组其实就是个对象.因此,在进行一些看似是非法的数组操作时,JavaScript也能正常运行.
比如,读取一个超大的索引:
var arr = []; arr[999999999999999999999999999999999999999999999]; //"undefined"
再比如,新建一个字符串属性(非法索引):
var arr = []; arr["foo"] = 1; arr["foo"] //1
这些操作都不是一个真正的数组应该有的合法操作.下面我们就利用代理来实现一个自定义的对索引类型和范围严格校验的数组类型MyArray :
var MyArray = function () { return new Proxy(Array.apply(null, arguments), { get: function (target, name) { if (name in target || (+name >= 0 && +name < 4294967295)) { return target[name]; } else { throw "非法索引";
} }, set: function (target, name, val) { if (name in target || (+name >= 0 && +name < 4294967295)) { target[name] = val;
} else { throw "非法索引"; } } }) }
尝试执行一下:
var arr = MyArray(1,2,3); //新建一个自定义数组 arr; //[1, 2, 3] arr.push(4); //数组方法也能正常使用 arr; //[1, 2, 3, 4] arr[999999999999] //抛出异常,"非法索引" arr["foo"] = 1 //抛出异常,"非法索引"
"继承"多个原型的对象
在JavaScript中,每个对象都只能有一个直接的上层原型,不能有分支,从而无法实现多重继承.比如:
var foo = {
foo: function () {
console.log("foo")
}
};
var bar = {
bar: function () {
console.log("bar")
}
};
var sonOfFoo = Object.create(foo);
sonOfFoo.foo(); //"foo" var sonOfBar = Object.create(bar);
sonOfBar.bar(); //"bar"
sonOfFooBar = ? //怎么才能既有foo方法又有bar方法?
下面我们就利用代理API生成一个同时"继承"两个原型foo和bar的代理对象:
sonOfFooBar = new Proxy({}, { get: function (target, name) { return target[name] || foo[name] || bar[name]; } }) sonOfFooBar.foo(); //"foo",有foo方法,继承自对象foo sonOfFooBar.bar(); //"bar",也有bar方法,继承自对象bar
任何属性都包含的对象
想要检查一个对象是否包含有某个属性(属性值为undefined也算),我们通常使用in运算符.比如:
0 in [1] //true "a" in {a:"a"} //true
但是我想构建个对象,当in的左操作数为任意值时,它都返回true,这可能吗:
"我是一个随机字符串" in obj //返回true
是的,使用代理对象可以办得到:
var obj = new Proxy({}, { has: function () { return true; } }); 1 in obj; //true "hello" in obj; //true "妈妈说我的名字再长也存在" in obj; //true
当然,这只是表象,其实这个代理对象一个自身属性也没有.
其他
本文只举了set,get,has这三种trap的例子,其实还有其他十几种trap.你可以利用它们实现一些以前不可能实现的效果.本文中的例子都可以在最新版的Firefox中运行.如果你还想进一步了解Proxy的相关知识,ES wiki上有你想要的.