Javascript closure (this) [duplicate] - javascript

This question already has answers here:
Methods in ES6 objects: using arrow functions
(6 answers)
Closed 6 years ago.
I have already looked everywhere on stackoverflow, but couldn't find any answer for this.
Uncaught TypeError: this.rsGame is not a function (same about this.addEnemy)
let game = new Phaser.Game(600,600);
let speed = 500;
let scyla = {
preload: () => {
game.load.image('bg', 'assets/bg.png');
game.load.image('pl', 'assets/pl.png');
game.load.image('enemy', 'assets/enemy.png');
},
create: () => {
game.physics.startSystem(Phaser.Physics.ARCADE)
game.add.sprite(0,0, 'bg');
this.player = game.add.sprite(300, 500, 'pl');
this.player.anchor.set(0.5);
game.physics.arcade.enable(this.player);
this.cursors = game.input.keyboard.createCursorKeys();
this.enemies = game.add.group();
// this.timer = game.time.events.loop(200, this.addEnemy(), this);
},
update: () => {
this.player.body.velocity.x = 0;
this.player.body.velocity.y = 0;
if (this.cursors.left.isDown)
this.player.body.velocity.x = speed * -1;
if (this.cursors.right.isDown)
this.player.body.velocity.x = speed;
if (this.cursors.up.isDown)
this.player.body.velocity.y = speed * -1;
if (this.cursors.down.isDown)
this.player.body.velocity.y = speed;
if (this.player.inWorld === false)
this.rsGame();
},
rsGame: () => {
game.state.start('scyla');
},
addEnemy: () => {
let enemy = game.add.sprite(300, 100, 'enemy');
game.physics.arcade.enable(enemy);
enemy.body.gravity.y = 200;
this.enemies.add(enemy);
enemy.checkWorldBounds = true;
enemy.outOfBoundsKill = true;
}
}
game.state.add('scyla', scyla);
game.state.start('scyla');
I tried things like
let self = this
this return the windows object anyway. This has something to do with closure, but I don't understand exactly
don't know how to solve this :/

Arrow function set this to the lexical scope. You're trying to access the scyla object but the arrow function is setting it to window (or whatever this is equal to at the time that you declare scyla).
Either reference scyla directly:
scyla.rsGame();
or write your methods using standard function expressions:
update: function() {
...
if (this.player.inWorld === false)
this.rsGame();
}
or shorthand method declarations:
update() {
...
if (this.player.inWorld === false)
this.rsGame();
}

Arrow functions preserve the value of this from when they are declared.
Use a regular function expression. Don't use arrow functions for this.
preload: function preload () {
// etc
}

