Why is "this" scope wrong in a defined property? - javascript

I am creating a debounce function in my code, I have made a really simple example however when using it in combination with Object.defineProperty the value of "this" is questionable.
This is the code:
// Debounce a function call
Object.defineProperty(Function.prototype, "debounce", {
enumerable: false,
writable: false,
value: function(ms, scope) {
var fn = this,
time = ms,
timer;
function debounced() {
var args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(function() { fn.apply(scope, args); }, time);
}
return debounced;
}
});
And it is called with (where the .bind... is done as part of an external library):
function() {
console.log(this.something);
}.debounce(300).bind(someObject)();
And it works, but the value of "this" cannot be used. Within the value function "this" points to the original function. Is there anyway I can get the bound scope with this design style?
Example
var a = {
b: function() {
console.log(this.something);
}.debounce(300),
};
var c = {
something: true,
};
// I can't control this code -> 3rd party library
a.b.bind(c)();

You need to use the this of debounced when debounced is called
in old school JS
Object.defineProperty(Function.prototype, "debounce", {
value: function value(ms) {
var fn = this,
time = ms,
timer;
function debounced() {
var _this = this;
var args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(function () {
return fn.apply(_this, args);
}, time);
}
return debounced;
}
});
or simpler in ES2015+
Object.defineProperty(Function.prototype, "debounce", {
value: function(ms) {
var fn = this,
time = ms,
timer;
function debounced() {
if (timer) clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, arguments), time);
}
return debounced;
}
});
You can also remove enumerable:false, writable:false as false is the default value for these properties - makes the code a little smaller :p

Related

Javascript Function Self Invoking Inside Object

Instead using setInterval i can use this, to repeatly call an function.
function foo(){
setTimeout(foo, 1000);
}();
The problem is, i want to do the same thing, inside an object, here the snippet.
var evt;
var init;
evt = function() {
return {
cycle:function(str) {
setTimeout(function(str) {
this.cycle(str);
}, 1000);
}
}
}
init = new evt();
init.cycle("Hallo word");
Then the error shows up, it said
this.cycle() is not a function.
I'm trying to make an variable as this at the above line of my codes, here, like this
var evt;
var init;
evt = function() {
var parent;
parent = this;
return {
cycle:function(str) {
setTimeout(function(str) {
parent.cycle(str);
}, 1000);
}
}
}
init = new evt();
init.cycle("Hallo word");
But still getting.
parent.cycle() is not a function
Is there a way to do this, what i want here is, went i call evt.cycle("Hello World") after first attempt showing Hello World it will repeatly showing Hello World in every next seconds.
I need to keep the function inside the object that generated by that function. Thanks for any correction.
When you return a new object a new scope is defined. So you should bind this pointer to the function. Or you can use .bind(this) function in this way:
setTimeout((function(str){
}).bind(this), 1000)
For more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Or you can use call or apply: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
In es6 you could use ()=>{} (arrow) function, when the this pointer is inherited.
Other working solution:
var evt;
var init;
evt = function() {
var parent;
parent = this;
return {
cycle:function(str) {
var me = this;
setTimeout(function(str) {
console.log("cycle");
me.cycle(str);
}, 1000);
}
}
}
init = new evt();
init.cycle("Hallo word");
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
const evt = function() {
return {
i: 0,
cycle: function(str) {
const _this = this;
setTimeout(() => {
console.log(str.substring(0, this.i));
_this.cycle(str, ++this.i);
}, 1000);
}
}
}
init = new evt();
init.cycle("Hello world");
I extended the example a little bit to illustrate the effect of this a little more.

Overriding a function on a javascript object's property

