在Javascript中访问内部函数变量
例如,在许多框架中,内部函数变量用作私有变量在Javascript中访问内部函数变量,javascript,unit-testing,private-methods,Javascript,Unit Testing,Private Methods,例如,在许多框架中,内部函数变量用作私有变量 Raphael = (function(){ var _private = function(a,b) {return a+b;}; var _public = function(a) {return _private(a,a);} var object = {mult2:_public}; return object; })(); 在这里,我们不能从全局名称空间访问名为private的变量,因为它是第一行中匿名函数
Raphael = (function(){
var _private = function(a,b) {return a+b;};
var _public = function(a) {return _private(a,a);}
var object = {mult2:_public};
return object;
})();
在这里,我们不能从全局名称空间访问名为private
的变量,因为它是第一行中匿名函数的内部变量
有时,这个函数包含一个大的Javascript框架,这样它就不会污染全局名称空间
我需要对Raphael在内部使用的一些对象进行单元测试(在上面的示例中,我希望对对象private
运行单元测试)。我如何测试它们
编辑:我收到了关于单元测试的评论,单元测试应该测试公共接口
让我指定一个用例。我正在写一个名为拉斐尔的库。这个库应该只向全局名称空间添加一个名称,而不是其他名称。这是Javascript的特殊要求,因为Javascript没有名称空间
让我们假设Raphael
使用一个链表。如果Javascript有包的概念,我会这么做
require 'linked_list'
Raphael = (function(){/* use linked list */})();
然而,Javascript不允许我以任何不会污染链表对象的全局范围的方式来做这件事!因此,我必须将链表
内联到拉斐尔的局部范围中:
Raphael = (function(){
/* implement linked list */
var linked_list = function(){/*implementation*/};
})();
现在我想测试链表的实现。试试这个:
var Raphael;
var test = true; //or false;
Raphael = (function(){
var private = function(a,b) {return a+b;};
var public = function(a) {return private(a,a);}
var object = {mult2:public};
if (test) Raphael.private = private;
return object;
})();
var adder = function(a,b) {
return a + b;
}
Raphael = function(fn){
var _private = function(a,b) {
fn(a,b);
}
var _public = function(a) {
return _private(a,a);
}
var object = {doubleIt: _public};
return object;
}(adder);
只需一点函数注入您仍然没有抓住要点 单元测试的目的是验证对象的公共接口是否符合预期。单元测试展示了代码是如何工作的 唯一需要测试的是对象的公共接口。这样,当开发人员想要更改对象的实现方式时,您只需担心被测试的对象是否仍能达到预期效果 如果您觉得该闭包中的对象需要测试,那么就进行测试,但在外部进行测试,然后将其传递到闭包中
var Raphael= function(listIterator) {
listIterator.method();
}(new ListIterator());
下面所示的虚假黑客是完全不合适的(在单元测试或任何地方)
测试函数应该简单,只测试一件事,并且有一个断言。这通常发生在三到十行测试代码中
当您的测试函数变得复杂时,因为它们将遵循您所询问的方法,那么
(1) 认识到你的设计可能不是你想要的,并改变它,使之成为现实,或者(2)在测试中改变你的期望
关于您发布的代码,您忘记了var
,漏掉了一个分号,并使用了两个保留字作为标识符:private
和public
不使用var
的后果是有可能触发与非标准globalScopeInnoiser
类型对象(“对象不支持此属性或方法”见IE)的各种实现相关的错误和问题。使用FutureReservedWord的结果是语法错误
。实现可能会提供一个语法扩展,以允许FutureReservedWord作为标识符,事实上很多实现都是这样,但是最好不要依赖这些扩展,如果出现错误,那完全是您的错
您提到了向用户交付代码。我建议你在获得更多的经验和理解之前不要这样做
// DO NOT USE THIS CODE.
var Raphael = (function(){
var _private = function(a,b) {return a+b;};
var _public = function(a) {return _private(a,a);};
var object = {mult2:_public};
return object;
})();
var leakedFunction;
// Spurious hack:
// Give valueOf a side effect of leaking function.
// valueOf is called by the _private function as a
// side effect of primitive conversion, where
// ToPrimitive(input argument, hint Number) results
// in calling valueOf.
function valueOfSnoop(){
leakedFunction = leakedFunction || valueOfSnoop.caller || function(){};
return 2;
}
var a = {
valueOf : valueOfSnoop
};
Raphael.mult2(a, 3);
var privateMathod = leakedFunction;
alert(leakedFunction(1, 2));
那个示例代码只是为了证明这样的事情是可能的。考虑到选择,它是前面提到的替代方案的一个糟糕的替代方案;要么更改设计,要么更改测试 我想出的最佳解决方案是: 在源代码中使用Javascript文件
Raphael = (function(){
// start linked_list
var linked_list = function() {/*...*/};
// end linked_list
var object = {mult2:_public};
return object;
})();
现在,使用一个脚本来提取//开始([a-zA-Z]*)
和//结束([a-zA-Z]*)
之间的对象,并对提取的代码进行单元测试
显然,从外部作用域访问函数内部作用域中的变量是不可能的。正如Jason在评论中链接到的SO问题中所述。unittests背后的思想是测试公共函数/方法only@Andrey,Javascript不是你的日常语言!您无法以正常的方式导入其他模块,因此如果我将包含一个小的(比如)链表实现供Raphael使用,那么它必须内联到Raphael的内部函数中,这样它就不会向Raphael的用户公开。我不能只
#包含它
请参见:等等,当我将库交付给用户时,如何取消“测试”代码,#ifdef
;-)是的,您必须在文件的顶部声明测试变量或其他什么。在函数体中使用private
作为变量的整个想法是为了不污染全局范围。在您的解决方案中,Raphael
用adder
污染了全局范围。如果您打算使用“私有属性”,您必须有一个传感变量/函数来测试私有属性。或者你必须坚持使用公共界面。此外,您还可以向代码中添加另一个名称空间层,并将其私有化,而不是其所在位置。单元测试较低级别,然后在新层API上运行集成测试。@Garrett,声明函数(){}而不仅仅是{}的任何原因。我喜欢!通常在声明var
时,我喜欢只为防御编码而对类型进行编码代码>-我这样做是因为leakedFunction
稍后会被调用。但只有在您使用此参数重新分配它之后。callee.caller
。是吗?嗯,是的,leakedFunction被设置为
arguments.callee.caller`insidevalueOf
。我这样做是为了防止function.caller或arguments.callee不可用。任务应该在里面,我可以重用一个函数。。。编辑。。。