Arrow functions have lexically scoped this. You need to use regular functions in your case so that this gets binded as usual. Change:
update: () => {
To:
update: function ( ) {
and similarly for the other properties of scyla.

Related

this.interval is undefined inside a class function [duplicate]

This question already has answers here:
"this" keyword in event methods when using JavaScript prototype object
(4 answers)
Closed 3 years ago.
in summarize I want to blink an object material in BJS, so I create two functions blink() and stopBlink():
function blink(obj, delay, box) {
var changeMaterial = function(obj, newMaterial) {
{
obj.material = newMaterial;
}
};
oldMaterial = obj.material;
interval = (function() {
var Toggle = true;
return setInterval(function() {
if (Toggle) changeMaterial(obj, box);
else {
changeMaterial(obj, OriginalStairMaterial);
}
Toggle = !Toggle;
}, delay);
})();
}
function stopBlink(obj, duration) {
setTimeout(function() {
obj.material = oldMaterial;
clearInterval(interval);
}, duration);
}
So I called my function inside JQuery Ready function :
$(document).ready(function() {
$("#btn1").click(function() {
if(Toggle1)
blink(stairMesh,100,boxMaterial[0]);
else
stopBlink(stairMesh,500);
Toggle1=!Toggle1;
}
);
until now everything works good :), I decided to creat a class and implement my functions inside it (because I want to use polymorphism to redefine blink() and stopBlink() functions).
class A {
blink() {}
stopBlink() {}
}
class B extends A {
constructor(oldMaterial) {
super();
this.oldMaterial = oldMaterial;
this.interval = null;
}
blink(obj, delay, box, duration) {
var changeMaterial = function(obj, newMaterial) {
{
obj.material = newMaterial;
}
};
//var oldMaterial=obj.material;
this.interval = (function() {
var Toggle = true;
return setInterval(
function() {
if (Toggle) changeMaterial(obj, box);
else {
changeMaterial(obj, OriginalStairMaterial);
}
Toggle = !Toggle;
},
delay
);
})();
console.log(this.interval);
}
stopBlink(obj, duration) {
setTimeout(function() {
obj.material = oldMaterial;
console.log(this.interval);
clearInterval(this.interval);
}, duration);
}
}
and I create a new object inside ready function:
$(document).ready(function() {
var a = new B();
$("#btn1").click(function() {
if(Toggle1)
a.blink(stairMesh,100,boxMaterial[0]);
else
a.stopBlink(stairMesh,500);
Toggle1=!Toggle1;
}
);
The problem is, when I click the button the blink() function works goos and my object blinks, but when I click again in order to stop blining it continues to blink!
I tried to console.log(this.interval) in blink(), it gives me a number (11), but when I console it in stopBlink() it is undefined!
Your click handlers are not being bound to the context of your class. An easy fix is to bind them inside your constructor. So, you're setting this.interval inside of blink but it only exists in the context of that function. By binding it to the class, this will refer to the context of the class, instead of the function context.
constructor(oldMaterial)
{
super();
this.oldMaterial=oldMaterial;
this.interval=null;
this.blink = this.blink.bind(this);
this.stopBlink = this.stopBlink.bind(this);
}

getting error "property does not exist" [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I modified my question to be more specific. now i don't care about the desired behavior and i just need to correct syntax error
I was studying this tutorial I face with an error in this code.
severity: 'Error'
message: 'Property 'offset' does not exist on type 'PagerserviceProvider'.'
actually i have the same error for this three variables.
that.pageSize,that.offset,that.size
public async getPager(tableName:string,pageSize: number = 10) {
let pageSize = pageSize;
let offset = 0;
let limit = pageSize;
let size = await this.getTotal(tableName);
let that = this;
return {
initialPage:function(){
return new Promise((resolve,reject)=>{
var d = [];
that.executeSql(tableName,limit,offset).then((data)=>{
console.log(JSON.stringify(data));
for(var i = 0 ; i < data.rows.length ; i++)
{
d.push(data.rows.item(i));
}
resolve(d);
},(e)=>{
reject(e);
});
});
},
nextPage:function(){
if(that.offset <= that.size - that.pageSize )
{
that.offset += that.pageSize;
}
return new Promise((resolve,reject)=>{
var d = [];
that.executeSql(tableName,limit,offset).then((data)=>{
for(var i = 0 ; i < data.rows.length ; i++)
{
d.push(data.rows.item(i));
}
resolve(d);
},(e)=>{
reject(e);
});
});
}
};}
When you use the keyword function to declare a function, the function's this does not refer to the upper this. so using this in the function body, refers to the function itself.
The problem you are facing is related to the fact that your function is declared inside a class which already have a this defined, so you need a way to reference the upper this while being inside the nested function.
class Test {
hello () { console.log('hello') }
method () {
this.hello() // It will work because `this` refers to the class
function sayHello () {
return this.hello()
// it won't work because `this` refers to the function sayHello
}
return sayHello()
}
}
To bypass this limitation, you can save your upper this in a variable while your code is in the upper scope. This variable is usually called that or self.
class Test {
hello () { console.log('hello') }
method () {
var that = this // that is now refering to the class
this.hello() // It will work because `this` refers to the class
function sayHello () {
return that.hello()
// that is still refering to the class so it will succeed
}
return sayHello()
}
}
EDIT:
another trick to avoid using that is to use ES6 arrow function. Inside an arrow function, this alway refers to the upper scope.
class Test {
hello () { console.log('hello') }
method () {
this.hello() // It will work because `this` refers to the class
// `this` refers to the upper scope by default so it works
const sayHello = () => this.hello()
return sayHello()
}
}
EDIT 2:
Your code should be:
public async getPager(tableName: string, pageSize: number = 10) {
let pageSize = pageSize;
let offset = 0;
let limit = pageSize;
let size = await this.getTotal(tableName);
let that = this;
return {
initialPage: function () {
return new Promise((resolve,reject)=>{
var d = [];
that.executeSql(tableName, limit, offset).then(data => {
console.log(JSON.stringify(data));
for(var i = 0 ; i < data.rows.length ; i++) {
d.push(data.rows.item(i));
}
resolve(d);
}, e => {
reject(e);
});
});
},
nextPage: function () {
if(offset <= size - pageSize ) {
offset += pageSize;
// no need to use `that` because you used `let`
}
return new Promise((resolve, reject) => {
var d = [];
that.executeSql(tableName, limit, offset).then(data => {
for(var i = 0 ; i < data.rows.length ; i++) {
d.push(data.rows.item(i));
}
resolve(d);
}, e => {
reject(e);
});
});
}
};
}
'that' is just the name of a variable used to store the original value of 'this' at the start of your code, as the value of 'this' changes. The variable name could just as easily be 'dog' or 'apple'.
You might choose to use 'this' later on in your code instead if you intend to access the current value of 'this' at that point in time. Otherwise you will probably reference your original variable that stored it's value, e.g. 'that', 'dog' or 'apple'.
getPager is a method: If you call it with an already lost context, that will get the current this value, which is not pointing the correct context.
const someInstance = new SomeClass()
someInstance.getPager() // You call getPager directly from the someInstance context
someHTMLButton.onClick = someInstance.getPager // Here the getPager method lost its context
A solution is to bind getPager to someInstance. This way it will always have its context this pointing to someInstance.
someHTMLButton.onClick = someInstance.getPager.bind(someInstance)

JavaScript parameter value 'becomes' undefined when passed to constructor

I am creating a JavaScript component which I am creating instances of based on jQuery results however, the DOM element which I pass into the constructor, although populated when I step through the loop in the calling code, is undefined when passed to the constructor.
Here's my class and constructor...
export default class DeleteButton {
/**
* Creates an instance of DeleteButton.
*
* #param {object} element The DOM element to make into a delete button.
*
* #memberOf DeleteButton
*/
constructor(element) {
this.id = element.getAttribute("data-id");
if (!this.id) throw new Error("The 'data-id' attribute is required.");
this.deleteUri = element.getAttribute("data-delete-uri");
if (!this.deleteUri) throw new Error("The 'data-delete-uri' attribute is required.");
$(element).click(this.confirmRemove);
}
confirmRemove() { // does something }
}
and here's the calling code (This is a component manager that handles when to load components based on URLs / DOM state etc)...
export default class JsComponentManager {
constructor(onLoader) {
this._loader = onLoader;
this.select = {
deleteButtons: () => $(".js-delete-button")
}
this.result = 0;
}
bindComponents() {
const paths = new PathManager();
let $deleteButtons = this.select.deleteButtons()
if ($deleteButtons.length > 0) {
this._loader.add(this.renderDeleteButtons, $deleteButtons);
}
}
renderDeleteButtons($elements) {
$elements.each(() => {
document.DeleteButtons = document.DeleteButtons || [];
document.DeleteButtons.push(new DeleteButton(this));
});
}
}
This uses the following loader function to ensure that items are loaded...
/**
* Adds an event to the onload queue.
*
* #param {function} func The function to add to the queue.
* #param {any} param1 The first (optional) parameter to the function.
* #param {any} param2 The second (optional) parameter to the function.
*/
var AddLoadEvent = function (func, param1, param2) {
var oldonload = window.onload;
if (typeof window.onload !== "function") {
window.onload = () => { func(param1, param2); };
} else {
window.onload = () => {
if (oldonload) { oldonload(); }
func(param1, param2);
};
}
};
module.exports = {
add: AddLoadEvent
};
The onload management code seems to be running fine and, stepping through, code execustion is completely as expected until document.DeleteButtons.push(new DeleteButton(this)); - 'this' here is the DOM element, as I would expect, but as soon as the debugger steps into the controller the value is undefined.
Is this some odd scoping pain I've walked into?
renderDeleteButtons($elements) {
$elements.each(() => {
document.DeleteButtons = document.DeleteButtons || [];
document.DeleteButtons.push(new DeleteButton(this));
});
}
doesn't do what you think it does. jQuery relies on being able to set the this value of the callback function. But arrow functions don't have their own this, so jQuery cannot set the this value.
Inside the arrow function this will refer to whatever this refers to in renderDeleteButtons, which likely is an instance of JsComponentManager.
If you pass a function to another function and that function has to set the this value, you cannot use an arrow function. Use a function expression instead:
renderDeleteButtons($elements) {
$elements.each(function() {
document.DeleteButtons = document.DeleteButtons || [];
document.DeleteButtons.push(new DeleteButton(this));
});
}
See also: Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?
Maybe this helps to demonstrate the difference between an arrow function and a function declaration/expression:
// our library:
function each(values, callback) {
for (var i = 0; i < values.length; i++) {
// we use `.call` to explicitly set the value of `this` inside `callback`
callback.call(values[i]);
}
}
// Function declaration/expression
var obj = {
someMethod() {
"use strict";
each([1,2,3], function() {
console.log('function declaration:', this);
});
},
};
// Because we use a function expression, `each` is able to set the value of `this`
// so this will log the values 1, 2, 3
obj.someMethod();
// Arrow function
obj = {
someMethod() {
each([1,2,3], () => {
"use strict";
console.log('arrow function:', this);
});
},
};
// `this` is resolved lexically; whatever `each` sets is ignored
// this will log the value of `obj` (the value of `this` inside `someMethod`)
obj.someMethod();
I've now got this working by abandonning jQuery.each (which seems to have serious scoping issues passing the element from the array to anything else due to the way it messes with 'this'). I solved this by using a JS forEach call instead as follows. Discovering jQuery's makeArray method was the key. This is similar to what I had started with originally but was banging my head against forEach not working on a jQuery object...
renderDeleteButtons($elements) {
$.makeArray($elements).forEach((el) => {
document.DeleteButtons = document.DeleteButtons || [];
document.DeleteButtons.push(new DeleteButton(el));
});
}
It also doesn't hurt my sensibilities by doing weird stuff with 'this' (for Felix)
See Felix's extra info on lexical scoping with 'this' at Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?

Why object property became undefined when using setInterval

As below code, I make an object named "test", and give it properties and method.
The property came from its argument.
And I try to call the method every 2 sec after onload, and the result shows undefined.
But if I only call the method not using setInterval(), like this
window.onload = function() {
giveword.showWord();
}
I'll be able to show the text "Hi".. Why is that?
var giveword = new test("Hi");
function test(word) {
this.word = word;
}
test.prototype.showWord = function() {
document.getElementById("msg_box").innerHTML = this.word;
}
window.onload = function() {
setInterval(giveword.showWord, 2000);
}
Thanks for help...
The reason is because in your test.prototype.showWord function your this object is referring to the context in which the function is called, which is the window object when called from setInterval.
I think what you want to do is use a closure to make the context of showWord() be the giveword instance like this:
var giveword = new test("Hi");
function test(word) {
this.word = word;
}
test.prototype.showWord = function() {
document.getElementById("msg_box").innerHTML = this.word;
}
window.onload = function(){
setInterval(function(){giveword.showWord();}, 2000); // <<-- here's the closure
}
The difference is that with the closure you're telling the setInterval function to call a function within the context as it was when the setInterval was declared. When setInterval was declared there was a variable in scope called giveword that had a method showWord() that returns the value of your initial input. (Closures are hard to explain, and I'm afraid you'd be best served by someone else explaining them if you need more info.)
This solution this now so easy, use an arrow function in setInterval. Here is an example using setInterval inside of an object method.
const mobile = {
make: 'iPhone',
model: 'X',
battery: 10,
charging: false,
charge: function() {
if(this.battery < 100) {
this.charging = true;
console.info('Battery is charging...');
let interval = setInterval(() => {
this.battery = this.battery + 10;
console.info(mobile.battery);
if( this.battery === 100){
this.charging = false;
clearInterval(interval);
console.info('Battery has finished charging.');
}
}, 100);
}
else {
console.info('Battery does not need charging.');
}
}
}

What is the best way to have a function toggle between two different processes?

I have a function that I want it execute alternating processes every time it's triggered. Any help on how I would achieve this would be great.
function onoff(){
statusOn process /*or if on*/ statusOff process
}
One interesting aspect of JavaScript is that functions are first-class objects, meaning they can have custom properties:
function onoff() {
onoff.enabled = !onoff.enabled;
if(onoff.enabled) {
alert('on');
} else {
alert('off');
}
}
For this to work, your function should have a name. If your function is anonymous (unnamed), you can try to use arguments.callee to access it, but that is deprecated in the new ES5 standard and not possible when using its strict mode.
With the use of closures, you can define a static variable that is only accessible by the function itself:
var toggle = (function()
{
var state = true;
return function()
{
if(state)
alert("A");
else
alert("B");
state = !state;
};
})();
Now you can repeatedly invoke toggle(), and it would alternate between "A" and "B". The state variable is unaccessible from the outside, so you don't pollute the global variable scope.
Use closures. In addition to closures, this method demonstrates arbitrary arguments and arbitrary numbers of functions to cycle through:
Function cycler
function cycle() {
var toCall = arguments;
var which = 0;
return function() {
var R = toCall[which].apply(this, arguments);
which = (which+1) % toCall.length; // see NOTE
return R;
}
}
Demo:
function sum(a,b) {return a+b}
function prod(a,b) {return a*b}
function pow(a,b) {return Math.pow(a,b)}
function negate(x) {return -x;}
var f = cycle(sum, prod, pow, negate);
console.log(f(2,10)); // 12
console.log(f(2,10)); // 20
console.log(f(2,10)); // 1024
console.log(f(2)); // -2
// repeat!
console.log(f(2,10)); // 12
console.log(f(2,10)); // 20
console.log(f(2,10)); // 1024
console.log(f(2)); // -2
Arbitrary cycler
Alternatively if you do not wish to assume all cycled things are functions, you can use this pattern. In some ways it is more elegant; in some ways it is less elegant.
function cycle() {
var list = arguments;
var which = 0;
return function() {
var R = list[which];
which = (which+1) % toCall.length; // see NOTE
return R;
}
}
Demo:
var cycler = cycle(function(x){return x}, 4, function(a,b){return a+b});
cycler()(1); // 1
cycler(); // 4
cycler()(1,5); // 6
// repeat!
cycler()(1); // 1
cycler(); // 4
cycler()(1,5); // 6
NOTE: Because javascript thinks 10000000000000001%2 is 0 (i.e. that this number is even), this function must be three codelines longer than necessary, or else you will only be able to call this function 10 quadrillion times before it gives an incorrect answer. You are unlikely to reach this limit in a single browsing session... but who knows
If I'm understanding what you want, this may be what you're looking for:
var AlternateFunctions = function() {
var one = function() {
// do stuff...
current = two;
}, two = function() {
// do stuff...
current = one;
}, current = one;
return function() {
current();
}
}();
Then calling AlternateFunctions(); will cycle between one() and two()
There are a couple of good answers already posted, but I'm wondering what you're trying to achieve. If you're keeping track of some DOM element's state, instead of having state saved within the function, you should check the state of the element so that the function isn't operating in a vacuum (and possibly not doing what you expect). You can check some attribute, e.g., class:
function onoff(obj){
if(obj.className === 'on') {
obj.className = 'off';
}else{
obj.className = 'on';
}
}
var last=0;
function toggle() {
if(last) {
last=0;
// do process 2
}
else {
last=1;
// do process 1
}
}
See jsfiddle demo
var status=true;
function onOff(el){
/*
* toggle
*/
status = (status ? false : true);
status
? el.html('on')
: el.html('off');
}

Categories

Resources