How would you override a function on a javascript object when the function is on another object within the parent object.
Example:
function TestingABC() {
this.events = { finish: function() { console.log("FINISHED"); } };
}
function TestingXYZ() {
TestingABC.call(this);
}
TestingXYZ.prototype = Object.create(TestingABC.prototype);
How would I override the events.finish function on TestingXYZ to run the parent (TestingABC) code along with some new code that I need to write?
Because the events object is property of the instance, not on the prototype, you could employ a technique similar to monkey patching, where you store a reference to the current function, then override the current function with one that can call the old one in addition to doing other stuff.
e.g.
function TestingABC() {
this.events = { finish: function() { console.log("FINISHED"); } };
}
function TestingXYZ() {
TestingABC.call(this);
var superEvents = this.events;
this.events = {
finish: function () {
superEvents.finish();
doMyStuff();
}
};
}
TestingXYZ.prototype = Object.create(TestingABC.prototype);
.events is an instantiated property of the TestingABC() constructor - so you can amend the value once you have an instantiation of it.
Perhaps something like this is what you're after?...
function TestingABC() {
this.events = {
finish: function() {
console.log('ABC FINISHED');
},
other: function() {
console.log('ABC OTHER');
}
};
}
function TestingXYZ() {
TestingABC.call(this);
}
TestingXYZ.prototype = Object.create(TestingABC.prototype);
TestingXYZ.prototype.callEvents = function() {
this.events.finish();
this.events.other();
}
var test1 = new TestingABC();
var test2 = new TestingXYZ();
test2.events.finish = function() {
console.log('XYZ FINISHED');
};
test1.events.finish();
test1.events.other();
//-> ABC FINISHED
//-> ABC OTHER
test2.callEvents();
//-> XYZ FINISHED
//-> ABC OTHER

How to call a function from `setInterval`?

function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
setInterval(function() {
this.func2();
}, 200);
}
}
function main() {
var obj = new Obj();
obj.func1();
}
main();
I have the following exception:
this.func2(); ^ TypeError: this.func2 is not a function
at Timeout._onTimeout(C: \Users\ Admin\ Desktop\ CANVAS\ test.js: 15: 12)
at ontimeout(timers.js: 365: 14)
at tryOnTimeout(timers.js: 237: 5)
at Timer.listOnTimeout(timers.js: 207: 5)
Why this.func2 is function when I call without setInterval and is not a function when I call from setInterval?
Cause this is not referring to Obj in your case. That context has been changed and this is referring to setInterval context.
NOte the line var self = this;, which storing this for later usage.
This should work
function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
setInterval(function(){
self.func2();
}, 200);
}
}
function main() {
var obj = new Obj();
obj.func1();
}
main();
Maybe you wanted to do something like this:
function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
setInterval(function(){
this.func2();
}.bind(this), 200);
}
}
But this will soon lead to an overflow, because you create a new interval on each execution. Maybe you wanted to use setTimeout?
You can bind this
this.func2 = function() {
setInterval(function() {
this.func2();
}.bind(this), 200);
}
DEMO
The problem is the context inside of setTimeout is the top-level, not the context of your object, so this will point to window (in a browser) instead of your object. There are a couple of ways to solve the problem.
The most recent version is to use the bind method to bind the context of your function.
function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
console.log('hi');
setTimeout(this.func2.bind(this), 1000);
}
}
function main() {
var obj = new Obj();
obj.func1();
}
main();
Notice that I use this.func2.bind(this) as the callback for setTimeout (same for setInterval, just your example would be quite spammy if I left it with that). That means that no matter where it's called, it's this will always be your object.
The slightly older way to do it is to wrap it up in self-calling function that'll isolate it to a scope that has this set to your object.
function Obj() {
this.func1 = function() {
this.func2();
},
this.func2 = function() {
console.log('hi');
setTimeout((function(self){
return function () {
self.func2();
}
})(this), 1000);
}
}
function main() {
var obj = new Obj();
obj.func1();
}
main();
This way is a bit more involved, but basically I just wrapped what you originally had in a self-calling function that passes in this as a parameter for self, then I use self.func2() inside of it.
this is not passed to your setInterval callback because it is always called in the global context, just like when you use setTimeout.
this is either undefined (in 'use strict' mode) or the window object (in regular/loose mode). You can pass extra arguments to setInterval, though, and these are passed on to your callback:
this.func2 = function() {
setInterval(function(self) {
self.func2()
}, 200, this)
}
Demo Snippet
function Obj() {
this.func1 = function() {
this.func2()
}
this.func2 = function() {
setInterval(function(self) {
console.log(typeof self.func2) //=> 'function'
}, 200, this)
}
}
function main() {
var obj = new Obj()
obj.func1()
}
main()
Use arrow function:
this.func2 = function() {
setInterval(() => { this.func2(); }, 200);
}

