问题描述
我已经使用了模块模式了一段时间,但是最近开始希望将功能和属性混合在一起以增加代码重复使用.我已经阅读了有关该主题的一些良好资源,但对于最佳方法仍然有些不确定.这是一个模块:
var myModule = function () { var privateConfigVar = "Private!"; //"constructor" function module() {} module.publicMethod = function () { console.log('public'); } function privateMethod1() { console.log('private'); } return module; }
这是一个混合对象:
var myMixin = function () {}; Mixin.prototype = { mixinMethod1: function () { console.log('mixin private 1'); }, mixinMethod2: function () { console.log('mixin private 2'); } };
理想情况下,我想从其他对象作为私有方法和某些作为公共方法中混合一些方法,以便我可以将一些"扩展"函数称为"扩展"函数,而参数为"私有"/" public".所以,
mixin(myModule, myMixin, "private");
通过调用MixInmethod1()并具有正确的范围,并且:
,使mymixin方法可在myModule内使用.mixin(myModule, myMixin, "public");
通过调用module.mixinmethod1()使MyMixin方法可在myModule内使用,并具有正确的范围
我尝试使用一种将属性从一个原型复制到另一个原型的方法,我尝试了下划线扩展方法将对象的属性从一个到另一个,以及之间的各种事物.我认为此时我对范围和原型有些转折,并且希望如何在使用模块模式时如何最好地进行像这样的混合物.请注意,对象mymixin的外观无关紧要(无论是将函数添加到原型还是模块本身),我只是想找出某种方法来使其正常工作.
谢谢!
推荐答案
,即[某些代码]仅通过调用MixInmethod1()并具有正确的范围
,可以在myModule中提供mymixin方法.
这是不可能的.您无法通过调用功能来修改范围,尤其是从外部进行的.另请参见是否有可能在JavaScript中import变量?出于设计原因.
那么,您做什么可以做什么?
从模块外部
module函数的私有范围没有任何内容.显然,您不能使用模块的私人功能.您可以使用方法扩展其原型(最常见),甚至可以装饰其构造函数.在其中,您可以使用自己的私人功能,即完全静态的功能或特定于类的功能.
var myMixin = (function() { // everything class-unspecific but mixin-local var staticMixinVariables, …; function globalPrivateFunction(){…} function staticMethod(){…} return function(mod) { // everything class-specific // also using the locals from above mod.staticHelper = function() { staticMixinVariable … }; mod.prototype.mixinMethod1 = staticMethod; mod.prototype.mixinMethod2 = function(){…}; … }; })(); // Example: myMixin(SomeClass)
从模块中
在模块代码本身中使用混合蛋白可以允许更大的灵活性.
var myMixin = (function() { // everything class-unspecific but mixin-local … return { publicHelper1: function(){…}, publicHelper2: function(){…}, decorateInstance: function(o) { o.xy = …; }, extendPrototype: function(proto) { // everything class-specific // also using the locals from above proto.mixinMethod1 = staticMethod; proto.mixinMethod2 = function(){…}; … } }; })();
使用这种接口,很容易构造一个将其用作混合蛋白的类(不从中继承):
var myClass = (function() { function Constructor() { myMixin.decorateInstance(this); … } Constructor.prototype.method1 = function() { myMixin.publicHelper1() … }; Constructor.prototype.method2 = function() { … }; myMixin.extendPrototype(Constructor.prototype); Constructor.myHelper = myMixin.publicHelper2; // re-export explicitly return Constructor; })();
但是,混合蛋白将永远无法访问私人类变量,也无法提出特定于类的私人API.尽管如此,我们可以使用依赖注入来明确提供该访问(并有效使用Mixin工厂):
var myClass = (function() { var … // private class functions and variables var mixer = myMixin(privateClassHelper, privateClassVariable, function setPrivateVar(x) {…}, … ); var myHelper = mixer.customHelper, … // local "aliases" function Constructor(localX) { mixer.decorateInstance(this, localX); … } … // further using the class-specific private mixer return Constructor; })();
并非上面显示的所有技术都需要在每种混合物中使用,只需选择所需的技术即可.并非所有可能的技术都在上面的示例中显示:-) Mixin模式也可以应用于普通模块或其内部声明中,上面的示例仅显示了具有原型的类.
有关一些好的例子,以及(无状态)特征,(状态)Mixins及其"特权"对应物之间的理论区别,请查看此演示文稿.
其他推荐答案
with关键字对于定义范围非常有用,但它也有一些缺点(在严格模式下禁止禁止).
使用with关键字,您可以在模块正文内定义一个私有变量privateScope,该变量将包含您所有证明的方法:
var myModule = function () { var privateConfigVar = "Private!"; var privateScope = {}; //"constructor" function module() {} var proto = module.prototype;//avoids multiple attribute lookup //Let's re-define you example' private method, but with a new strategy privateScope['privateMethod1'] = function() { console.log('private'); } proto.publicMethod = function () { with(privateScope){ //this call should work privateMethod1(); } console.log('public'); } proto.publicMethod2=function(name,fn){ with(privateScope){ //this will be defined later by a Mixin otherPrivateMethod(); } console.log('public2'); } proto.definePrivateFunction=function(name,fn){ privateScope[name] = fn; } return module; }
您的Mixin将使用definePrivateFunction我们刚刚定义为私人范围添加私人方法:
//An example mixin implementation function Mixin(source,target,flag){ if(flag==="private"){ for(var currentMethodName in source){ target.definePrivateFunction(currentMethodName,source[currentMethod]) } }else{ for(var currentMethodName in source){ target[currentMethodName]=source[currentMethod]; } } }
以下代码应正常工作:
var test = myModule(); var testInstance = new test(); testInstance.publicMethod();// will call the private method defined internally Mixin({ otherPrivateMethod:function(){ console.log("other Prvate Method called") } },test.prototype,"private"); testInstance.publicMethod2();// will call the private method defined by the mixin
其他推荐答案
理想情况下,我想从其他对象作为私有方法和某些作为公共方法中混合一些方法,以便我可以将一些"扩展"函数称为"扩展"函数,而参数为"私有"/" public". ...
正如已经提到的那样,无法准确实现这一目标.
so,那就是...通过调用mixInmethod1()并具有正确的范围,使mymixin方法可在myModule内使用,并且:...使mymixin方法通过调用mymodule中可用,并通过调用module.mixinmethod1()并具有正确的范围. .
并参考范围 ...这是函数创建的封闭地址空间. 除 closure s,范围仅在函数运行时可用 在这个功能的身体中.它永远不会被操纵/欺骗.
一词正在寻找的是上下文. JavaScript,在许多方面都有很高的高度 动态,建立在较晚绑定的基础上(对象/目标/上下文一种方法称为 在运行时进行评估/查找)和两种代表团. 上下文通过"行走原型链"自动委派委派 或通过每个函数对象都提供的两种呼叫方法之一明确地 - call或apply.
因此,JavaScript已经在语言核心级别确实提供了基于函数的 混合图案比任何可用的extend(s)或mixin都更强大 它的实施为免费提供委派,并能够通过 围绕 state ,几乎每个指责的助手都缺乏,除非那里 是以相当回旋的方式再次实施此功能的努力 (或坦率地放置屁股).
bergi 为他的解释已经赢得了赏金. 在他的回答的最后一段中,有一个指向我的资源的链接 发表推荐的谈话后已经过了3个月.由于没有 声誉足够多,我无法直接评论他的答案.为了这 我现在将机会指向我个人研究的最新状态, understanding of »»JavaScript的许多才华,用于概括角色导向的编程方法,例如性状和Mixins«
再次回答OP的问题.
我将从假定的模块模式中更改两个给定的代码示例 并且相当示例地提供了用于普通构造函数的混合代码基础 与此同时,我很想按顺序称为"代理"和/或"比科文化"混合物 沸腾了一次委派两个不同的目标/上下文对象的机制. 因此展示了可能最接近什么的基于纯函数的混合蛋白模式 OP尝试实现.
var MyBicontextualMixin = function (localProxy) { localProxy.proxifiedAccessible = function () { console.log("proxified accessible."); }; this.publiclyAccessible = function () { console.log("publicly accessible."); }; }; var MyConstructor = function () { var localProxy = {}; MyBicontextualMixin.call(this, localProxy); var locallyAccessible = localProxy.proxifiedAccessible; // call 'em locallyAccessible(); // "proxified accessible." this.publiclyAccessible(); // "publicly accessible." }; (new MyConstructor); // will log: // // proxified accessible. // publicly accessible.
这种特殊模式也是组成纯的基础 基于函数的特征依靠冲突解决功能 由"定位"混合物提供的,不会暴露此功能 公开.
并且不结束理论上会有一个"现实世界的例子", 由各种可重复使用的混合物组成Queue模块 崇拜干的方法.它还应该回答OP的问题 如何实现封装和 exposition 仅建立在模块上 基于模式和功能的混合蛋白组成.
var Enumerable_first_last_item = (function (global) { var parseFloat = global.parseFloat, math_floor = global.Math.floor, // shared code. first = function () { return this[0]; }, last = function () { return this[this.length - 1]; }, item = function (idx) { return this[math_floor(parseFloat(idx, 10))]; } ; return function () { // [Enumerable_first_last_item] Mixin. var enumerable = this; enumerable.first = first; enumerable.last = last; enumerable.item = item; }; }(window || this)); var Enumerable_first_last_item_proxified = function (list) { Enumerable_first_last_item.call(list); // implementing the proxified / bicontextual [Enumerable_first_last_item] Mixin. var enumerable = this; enumerable.first = function () { return list.first(); }; enumerable.last = function () { return list.last(); }; enumerable.item = function (idx) { return list.item(idx); }; }; var Allocable = (function (Array) { var array_from = ((typeof Array.from == "function") && Array.from) || (function (array_prototype_slice) { return function (listType) { return array_prototype_slice.call(listType); }; }(Array.prototype.slice)) ; return function (list) { // proxified / bicontextual [Allocable] Mixin. var allocable = this ; allocable.valueOf = allocable.toArray = function () { return array_from(list); }; allocable.toString = function () { return ("" + list); }; allocable.size = function () { return list.length; }; Enumerable_first_last_item_proxified.call(allocable, list); }; }(Array)); var Queue = (function () { // [Queue] Module. var onEnqueue = function (queue, type) { //queue.dispatchEvent({type: "enqueue", item: type}); }, onDequeue = function (queue, type) { //queue.dispatchEvent({type: "dequeue", item: type}); }/*, onEmpty = function (queue) { //queue.dispatchEvent({type: "empty"}); }*/, onEmpty = function (queue) { //queue.dispatchEvent("empty"); }, Queue = function () { // [Queue] Constructor. var queue = this, list = [] ; queue.enqueue = function (type) { list.push(type); onEnqueue(queue, type); return type; }; queue.dequeue = function () { var type = list.shift(); onDequeue(queue, type); (list.length || onEmpty(queue)); return type; }; //Observable.call(queue); // applying the [Observable] Mixin. Allocable.call(queue, list); // applying the bicontextual [Allocable] Mixin. }, isQueue = function (type) { return !!(type && (type instanceof Queue)); }, createQueue = function () { // [Queue] Factory. return (new Queue); } ; return { // [Queue] Module. isQueue : isQueue, create : createQueue }; }()); var q = Queue.create(); //q.addEventListener("enqueue", function (evt) {/* ... */}); //q.addEventListener("dequeue", function (evt) {/* ... */}); //q.addEventListener("empty", function (evt) {/* ... */}); console.log("q : ", q); // { .., .., .., } console.log("q.size() : ", q.size()); // 0 console.log("q.valueOf() : ", q.valueOf()); // [] "the quick brown fox jumped over the lazy dog".split(/\s+/).forEach(function (elm/*, idx, arr*/) { console.log("q.enqueue(\"" + elm + "\")", q.enqueue(elm)); }); console.log("q.size() : ", q.size()); // 9 console.log("q.toArray() : ", q.toArray()); // [ .., .., .., ] console.log("q.first() : ", q.first()); // "the" console.log("q.last() : ", q.last()); // "dog" console.log("q.item(2) : ", q.item(2)); // "brown" console.log("q.item(5) : ", q.item(5)); // "over" console.log("q.dequeue()", q.dequeue()); // "the" console.log("q.dequeue()", q.dequeue()); // "quick" console.log("q.dequeue()", q.dequeue()); // "brown" console.log("q.dequeue()", q.dequeue()); // "fox" console.log("q.dequeue()", q.dequeue()); // "jumped" console.log("q.size() : ", q.size()); // 4 console.log("q.toArray() : ", q.toArray()); // [ .., .., .., ] console.log("q.first() : ", q.first()); // "over" console.log("q.last() : ", q.last()); // "dog" console.log("q.item(2) : ", q.item(2)); // "lazy" console.log("q.item(5) : ", q.item(5)); // undefined
.as-console-wrapper { max-height: 100%!important; top: 0; }
问题描述
I've been using the module pattern for a while, but recently have started wanting to mix in functions and properties into them to increase code re-use. I've read some good resources on the subject, but still am a bit uncertain as to the best approach. Here is a module:
var myModule = function () { var privateConfigVar = "Private!"; //"constructor" function module() {} module.publicMethod = function () { console.log('public'); } function privateMethod1() { console.log('private'); } return module; }
And here is a mixin object:
var myMixin = function () {}; Mixin.prototype = { mixinMethod1: function () { console.log('mixin private 1'); }, mixinMethod2: function () { console.log('mixin private 2'); } };
Ideally, I'd like to mix-in some methods from other objects as private methods and some as public methods, so that I could call some "extend" function, with a param as "private"/"public". So, that
mixin(myModule, myMixin, "private");
makes the myMixin methods available within myModule by just calling mixinMethod1() and have correct scope, and:
mixin(myModule, myMixin, "public");
makes the myMixin methods available within myModule by calling module.mixinMethod1() and have correct scope
I've tried using a method that copies properties from one prototype to another, I've tried the underscore extend method to copy properties of the object from one to to the other, and various things in between. I think I'm a bit turned around regarding scope and prototypes at this point, and would love some direction as to how best to do mixins like this when using the module pattern. Note that it doesn't matter what the object myMixin looks like (whether adding functions to the prototype, or a module itself), I'm just trying to figure out some way to make it work.
Thank!
推荐答案
So that [some code] makes the myMixin methods available within myModule by just calling mixinMethod1() and have correct scope
That's impossible. You cannot modify a scope by calling a function, especially not from outside. See also Is it possible to import variables in JavaScript? for the design reasons of that.
So, what can you do?
From outside the module
Nothing to the private scope(s) of module functions. And you cannot use the private functions of the module, obviously. You can extend its prototype with methods (which is the most common), you can even decorate its constructor function. Within those, you can use your own private functions, either completely static ones or class-specific ones.
var myMixin = (function() { // everything class-unspecific but mixin-local var staticMixinVariables, …; function globalPrivateFunction(){…} function staticMethod(){…} return function(mod) { // everything class-specific // also using the locals from above mod.staticHelper = function() { staticMixinVariable … }; mod.prototype.mixinMethod1 = staticMethod; mod.prototype.mixinMethod2 = function(){…}; … }; })(); // Example: myMixin(SomeClass)
From within the module
Using the mixin in the module code itself can allow for much greater flexibility.
var myMixin = (function() { // everything class-unspecific but mixin-local … return { publicHelper1: function(){…}, publicHelper2: function(){…}, decorateInstance: function(o) { o.xy = …; }, extendPrototype: function(proto) { // everything class-specific // also using the locals from above proto.mixinMethod1 = staticMethod; proto.mixinMethod2 = function(){…}; … } }; })();
With such an interface, it becomes easy to construct a class that is using this as a mixin (not inheriting from it):
var myClass = (function() { function Constructor() { myMixin.decorateInstance(this); … } Constructor.prototype.method1 = function() { myMixin.publicHelper1() … }; Constructor.prototype.method2 = function() { … }; myMixin.extendPrototype(Constructor.prototype); Constructor.myHelper = myMixin.publicHelper2; // re-export explicitly return Constructor; })();
However, the mixin will never have access to the private class variables, nor can it present a private, class-specific API. Still, we can use dependency injection to provide that access explicitly (and having a mixin factory in effect):
var myClass = (function() { var … // private class functions and variables var mixer = myMixin(privateClassHelper, privateClassVariable, function setPrivateVar(x) {…}, … ); var myHelper = mixer.customHelper, … // local "aliases" function Constructor(localX) { mixer.decorateInstance(this, localX); … } … // further using the class-specific private mixer return Constructor; })();
Not all techniques shown above need to be used in every mixin, just choose the ones you need. Not all possible techniques are shown in the above examples, also :-) The mixin pattern can be applied onto a plain module or inside its declaration as well, the above examples have only shown classes with prototypes.
For a few good examples, and a theoretical distinction between (stateless) Traits, (stateful) Mixins and their "privileged" counterparts, have a look at this presentation.
其他推荐答案
The with keyword can be very usefull to define a scope, but it has also some drawbacks (it is by the way forbidden in strict mode).
Using the with keyword, you can define a private variable privateScope within the body of your module, that would contain all your provate methods :
var myModule = function () { var privateConfigVar = "Private!"; var privateScope = {}; //"constructor" function module() {} var proto = module.prototype;//avoids multiple attribute lookup //Let's re-define you example' private method, but with a new strategy privateScope['privateMethod1'] = function() { console.log('private'); } proto.publicMethod = function () { with(privateScope){ //this call should work privateMethod1(); } console.log('public'); } proto.publicMethod2=function(name,fn){ with(privateScope){ //this will be defined later by a Mixin otherPrivateMethod(); } console.log('public2'); } proto.definePrivateFunction=function(name,fn){ privateScope[name] = fn; } return module; }
Your mixin will use the definePrivateFunction we just defined to add private methods to the private scope :
//An example mixin implementation function Mixin(source,target,flag){ if(flag==="private"){ for(var currentMethodName in source){ target.definePrivateFunction(currentMethodName,source[currentMethod]) } }else{ for(var currentMethodName in source){ target[currentMethodName]=source[currentMethod]; } } }
The following code should work fine:
var test = myModule(); var testInstance = new test(); testInstance.publicMethod();// will call the private method defined internally Mixin({ otherPrivateMethod:function(){ console.log("other Prvate Method called") } },test.prototype,"private"); testInstance.publicMethod2();// will call the private method defined by the mixin
其他推荐答案
Ideally, I'd like to mix-in some methods from other objects as private methods and some as public methods, so that I could call some "extend" function, with a param as "private"/"public". ...
As it already has been mentioned, there is no way of achieving exactly this goal.
So, that ... makes the myMixin methods available within myModule by just calling mixinMethod1() and have correct scope, and: ... makes the myMixin methods available within myModule by calling module.mixinMethod1() and have correct scope.
And referring to scope ... this is a closed address space created by functions. Except for closures, scope only is available during a function's runtime within this function's body. It never ever can be manipulated/spoofed.
The term one is looking for is context. JavaScript, being in many ways highly dynamic, is build upon late binding (the object/target/context a method is called on gets evaluated/looked up at runtime) and two kinds of delegation. Context gets delegated either automatically by "walking the prototype chain" or explicitly by one of both call methods which every function object does provide - either call or apply.
Thus JavaScript already at language core level does offer a function based Mixin pattern that is mightier than any of the available extend(s) or mixin implementations for it provides delegation for free and is able of passing around state which almost every of the blamed helpers does lack unless there was effort of implementing this feature again in a rather roundabout fashion (or ass-backwards to put it bluntly).
Bergi for his explanation already earned the bounties. Within his answer's last paragraph there is a link to resources of mine that already got outdated 3 month after giving the referred talk. Due of not having enough reputation points, I'm not able to comment his answer directly. For this I'll take the chance pointing now to the latest state of my personal research and understanding of »The many talents of JavaScript for generalizing Role Oriented Programming approaches like Traits and Mixins«
Back again answering the OP's question.
I'm going to change the two first given code examples from the assumed module pattern and the rather exemplarily provided mixin code base towards a plain constructor function and what I'm meanwhile tempted to call a "proxified" and/or "bicontextual" mixin in order to boil down the mechanics of delegating two different target/context objects at once. Thus demonstrating a pure function based mixin pattern that might come closest to what the OP tries to achieve.
var MyBicontextualMixin = function (localProxy) { localProxy.proxifiedAccessible = function () { console.log("proxified accessible."); }; this.publiclyAccessible = function () { console.log("publicly accessible."); }; }; var MyConstructor = function () { var localProxy = {}; MyBicontextualMixin.call(this, localProxy); var locallyAccessible = localProxy.proxifiedAccessible; // call 'em locallyAccessible(); // "proxified accessible." this.publiclyAccessible(); // "publicly accessible." }; (new MyConstructor); // will log: // // proxified accessible. // publicly accessible.
This special pattern also is the underlying base for composing pure function based Traits that rely on conflict resolution functionality provided by "proxified" Mixins that won't expose this functionality into public.
And for not ending up that theoretical there will be a "real world example", composing a Queue module out of various reusable mixins that entirely worship the approach of DRY. It also should answer the OP's question about how to achieve encapsulation and exposition build only upon the module pattern and function based mixin composition.
var Enumerable_first_last_item = (function (global) { var parseFloat = global.parseFloat, math_floor = global.Math.floor, // shared code. first = function () { return this[0]; }, last = function () { return this[this.length - 1]; }, item = function (idx) { return this[math_floor(parseFloat(idx, 10))]; } ; return function () { // [Enumerable_first_last_item] Mixin. var enumerable = this; enumerable.first = first; enumerable.last = last; enumerable.item = item; }; }(window || this)); var Enumerable_first_last_item_proxified = function (list) { Enumerable_first_last_item.call(list); // implementing the proxified / bicontextual [Enumerable_first_last_item] Mixin. var enumerable = this; enumerable.first = function () { return list.first(); }; enumerable.last = function () { return list.last(); }; enumerable.item = function (idx) { return list.item(idx); }; }; var Allocable = (function (Array) { var array_from = ((typeof Array.from == "function") && Array.from) || (function (array_prototype_slice) { return function (listType) { return array_prototype_slice.call(listType); }; }(Array.prototype.slice)) ; return function (list) { // proxified / bicontextual [Allocable] Mixin. var allocable = this ; allocable.valueOf = allocable.toArray = function () { return array_from(list); }; allocable.toString = function () { return ("" + list); }; allocable.size = function () { return list.length; }; Enumerable_first_last_item_proxified.call(allocable, list); }; }(Array)); var Queue = (function () { // [Queue] Module. var onEnqueue = function (queue, type) { //queue.dispatchEvent({type: "enqueue", item: type}); }, onDequeue = function (queue, type) { //queue.dispatchEvent({type: "dequeue", item: type}); }/*, onEmpty = function (queue) { //queue.dispatchEvent({type: "empty"}); }*/, onEmpty = function (queue) { //queue.dispatchEvent("empty"); }, Queue = function () { // [Queue] Constructor. var queue = this, list = [] ; queue.enqueue = function (type) { list.push(type); onEnqueue(queue, type); return type; }; queue.dequeue = function () { var type = list.shift(); onDequeue(queue, type); (list.length || onEmpty(queue)); return type; }; //Observable.call(queue); // applying the [Observable] Mixin. Allocable.call(queue, list); // applying the bicontextual [Allocable] Mixin. }, isQueue = function (type) { return !!(type && (type instanceof Queue)); }, createQueue = function () { // [Queue] Factory. return (new Queue); } ; return { // [Queue] Module. isQueue : isQueue, create : createQueue }; }()); var q = Queue.create(); //q.addEventListener("enqueue", function (evt) {/* ... */}); //q.addEventListener("dequeue", function (evt) {/* ... */}); //q.addEventListener("empty", function (evt) {/* ... */}); console.log("q : ", q); // { .., .., .., } console.log("q.size() : ", q.size()); // 0 console.log("q.valueOf() : ", q.valueOf()); // [] "the quick brown fox jumped over the lazy dog".split(/\s+/).forEach(function (elm/*, idx, arr*/) { console.log("q.enqueue(\"" + elm + "\")", q.enqueue(elm)); }); console.log("q.size() : ", q.size()); // 9 console.log("q.toArray() : ", q.toArray()); // [ .., .., .., ] console.log("q.first() : ", q.first()); // "the" console.log("q.last() : ", q.last()); // "dog" console.log("q.item(2) : ", q.item(2)); // "brown" console.log("q.item(5) : ", q.item(5)); // "over" console.log("q.dequeue()", q.dequeue()); // "the" console.log("q.dequeue()", q.dequeue()); // "quick" console.log("q.dequeue()", q.dequeue()); // "brown" console.log("q.dequeue()", q.dequeue()); // "fox" console.log("q.dequeue()", q.dequeue()); // "jumped" console.log("q.size() : ", q.size()); // 4 console.log("q.toArray() : ", q.toArray()); // [ .., .., .., ] console.log("q.first() : ", q.first()); // "over" console.log("q.last() : ", q.last()); // "dog" console.log("q.item(2) : ", q.item(2)); // "lazy" console.log("q.item(5) : ", q.item(5)); // undefined
.as-console-wrapper { max-height: 100%!important; top: 0; }