Cannot access properties by 'this' [duplicate] - javascript

This question already has an answer here:
passing this.method in setTimeout doesn't work?
(1 answer)
Closed 8 years ago.
In the following snippet I try to access the property offset from within the member function shift(). As it seems, I cannot access it this way, because console.log reports Offset: NaN:
function shiftImg() {
this.offset = 0;
this.shift =
function() {
this.offset++;
console.log("Offset: " + this.offset);
};
}
productImg = new shiftImg;
window.setInterval(productImg.shift, 100);
However, converting the code above from a template paradigm to a closure paradigm works as I'd expect:
function shiftImg() {
var offset = 0;
return {
shift: function() {
offset++;
console.log("Offset: " + offset);
}
}
}
productImg = shiftImg();
window.setInterval(productImg.shift, 100);
In my first example, why I cannot access offset via the operator this?
My Answer:
I'll post here my solution, as I cannot append a standalone answer.
Browsing again into the mess of the horribly-written MDN's documentation, I learned of the bind method:
function shiftImg() {
this.offset = 0;
this.shift =
function() {
this.offset++;
var img = document.getElementById('img');
img.style.paddingLeft = this.offset + 'px';
console.log("Offset: " + this.offset);
};
}
productImg = new shiftImg;
window.setInterval(productImg.shift.bind(productImg), 100);