Accessing a containing object from with in its method?

In the snippet below, an object literal holds properties, one of which is a method that needs access to the the object literal.
However, b.c. it is only used as an event handler callback, this always points to the element that triggered the event.
I need to access the containing object.
Otherwise, I'm forced to put a function in a function which seems odd.
/***************************************************************************************************
**MSimMenu - simple drop down menu
*/
NS.parsel({
Name: 'MSimMenu',
E: {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
},
A: {
time_out_id: null,
TIME_DELAY: 1000
},
// in mouseout this points to the element that triggered the event
// need access to containing object
mouse_out: function () {
this.A.time_out_id = NS.setTimeout(this.hideBottom, this.A.TIME_DELAY);
},
init: function () {
var self = this;
// tempoaray fix - function in function seems odd
function mouse_out() {
self.A.time_out_id = NS.setTimeout(self.hideBottom, self.A.TIME_DELAY);
}
self.E.hold_name.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
self.showBottom();
}, false);
self.E.wrap_bottom.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
}, false);
self.E.wrap_bottom.addEventListener("mouseout", mouse_out, false);
self.E.hold_name.addEventListener("mouseout", mouse_out, false);
},
showBottom: function () {
this.E.wrap_bottom.style.visibility = 'visible';
},
hideBottom: function () {
this.E.wrap_bottom.style.visibility = 'hidden';
}
});
Final Code Using Bind
NS.parsel({
Name: 'MSimMenu',
E: {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
},
A: {
time_out_id: null,
TIME_DELAY: 1000
},
init: function () {
var self = this;
self.E.hold_name.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
self.showBottom();
}, false);
self.E.wrap_bottom.addEventListener("mouseover", function () {
NS.clearTimeout(self.A.time_out_id);
}, false);
self.E.wrap_bottom.addEventListener("mouseout", self.mouse_out.bind(self), false);
self.E.hold_name.addEventListener("mouseout", self.mouse_out.bind(self), false);
},
mouse_out: function () {
this.A.time_out_id = NS.setTimeout(this.hideBottom, this.A.TIME_DELAY);
},
showBottom: function () {
this.E.wrap_bottom.style.visibility = 'visible';
},
hideBottom: function () {
this.E.wrap_bottom.style.visibility = 'hidden';
}
});
I have seen alot of people create a variable to assign the object to and then use the variable.
var that = {
myfunc:function(){
console.log(that)
}
};
NS.parsel(that);
I actually like moving most of the logic into the init method. Provides nice encapsulation with an easy way to declare public and private methods/variables. For example:
NS.parsel({
init: function() {
var self = this;
//public instance variable
self.Name = 'MSimMenu';
//private instance variables
var A = {
time_out_id: null,
TIME_DELAY: 1000
};
var E = {
hold_name: '#hold_name',
wrap_bottom: '#wrap_bottom'
};
//public instance method
self.showBottom = function () {
E.wrap_bottom.style.visibility = 'visible';
};
//private instance method
E.wrap_bottom.addEventListener("mouseout", mouse_out, false);
function mouse_out() {
A.time_out_id = NS.setTimeout(self.hideBottom, A.TIME_DELAY);
}
}
});
There's a lot of ways you can get what you want.
One trick you can do is to not use the mouse_out function directly, but provide a helper function like get_mouse_out() that returns a bound version of the function.
var myobject = {
data:"Some data",
_mouse_out: function() { console.log(this.data); }
get_mouse_out: function() {
var self = this;
return function(){ return Function.apply(self._mouse_out,self,arguments); }
}
}
//Example call back using function.
function do_callback(fn) { fn(); }
//This doesn't work.
do_callback( myobject._mouse_out);
//But this does
do_callback( myobject.get_mouse_out() );
EDIT: Improved version inlining _mouse_out and using bind.
var myobject = {
data:"Some data",
get_mouse_out: function() {
function _mouse_out() { console.log(this.data); }
return _mouse_out.bind(this);
}
}
//Example call back using function.
function do_callback(fn) { fn(); }
//But this does
do_callback( myobject.get_mouse_out() );
If you're willing to have init be called as setup before mouse_out is used then you can do this.
var myobject = {
data:"Some data",
init: function() {
function _mouse_out() { console.log(this.data); }
this.mouse_out = _mouse_out.bind(this);
}
}
myobject.init();
fn( myobject.mouse_out );
Finally there's a nice variant on Shanimals that works a similar way, but provides encapsulation.
NS.parcel( (function(){
var myobj = {};
myobj.data = "Some data";
myobj.mouse_out = function(){ console.log(myobj.data); }
return myobj;
})()
);

