我有这个对象,类似于谷歌分析的第三方跟踪工具。我希望使用我自己的“缓存”功能扩展它,该功能可以保存先前跟踪调用的数据,以便在需要时可以在下一个跟踪调用中引用内容。
这是我到目前为止所做的工作:
// Current 3rd party tool, can't really mess with this.
// It is loaded from an external script
window.someTool={/* stuff */};
// my code
someTool._cache=someTool._cache||{};
someTool._cache._get=function(variabl) {
var length,index,variabl=(variabl||'').split('.'),
cache=someTool&&someTool._cache&&someTool._cache._dataLayer||{};
for (index=0,length=var.length;index<length;index++){
cache=cache[variabl[index]];
if (!cache) break;
}
return cache;
};
然后我有/做以下
// data layer output on initial page that gets wiped later
var dataLayer = {
'page' : {
'name' : 'foo',
'lang' : 'en'
},
'events' : {
'pageView' : true,
'search' : true
}
}
// I grab the initial data layer and save it here
someTool._cache._dataLayer = dataLayer;
这允许我做像
这样的事情someTool._cache._get('page'); // returns {'page':{'name':'foo','lang':'en'}
someTool._cache._get('page')['name']; // returns 'foo'
someTool._cache._get('page.lang'); // returns 'en'
所以这对我有用,但问题/目标来了:我想改进我的_get
功能。也就是说,我不喜欢我必须硬编码someTool
,或者甚至是_cache
,如果我能以某种方式摆动它,_dataLayer
。
理想情况下,我想要someTool._cache._dataLayer
传递/公开_get
(例如parent
类型参考)的引用,以便someTool
,{{1} }或_cache
要更改名称空间,我不必更新_dataLayer
。 但我不知道该怎么做。
这是我到目前为止所做的:
_get
这似乎适用于我称之为的第一次时间,例如
(function(tool, cache, dataLayer) {
var tool = tool || {},
cache = cache || '_cache',
dataLayer = dataLayer || '_dataLayer';
dataLayer = tool[cache][dataLayer] || {};
tool[cache]._get = function(property) {
var length, index, property = (property || '').split('.');
for (index = 0, length = property.length; index < length; index++) {
dataLayer = dataLayer[property[index]];
if (!dataLayer) break;
}
return dataLayer;
};
})(someTool, '_cache', '_dataLayer');
//返回'foo'
但在那之后,我收到了一个错误:
someTool._cache._get('page')['name'];
我觉得它与TypeError: someTool._cache._get(...) is undefined
失去了它的引用或其他东西有关,我不知道(虽然我不确定它是如何工作的第一次......)。我正在做什么甚至可能,如果是这样,我哪里出错了?或者是我最初能做的最好的事情?
答案 0 :(得分:1)
我觉得它与
dataLayer
失去了它的参考或其他东西有关,我不知道(虽然我不确定它是如何在第一次工作......)。
发生这种情况的原因是因为您在dataLayer
的关闭中使用了相同的_get
来初始化:
如果你看一下你的代码:
(function(tool, cache, dataLayer) {
// ...
// Here you are initializing (or looking up) the dataLayer
dataLayer = tool[cache][dataLayer] || {};
tool[cache]._get = function(property) {
// ...
for (index = 0, length = property.length; index < length; index++) {
// here you are overwriting the same dataLayer
dataLayer = dataLayer[property[index]];
if (!dataLayer) break;
}
return dataLayer;
};
})(someTool, '_cache', '_dataLayer');
您可以看到您的循环将在每次迭代时覆盖dataLayer
,这意味着在第一次迭代之后的每次查找都很可能是错误的。
最终,dataLayer
将被undefined
覆盖,然后任何进一步的查找都会破坏代码。
您可以做的是使用另一个变量进行循环迭代:
var temp;
for (index = 0, length = property.length; index < length; index++) {
temp = dataLayer[property[index]];
if (!temp) break;
}
return temp;
这将使您的dataLayer
对象保持不变。
答案 1 :(得分:0)
虽然你的代码是如此有意义(一个字符的变量名,滥用逗号运算符等),但很难确定,似乎你需要在继续之前解决一些问题。
以下划线为前缀的属性应为私有。它们可能会发生变化,而变化则意味着您的应用随意破坏。 使用公共API 。
手工解析字符串是一项看似微不足道的工作。 get('page.id')
超过get('page').id
的用例真的如此引人注目吗?
您的代码难以理解。这是一种人们对缩小器的期望输出:它使得很难理解它/它应该做什么。
除非第三方API对您的应用程序如此不可或缺,否则替换它将需要重写,无论是什么(例如谷歌地图)或如此众所周知它有无数克隆(jquery),它通常是最好包装第三方库调用,以便以后可以更改库。
我意识到这并没有回答你的问题,但它的评论时间太长了,如果不指出你在抛光前涂在脚上的鲜红色目标(复数),那将是我的疏忽你的枪支。
至于你的实际问题(编辑后),你正走在正确的轨道上。但是我将它作为一个curried函数,以便您可以动态访问不同的属性。我们将忽略一分钟正在访问私有财产的巨大错误只是为了明白这一点:
function accessDataCache(cache) {
return function(dataLayer) {
return function(namespaceObj) {
return function(property) {
return namespaceObj[cache][dataLayer][property];
};
};
};
};
var getFn = accessDataCache('_cache')('_dataLayer')(someTool);
getFn('page');
如果您需要其他东西,现在也可以混合搭配:
var getSomeOtherCachedThing = accessDataCache('_cache')('_someOtherThing')(someTool);
所有这些都是手工写出来的非常繁琐,所以我建议使用像lodash或Ramda和.curry
之类的东西来实现这个效果:
var accessCacheData = R.curry(function(cache, dataLayer, namespaceObj, property) {
return namespaceObj[cache][dataLayer][property];
});