7.3 对象充当识别器
说明:
用一个对象来充当函数调用界面上的,或针对特殊成员的识别器。
示例背景:
该技巧利用了对象实例在全局唯一性的特性(包括直接量空白对象“{}!=={}”)。
我们在一个函数闭包内声明的局部变量,是不会被外部代码直接访问到的,但能在该闭包中、更内层的子函数闭包中通过upvalue访问。因此我们可以用该局部变量作为一个识别器,来辨识函数是在内部亦或外部调用。
除了在函数调用界面上的识别外,我们也可以利用这种技巧来弥补in运算不能有效甄别对象内部成员的问题。
示例代码:
142 /**
143 * 1. 用作函数调用界面上的识别器
144 */
145 var myFunc = function() {
146 var handle = {};
147 function _myFunc(x,y,z) {
148 if (arguments[0] === handle)
149 // 是内部调用...
150 else
151 // 是外部调用...
152 }
153
154 // 内部调用测试(可传入更多参数)
155 _myFunc(handle, 1, 2, 3);
156
157 return _myFunc;
158 }
159 // 外部调用测试(不能访问变量handle)
160 myFunc(1, 2, 3);
161
162 /**
163 * 2. 用作对象成员识别器
164 */
165 var obj = function() {
166 var yes = {};
167 var tbl = { v1: yes, v2: yes, v3: yes };
168 return {
169 query: function(id) { return tbl[id] === yes }
170 }
171 }();
172 // 测试: 查询指定id是否存在
173 obj.query('v1');
174 obj.query('constructor');
示例说明:
与示例1相同的模式,被应用在Qomo的切面与多投事件特性的实现中。在这两个Qomo特性中,内部函数_myFunc()掌握着一些特殊的信息(例如更内部的变量或受保护的信息)。一方面,在某些有关_myFunc()的内部调用时需要索取该信息,另一方面,又要避免它的外部引用myFunc()在调用时索取该信息。这种情况下,示例1使用一个内部或外部调用的识别器handle来识别函数调用的上下文环境。
另一个识别函数调用的上下文环境的方法,是直接检测arguments.callee.caller以判定是否来自某个函数调用。这种方法在Qomo中也有应用,但它不适合对批量调用者的识别,另外也存在限制:调用者函数的标识符必须能被_myFunc()访问。
示例2被应用在Qomo的切面系统的实现中检测某个标识名是否存在。示例2创建的内部检索表tbl是一个对象,而不是使用通常的索引数组。对对象的成员做检测的方法,通常是使用in运算。但是in运算并不排除construtor这种基本属性,以及通过原型继承来的属性,因此往往要花大量代码消除误判。在本示例中,通过识别器变量yes,能有效地避免这些问题,并具有与in运算相同的性能。此外,在tbl表声明时,可以覆盖任何内部的或继承自原型的属性,这不会使运算效率或准确性受到影响。
本技巧在检测handle与yes等识别器变量时,必须使用“===”运算符。