Using the JavaScript revealing prototype pattern, how can I namespace functions contained within prototypes?

I'm using the Revealing Prototype Pattern and have 2 different prototypes that I'm putting into the same JavaScript file. These links are to articles I found which relate to this.
http://bit.ly/U83hdg, http://bit.ly/VmJ71h.
I was under the impression that these would operate like atomic classes, where functions associated with one would be unaware of functions in the other.
For instance, both of these prototypes have an "init" and a "set" function. The behavior I'm seeing in the browser is that the last version of "init" gets executed, even when the code references the first prototype name.
This is generic stripped-down code from my two prototypes.
var operationA = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationA.prototype = function () {
init = function () {
// do something
return this;
}
set = function () {
// do something
return this;
};
return {
init: init,
set: set
};
}
var operationB = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationB.prototype = function () {
init = function () {
// do something
return this;
}
set = function () {
// do something
return this;
};
return {
init: init,
set: set
};
}
This is how I'm instantiating the first object.
var objectASettings = {
property1: 48,
property2: 37
};
var objectA = new operationA('#mySelector', objectASettings);
objectA.init().set();
When the above runs, the init and set functions from the prototype for operationB are being executed, instead of executing the init and set functions from the prototype for operationA.
I assumed these prototypes basically namespaced their contained functions. Am I required to create unique public function names for operationA and operationB (like initA , setA, initB, setB)?
Is there a way to self-contain and/or namespace these public functions, so I can expose the same operation names of init and set on 2 different prototypes in the same file?
Thanks for your help.
A couple of things to get it working:
Add var before the first member in the prototype function.
Separate each member with a comma (you can certainly put var in front of each member but I like to keep it clean...personal preference though).
The function assigned to the prototype must be self-invoked for the pattern to work properly.
Here's an example that should work for you:
<html>
<head>
<script>
var operationA = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationA.prototype = function () {
var init = function () {
// do something
return this;
},
set = function () {
alert('set A');
return this;
};
return {
init: init,
set: set
};
}();
var operationB = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationB.prototype = function () {
var init = function () {
// do something
return this;
},
set = function () {
alert('set B');
return this;
};
return {
init: init,
set: set
};
}();
window.onload = function() {
var objectASettings = {
property1: 48,
property2: 37
};
var objectBSettings = {
property1: 50,
property2: 50
};
var objectA = new operationA('#mySelector', objectASettings);
objectA.init().set();
var objectB = new operationB('#foo', objectBSettings)
objectB.init().set();
}
</script>
</head>
You're omitting the var keyword when defining init and set so they're both assigned to the global object.
Just define the prototypes as Objects.
var operationA = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationA.prototype = {
init: function () {
// do something
return this;
},
set: function () {
// do something
return this;
}
}
var operationB = function (control, settings) {
this.control = control;
this.settings = settings;
};
operationB.prototype = {
init: function () {
// do something
return this;
},
set: function () {
// do something
return this;
}
};

Categories

Resources