The nested function doesn't have it's own this context (it'll simply refer to the window), so assign a variable to the this method within shiftImg to which you can refer in the nested function:
function shiftImg() {
var self = this;
this.offset = 0;
this.shift =
function() {
self.offset++;
console.log("Offset: " + self.offset);
};
}
productImg = new shiftImg();
window.setInterval(productImg.shift, 100);
The reason you need to do this is because the call to setInterval which invokes the method, is run in a separate execution context, where this is equal to the window. If you called this.shift() from within shiftImg() you'll see that you it works just fine without the need to add self. See this MDN article for more.
Alternatively you pass an anonymous function to the callback method in setInterval:
window.setInterval(function() {
productImg.shift();
}, 100);
If you use objects and jQuery then you'll find into this problem quite a lot, and jQuery's $.proxy utility method makes doing similar things to above fairly easy.

In the first example, the context of this for the offset is not the same context as shiftImage. The second function closure changes the scope of this.

Related

React: this.setState is not a function [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
I'm working on a workaround for another problem I'm having but with this I got a "this.setState is not a function" error. I found this answer which advises to bind it within the constructor, which I did.
This is part of my constructor:
this.newProject = this.newProject.bind(this);
this.openProject = this.openProject.bind(this);
this.saveProject = this.saveProject.bind(this);
And this is my function:
// Open a file, set data as session item and reload page
openProject(FileObject) {
var read = new FileReader();
read.readAsBinaryString(FileObject);
read.onloadend = function() {
//sessionStorage.setItem("reloading", "true");
//sessionStorage.setItem("data", read.result);
//document.location.reload();
// Fix for missing data.
var jsonData = JSON.parse(read.result);
for (var i = 0; i < jsonData.blocks.length; i++) {
var name = jsonData.blocks[i].name;
var id = jsonData.blocks[i].id;
var ip = jsonData.blocks[i].ip;
var port = jsonData.blocks[i].port;
this.setState( { blockCount: (i + 1), });
// Add block to the list
this.setState({
blocks: this.state.blocks.concat({
id: id,
name: name,
ref: React.createRef(),
positionX: window.innerWidth*0.4 - 125 / 2,
positionY: 75 + ( 50 * this.state.blocks.length),
links:[],
requires: this.state.parameters.blockRequires
})
});
}
}
}
What would be a solution to this?
this belongs to the closest function context, which in your case is read.onloadend = function()...NOT the class.
You can solve this problem by assigning the class-level this to a new variable before you enter that ad-hoc function:
openProject(FileObject) {
var read = new FileReader();
read.readAsBinaryString(FileObject);
var that = this;
read.onloadend = function() {
// ...
that.setState(/*... etc*/
And of course you'll want to change all instances of this within your onloadend callback function to that (or whatever variable name you choose).
*Edit: as #MDTabishMahfuz describes, you can also use an arrow function, because unlike the function declaration, an arrow function:
Does not have its own bindings to this or super
Binding this is necessary for functions of the class but you have an additional callback (onloadend function) which is a different function and the component's this is not available there.
Conventional Functions in JS have their on this in their context. You can either use the method suggested by #David784 or use an arrow function for the onloadend handler like so:
read.onloadend = () => {
....
this.setState(....);
....
}
Arrow functions have this from the parent's context, that is the React component in your case.

JavaScript - Closures

I'm reading the explanation of closures on Mozilla developer site and am struggling a bit. Please have a look at the following code from Mozilla website. I kind of understand how it works but I'd think that the code below my comments should also work. Why does it not work if one click on 18 and 20?
/* mozilla dev code */
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
/* end of mozilla dev example */
/* my code starts */
/* see - no inner function below */
function makeS(size) {
document.body.style.fontSize = size + 'px'
}
/* Let's see if that works */
var size18 = makeS(18);
document.getElementById('size-18').onclick = size18;
/* What about that? */
document.getElementById('size-20').onclick = makeS(20);
Why
CodePen:
http://codepen.io/wasteland/pen/qqoooW
makeS(18) immediately invokes the function and changes the size. What you assign to the onclick event in that case is actually undefined, since that's what the function returns when invoked, as it has no explicit return.
function makeS(size) {
document.body.style.fontSize = size + 'px'
}
console.log("font size before calling makeS:", document.body.style.fontSize); //it's actually an empty string, hence why it doesn't show up in the output
var size18 = makeS(18);
console.log("font size after calling makeS:", document.body.style.fontSize);
console.log("what is size18?", typeof size18);
By contrast, makeSizer(18) will create a new function that when called, will change the size.
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
console.log("font size before calling makeSizer:", document.body.style.fontSize); //it's actually an empty string, hence why it doesn't show up in the output
var size18Function = makeSizer(18);
console.log("font size after calling makeSizer:", document.body.style.fontSize); //it's still an empty string
console.log("what is size18Function?", typeof size18Function);
//let's call it now
size18Function();
console.log("font size after calling size18Function:", document.body.style.fontSize); //it's now changed
A closure is fact of 'remembering' variables of the scope chain by the function when it is defined. Since function argument is just a local variable during execution, this:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
is an equivalent to:
function makeSizer() {
var size = 1; // or some other value
return function() {
document.body.style.fontSize = size + 'px';
};
}
In the above case, the anonymous function, which is the return value, will 'remember' value of size variable and use it each time it is called.
MakeSizer should be treated as a 'factory' function that provides different values for local variable size.
You cannot acheive this kind of 'remembering' without using a function definition statement inside of 'factory' function.
Onclick of 18 your calling makeS. It does not return anything to size18,siz18 would be undefined here,if you want to make it work through clousre change makeS(),to return a method as similar to makeSizer
check this snippet
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
function makeS(size) {
return function() {
document.body.style.fontSize = size + 'px';
}
}
/* Let's see if that works */
var size18 = makeS(18);
document.getElementById('size-18').onclick = size18;
/* What about that? */
document.getElementById('size-20').onclick = makeS(20);
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
<h1>How closures work</h1>
12
14
16
<div>
18
20
</div>
Hope this helps
A closure refers to the concept of a function retaining access to variables in the lexical scope that it was declared within, even as it is passed, and called in different locations and scopes throughout a program.
Huh?
What does this mean then? Consider the below piece of code
function functionFactory() {
var x = 1;
return function () {
return ++x;
};
}
var first = functionFactory();
var second = functionFactory();
console.log(first()); // outputs 1
console.log(first()); // outputs 2
console.log(first()); // outputs 3
console.log(second()); // outputs 1
Each time functionFactory is called, it declares a private variable x, and then returns an anonymous function that mutates that variable. We can see in the output that each subsequent call of that returned function, first, returns a different value.
When we call second though, it returns 1. Why? Because it was created by a separate invokation of functionFactory, and so it has its own reference to its own closure over x.
Why do I care?
Because this is super-useful. In programming languages (like javascript, but other functional languages too) which treat functions as first-class variables, we can use closures for encapsulation of private variables.
So to your actual question...
The reason why your code is not working, is because onclick expects you to assign a function to it - instead you are assigning the output of your makeS function, which is void. makeSizer is returning a function, that establishes a closure over whatever number value is passed into it as a parameter, and is then assigned to the onclick variable of each element.
Hope this makes things clearer for you.

Doubts About Use of Practical Closure

I'm trying to find out more about closures in Javascript and was going through this: https://developer.mozilla.org/en/JavaScript/Guide/Closures#Practical_closures
According to this article, by using such a function:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
We can then make use of such statements to increase/decrease the font-size of text on a page:
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
While I understand the concept here - i.e. size12, size14 and size16 become closures that allow access to the internal function, I can't help but feel that this is unnecessary. Isn't it easier to just have:
function makeSizer(size) {
document.body.style.fontSize = size + 'px';
}
, and then invoke it with these?
document.getElementById('size-12').onclick = makeSizer(12);
document.getElementById('size-14').onclick = makeSizer(14);
document.getElementById('size-16').onclick = makeSizer(16);
Can anyone tell me if my thinking is right - or maybe I'm just a novice to Javascript and doesn't understand the advantage to using closure in this scenario, in which case I'll be most glad if you can explain the advantage of doing so.
Thanks in advance guys.
No, you can't do that.
It's as if you had written:
document.getElementById('size-12').onclick = (function(size) {
document.body.style.fontSize = size + 'px';
})(12);
The function gets immediately invoked, the style will be applied straight away, and no .onclick handler gets registered because the return value of the function is undefined.
The real point of the example is to show that you can return a function from another function, and that you can then assign that result to an event handler.
If you had left makeSizer() unmodified then you could assign the handlers as proposed without intermediate variables, i.e.:
document.getElementById('size-12').onclick = makeSizer(12);
but that won't work if you change makeSizer() the way you described.
It is also less efficient than storing the "sizer" in a variable if you use the same sizer more than once.
For the example you presented, of course closure is not necessary, but I guess it is just to make it simple to present the concept. There are cases though that closure is the best solution to use: think about how to implement a "private" attribute in javascript or when you need curryng to encapsulate arguments (ie, for a callback function).
I hope the following example helps:
var makeSequencer = function() {
var _count = 0; // not accessible outside this function
var sequencer = function () {
return _count++;
}
return sequencer;
}
var fnext = makeSequencer();
var v0 = fnext(); // v0 = 0;
var v1 = fnext(); // v1 = 1;
var vz = fnext._count // vz = undefined
Yes, those variables (sizeN) are unnecessary. You can directly assign the result of makeSizer() as handlers, which looks far better.
But, the use of these variables is not the concept of closures. The closure in this example is the function makeSizer, which returns a function (even without arguments), which still has access to the size variable.
Though, you need to see the difference between
function makeSizer(size) {
return function resize() {
document.body.style.fontSize = size + 'px';
};
}
and
function resize(size) {
document.body.style.fontSize = size + 'px';
}
Executing makeSizer(5) does not do anything, it returns a function that sets the size to the pre-defined size when invoked. Instead executing resize(5) does set the size directly. You can't use the result of the latter function as an event handler.

Write a wrapper object in Javascript

First off, let me apologize if my question isn't worded correctly - I'm not a professional coder so my terminology might be weird. I hope my code isn't too embarrassing :(
I have a fade() method that fades an image in and out with a mouse rollover. I would like to use a wrapper object (I think this is the correct term), to hold the image element and a few required properties, but I don't know how to accomplish this. fade() is called from the HTML, and is designed to be dropped into a page without much additional setup (so that I can easily add new fading images to any HTML), just like this:
<div id="obj" onmouseover="fade('obj', 1);" onmouseout="fade('obj', 0);">
The fade(obj, flag) method starts a SetInterval that fades the image in, and when the pointer is moved away, the interval is cleared and a new SetInterval is created to fade the image out. In order to save the opacity state, I've added a few properties to the object: obj.opacity, obj.upTimer, and obj.dnTimer.
Everything works okay, but I don't like the idea of adding properties to HTML elements, because it might lead to a future situation where some other method overwrites those properties. Ideally, I think there should be a wrapper object involved, but I don't know how to accomplish this cleanly without adding code to create the object when the page loads. If anyone has any suggestions, I would greatly appreciate it!
Here's my fader method:
var DELTA = 0.05;
function fade(id, flag) {
var element = document.getElementById(id);
var setCmd = "newOpacity('" + id + "', " + flag + ")";
if (!element.upTimer) {
element.upTimer = "";
element.dnTimer = "";
}
if (flag) {
clearInterval(element.dnTimer);
element.upTimer = window.setInterval(setCmd, 10);
} else {
clearInterval(element.upTimer);
element.dnTimer = window.setInterval(setCmd, 10);
}
}
function newOpacity(id, flag) {
var element = document.getElementById(id);
if (!element.opacity) {
element.opacity = 0;
element.modifier = DELTA;
}
if (flag) {
clearInterval(element.dnTimer)
element.opacity += element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity > 100) {
element.opacity = 100;
element.modifier = DELTA;
return;
}
element.opacity = Math.ceil(element.opacity);
} else {
clearInterval(element.upTimer)
element.opacity -= element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity < 0) {
element.opacity = 0;
element.modifier = DELTA;
return;
}
element.opacity =
Math.floor(element.opacity);
}
setStyle(id);
}
function setStyle(id) {
var opacity = document.getElementById(id).opacity;
with (document.getElementById(id)) {
style.opacity = (opacity / 100);
style.MozOpacity = (opacity / 100);
style.KhtmlOpacity = (opacity / 100);
style.filter = "alpha(opacity=" + opacity + ")";
}
}
You are right, adding the handlers in your HTML is not good. You also loose the possible to have several handlers for event attached to one object.
Unfortunately Microsoft goes its own way regarding attaching event handlers. But you should be able to write a small wrapper function to take care of that.
For the details, I suggest you read quirksmode.org - Advanced event registration models.
An example for W3C compatible browsers (which IE is not): Instead of adding your event handler in the HTML, get a reference to the element and call addEventListener:
var obj = document.getElementById('obj');
obj.addEventListener('mouseover', function(event) {
fade(event.currentTarget, 1);
}, false);
obj.addEventListener('mouseout', function(event) {
fade(event.currentTarget, 0);
}, false);
As you can see I'm passing directly a reference to the object, so in you fade method you already have a reference to the object.
You could wrap this in a function that accepts an ID (or reference) and every time you want to attach an event handler to a certain element, you can just pass the ID (or reference) to this function.
If you want to make your code reusable, I suggest to put everything into an object, like this:
var Fader = (function() {
var DELTA = 0.05;
function newOpacity() {}
function setStyle() {}
return {
fade: function(...) {...},
init: function(element) {
var that = this;
element.addEventListener('mouseover', function(event) {
that.fade(event.currentTarget, 1);
}, false);
element.addEventListener('mouseout', function(event) {
that.fade(event.currentTarget, 0);
}, false);
}
};
}())
Using an object to hold your functions reduces pollution of the global namespace.
Then you could call it with:
Fader.init(document.getElementById('obj'));
Explanation of the above code:
We have an immediate function (function(){...}()) which means, the function gets defined and executed (()) in one go. This function returns an object (return {...};, {..} is the object literal notation) which has the properties init and fade. Both properties hold functions that have access to all the variables defined inside the immediate function (they are closures). That means they can access newOpacity and setStyle which are not accessible from the outside. The returned object is assigned to the Fader variable.
This doesn't directly answer your question but you could use the jQuery library. It's simple, all you have to do is add a script tag at the top:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js">
Then your div would look like:
<div id="obj" onmouseover="$('#obj').fadeIn()" onmouseout="$('#obj').fadeOut()">
jQuery will handle all the browser dependencies for you so you don't have to worry about things like differences between firefox and mozilla etc...
If you want to keep your HTML clean, you should consider using JQuery to set up the events.
Your HTML will look like this:-
<div id="obj">
Your JavaScript will look "something" like this:-
$(document).ready(function() {
$("#obj").mouseover(function() {
Page.fade(this, 1);
}).mouseout(function(){
Page.fade(this, 0);
});
});
var Page = new function () {
// private-scoped variable
var DELTA = 0.05;
// public-scoped function
this.fade = function(divObj, flag) {
...
};
// private-scoped function
var newOpacity = function (divObj, flag) {
...
};
// private-scoped function
var setStyle = function (divObj) {
...
};
};
I introduced some scoping concept in your Javascript to ensure you are not going to have function overriding problems.

call function inside a nested jquery plugin

There are many topics related to my question and i have been through most of them, but i haven't got it right. The closest post to my question is the following:
How to call functions that are nested inside a JQuery Plugin?
Below is the jquery plugin i am using. On resize, the element sizes are recalculated. I am now trying to call the function resizeBind() from outside of the jquery plugin and it gives me error
I tried the following combinations to call the function
$.fn.splitter().resizeBind()
$.fn.splitter.resizeBind()
Any ideas, where i am getting wrong?
;(function($){
$.fn.splitter = function(args){
//Other functions ......
$(window).bind("resize", function(){
resizeBind();
});
function resizeBind(){
var top = splitter.offset().top;
var wh = $(window).height();
var ww = $(window).width();
var sh = 0; // scrollbar height
if (ww <0 && !jQuery.browser.msie )
sh = 17;
var footer = parseInt($("#footer").css("height")) || 26;
splitter.css("height", wh-top-footer-sh+"px");
$("#tabsRight").css("height", splitter.height()-30+"px");
$(".contentTabs").css("height", splitter.height()-70+"px");
}
return this.each(function() {
});
};
})(jQuery);
I had the same problem. Those answers on related posts didn't work for my case either. I solved it in a round about way using events.
The example below demonstrates calling a function that multiplies three internal data values by a given multiplier, and returns the result. To call the function, you trigger an event. The handler in turn triggers another event that contains the result. You need to set up a listener for the result event.
Here's the plugin - mostly standard jQuery plugin architecture created by an online wizard:
(function($){
$.foo = function(el, options){
// To avoid scope issues, use 'base' instead of 'this'
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Add a reverse reference to the DOM object
base.$el.data("foo", base);
base.init = function(){
base.options = $.extend({},$.foo.defaultOptions, options);
// create private data and copy in the options hash
base.private_obj = {};
base.private_obj.value1 = (base.options.opt1);
base.private_obj.value2 = (base.options.opt2);
base.private_obj.value3 = (base.options.opt3);
// make a little element to dump the results into
var ui_element = $('<p>').attr("id","my_paragraph").html(base.private_obj.value1 +" "+ base.private_obj.value2+" " +base.private_obj.value3);
base.$el.append(ui_element);
// this is the handler for the 'get_multiplied_data_please' event.
base.$el.bind('get_multiplied_data_please', function(e,mult) {
bar = {};
bar.v1 = base.private_obj.value1 *mult;
bar.v2 = base.private_obj.value2 *mult;
bar.v3 = base.private_obj.value3 *mult;
base.$el.trigger("here_is_the_multiplied_data", bar);
});
};
base.init();
}
$.foo.defaultOptions = {
opt1: 150,
opt2: 30,
opt3: 100
};
$.fn.foo = function(options){
return this.each(function(){
(new $.foo(this, options));
});
};
})(jQuery);
So, you can attach the object to an element as usual when the document is ready. And at the same time set up a handler for the result event.
$(document).ready(function(){
$('body').foo();
$('body').live('here_is_the_multiplied_data', function(e, data){
console.log("val1:" +data.v1);
console.log("val2:" +data.v2);
console.log("val3:" +data.v3);
$("#my_paragraph").html(data.v1 +" "+ data.v2+" " +data.v3);
});
})
All that's left is to trigger the event and pass it a multiplier value
You could type this into the console - or trigger it from a button that picks out the multiplier from another UI element
$('body').trigger('get_multiplied_data_please', 7);
Disclaimer ;) - I'm quite new to jQuery - sorry if this is using a hammer to crack a nut.
resizeBind function is defined as private so you cannot access it from outside of it's scope. If you want to use it in other scopes you need to define it like that
$.fn.resizeBind = function() { ... }
Then you would call it like that $(selector').resizeBind()
You have defined the resizeBind function in a scope that is different from the global scope. If you dont'use another javascript framework or anything else that uses the $ function (to prevent conflict) you can delete the
(function($){
...
})(jQuery);
statement and in this way the function will be callable everywhere without errors
I didn't test it:
this.resizeBind = function() { .... }

Categories

Resources