简体   繁体   中英

How to use closure in a recursive function without losing scope in JavaScript?

  • tem.jqw.Callback object contains an executable function, a CSS selector. and a looping delay.
  • tem.jqw.wait waits for jQuery to be loaded and then goes through an array of tem.jqw.Callback objects and executes their functions after having found an element with the CSS selector passed in.

The problem I am having is in the run function within the tem.jqw.Callback object. When the run function is called the first time, if the element exists, everything is fine and the executable function runs OK. However, if the element does not yet exist, and we need to loop, the function loses scope after setTimeout(this.run, 100) executes once. For example, when the run function is executing the second time, this.selector or this.fn become undefined. How can I work around this without using global variables? Thanks in advance.

 if (typeof tem !== "object") { tem = {}; } tem.jqw = {}; tem.jqw.retries = 0; tem.jqw.maxRetries = 100; tem.jqw.delay = 100; tem.jqw.callbacks = []; tem.jqw.Callback = function (fn, selector, delay) { this.fn = fn; this.selector = (typeof selector === "string" && selector.length > 0) ? selector : document; this.delay = (typeof delay === "number" && delay > 0 && delay < 1000) ? delay : 100; this.retries = 0; this.maxRetries = 100; this.start = function () { this.run(); }; this.run = function () { if (jQuery(this.selector).length > 0) { console.log("[OPDEBUG] tem.jqw.Callback.run says: " + this.selector.toString() + " is ready. Executing callback function..."); this.fn(); } else { this.retries++; console.log("[OPDEBUG] tem.jqw.Callback.run says: typeof this.selector " + typeof this.selector); console.log("[OPDEBUG] tem.jqw.Callback.run says: Waiting for " + this.selector.toString() + "..."); if (this.retries < this.maxRetries) { setTimeout(this.run, 100); } } }; }; tem.jqw.wait = function () { if (typeof jQuery === "function") { console.log("[OPDEBUG] tem.jqw.wait says: jQuery is ready."); for (var i = 0; i < tem.jqw.callbacks.length; i++) { if (typeof tem.jqw.callbacks[i] === "object" && typeof tem.jqw.callbacks[i].start === "function") { console.log("[OPDEBUG] tem.jqw.wait says: Executing callback function " + (i + 1) + "..."); tem.jqw.callbacks[i].start(); } } } else { tem.jqw.retries++; console.log("[OPDEBUG] tem.jqw.wait says: " + "Waiting for jQuery " + tem.jqw.retries + "..."); if (tem.jqw.retries < tem.jqw.maxRetries) { setTimeout(tem.jqw.wait, tem.jqw.delay); } } }; tem.jqw.callbacks.push(new tem.jqw.Callback(function () { jQuery('.hero-inner:first a').css('background-image', 'url("https://www.thedogs.co.nz/Files/PhotoFinishImages/11298_89160.jpg")') }, ".hero-inner:first")); tem.jqw.callbacks.push(new tem.jqw.Callback(function () { jQuery('.RR-placeholder ul li:first').hide(); }, ".RR-placeholder ul li:first")); tem.jqw.wait(); 

you can use bind to set this

change this.run() to

this.run.bind(this)

Its better to assign this to another variable eg)

var self = this;

and then pass around self for better readability

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM