问题描述
我想知道如何克隆完整的jQuery对象。
我尝试使用_.clone()
, _.cloneDeep()
(下划线或破折号),但它不仅是一个对象,还是一个函数。
目的是通过给我使用库的客户提供jQuery的副本,而不是提供引用,因为我不想让他们弄乱我自己的jQuery(库是沙盒的),因此他们不必注入另一个脚本,但只调用我提供的一个功能即可获得可以使用的jQuery。
1楼
Siguza
1
2015-07-30 13:41:37
既然您提到您的代码本身就是一个插件,并且您不想“弄乱” <head>
,那么我假设您也绝不能在任何时候修改原始的$
/ jQuery
,而是加载jQuery实例直接转换为新变量
我想我完全做到了。
以下代码的工作方式如下:
首先,使用XHR获取jQuery代码。
接下来,通过遍历window
所有属性并为函数添加包装器以及为属性添加getter / setter来构造proxy
变量。
try
/ catch
对于静默跳过可能会导致错误的属性是必要的,例如:
Uncaught SecurityError: Failed to read the 'caches' property from 'Window': Cache storage is disabled because the context is sandboxed and lacks the 'allow-same-origin' flag.
还要注意, $
和jQuery
被明确排除。
为了使jQuery不覆盖window.$
和window.jQuery
,我们需要更改window
指向的位置,因为jQuery显式设置了window.$
和window.jQuery
。
但是我们不能更改window
,因为它是只读的。
但是,我们可以做的就是命名一个函数参数window
。
~function(window)
{
/* in here, window == proxy */
}(proxy);
因此,最后,使用proxy
作为参数调用了一个匿名函数,该函数转到该函数内部的window
,最终对jQuery代码进行评估。
var xhr = new XMLHttpRequest(); xhr.open('GET', 'https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js'); xhr.addEventListener('load', function(e) { var proxy = {}; for(var key in window) { if(key !== '$' && key !== 'jQuery') { ~function(k) { try { if(typeof window[k] === 'function') { proxy[k] = function() { window[k].apply(window, arguments); }; } else { Object.defineProperty(proxy, k, { get: function() { return window[k]; }, set: function(val) { window[k] = val; } }); } } catch(error){} }(key); } } ~function(window) { eval(e.target.responseText); }(proxy); $.test = 'meow'; document.write($.test + '<br>'); document.write(proxy.$.test + '<br>'); document.write(JSON.stringify(Object.keys($('body'))) + '<br>'); document.write(JSON.stringify(Object.keys(proxy.$('body'))) + '<br>'); }); xhr.send();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
现在,我认为 proxy
可以像jQuery期望的那样100%工作,但是我不能保证 。
在生产中使用此产品之前,应进行详尽的测试。
但是“实际”克隆呢?
我非常确定没有办法再次加载jQuery。
导出的$
/ jQuery
函数可以访问许多闭包,而任何“局外人” JS代码都无法克隆甚至访问。
“克隆jQuery”的唯一方法是再次调用匿名函数,该函数在其中定义/导出,但该函数不可用。
$.toString()
也没有返回任何用处,因为它涉及许多闭包,并且在eval
时只会抛出大量异常。
先前的答案:
您可以将$
或jQuery
的引用存储在变量中,删除window.$
和window.jQuery
,然后将另一个<script>
标记附加到document.head
,并将src
设置为jQuery文件。
您最终还将获得jQuery的两个副本:
var myQuery = jQuery; delete window.jQuery; delete window.$; var script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js'; script.addEventListener('load', function() { jQuery.test = 'meow'; document.write($.test + '<br>'); document.write(myQuery.test + '<br>'); document.write(JSON.stringify(Object.keys($('body'))) + '<br>'); document.write(JSON.stringify(Object.keys(myQuery('body'))) + '<br>'); }); document.head.appendChild(script);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
甚至提出这个建议都有点痛...但是,当然也可以使用XHR + eval来完成...虽然不说应该这样做。
var myQuery = jQuery; delete window.jQuery; delete window.$; var xhr = new XMLHttpRequest(); xhr.open('GET', 'https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js'); xhr.addEventListener('load', function(e) { eval(e.target.responseText); jQuery.test = 'meow'; document.write($.test + '<br>'); document.write(myQuery.test + '<br>'); document.write(JSON.stringify(Object.keys($('body'))) + '<br>'); document.write(JSON.stringify(Object.keys(myQuery('body'))) + '<br>'); }); xhr.send();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
旧版/ Lodash:
看起来Lodash 无法做到这一点!
Lodash 2.4.2(可能是更低版本)将仅引用任何函数:
var a = function(){}; document.write('aa before setting ba: ' + aa + ' (expected: undefined)<br>'); var b = _.cloneDeep(a); ba = 'a'; document.write('aa after setting ba: ' + aa + ' (expected: undefined)<br>'); document.write('type of a: ' + (typeof a) + ' (expected: function)<br>'); document.write('type of b: ' + (typeof b) + ' (expected: function)<br>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.2/lodash.min.js"></script>
Lodash 3.0.0(以及更高版本,可能是可假定的)乍一看似乎可行,但是直到您意识到函数不会保留函数为止:
var a = function(){}; document.write('aa before setting ba: ' + aa + ' (expected: undefined)<br>'); var b = _.cloneDeep(a); ba = 'a'; document.write('aa after setting ba: ' + aa + ' (expected: undefined)<br>'); document.write('type of a: ' + (typeof a) + ' (expected: function)<br>'); document.write('type of b: ' + (typeof b) + ' (expected: function)<br>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.0.0/lodash.min.js"></script>