This question already has answers here:
JavaScript classes with getter and setter cause RangeError: Maximum call stack size exceeded
(5 answers)
Closed 6 years ago.
I want to convert Mike Herchel's Importing CSS Breakpoints into ES6 class. For this, I chose to use get and set to finally learn how to.
My code so far:
export default class Breakpoints {
constructor() {
this.value = null;
this.refreshValue();
window.addEventListener('resize', () => {
this.refreshValue();
});
}
refreshValue() {
let val = window.getComputedStyle(document.querySelector('body'), ':before').getPropertyValue('content').replace(/\"/g, '');
this.value = val;
}
set value(val) {
this.value = val;
}
get value() {
return this.value;
}
}
Problem is, when I run it, I am getting Maximum call stack size exceeded. Where I did go wrong?
There's absolutely no reason to use getters/setters here, they don't do anything else than a normal property would do.
When I run it, I am getting Maximum call stack size exceeded. Where I did go wrong?
Your getter returns the value of the property again, thereby invoking itself. Your settter sets the value of the property again, thereby invoking itself. Don't do that.
If you really wanted to use getters for some reason, go for
export default class Breakpoints {
constructor() {
this._val = null;
this.refreshValue();
window.addEventListener('resize', () => {
this.refreshValue();
});
}
refreshValue() {
this._val = window.getComputedStyle(document.querySelector('body'), ':before').getPropertyValue('content').replace(/\"/g, '');
}
get value() {
return this._val;
}
// no `value` setter, because it can't be changed from outside
}
Related
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 9 months ago.
Improve this question
I have log set text and it is triggered 3 times why ?
see behavior here https://jsfiddle.net/5kv2g6hc/
class Test {
set text(text) {
console.log(text); // 3 times ?!!!
this.text = text;
}
constructor() {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => {
response.text().then((response) => {
this.text = response;
// console.log(this.text);
});
});
}
}
let test = new Test();
Actually, it's called more than 3 times. It's a recursive call.
It is first initialized in the promise handler by using this.text = response.
Then, within the setter you call this.text = text which basically triggers the same setter once again. And so it goes indefinitely (limited by V8 stack and stack overflow error is thrown).
When setters are used new props are created to store the value, because name of a getter/setter cannot be the same as the one storing the value.
So your code should be modified. There are two ways. The old one by using an underscore to tell that it's a private prop and shouldn't be used directly from the outside
class Test {
set text(text) {
console.log(text);
this._text = text; // <- here _text instead of text
}
constructor() {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => {
response.text().then((response) => {
this.text = response;
// console.log(this.text);
});
});
}
}
let test = new Test();
And a new one which uses the new syntax of real private properties introduced in JS recently
class Test {
#text; // <- first, declare the private prop
set text(text) {
console.log(text);
this.#text = text; // <- then use #text instead of text
}
constructor() {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => {
response.text().then((response) => {
this.text = response;
// console.log(this.text);
});
});
}
}
let test = new Test();
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
Example :
Creating function
const isElementLoaded = () => {
//Logic
return true;
}
Calling function using this.isElementLoaded :
if (this.isElementLoaded)
{
//output true
}
If you don't want to use (), you need to make it an accessor property. (The only other way to call it without () is to use it as a tag function on a tagged template literal, which doesn't seem relevant to what you're doing. :-) )
In a class, you'd do that like this:
class Example {
get isElementLoaded() {
return /*...appropriate value...*/;
}
}
In an object literal, it's basically the same:
const obj = {
get isElementLoaded() {
return /*...appropriate value...*/;
}
};
In either case, TypeScript should be able to infer the return type from your return statement, but if necessary add the annotation, e.g.:
class Example {
get isElementLoaded(): boolean {
return /*...appropriate value...*/;
}
}
If you want to add an accessor to an object after it's created, you use defineProperty:
Object.defineProperty(obj, "isElementLoaded", {
get() {
return /*...appropriate value...*/;
},
// flags here if appropriate; they default to `false` if omitted
});
You'll need to be sure the type information for the object includes the accessor (perhaps as an optional property).
Either you use a function and then you need to use () to call the function (that's the TypeScript / JavaScript syntax):
const isElementLoaded = () => {
//Logic
return true;
}
// in a function:
if (this.isElementLoaded()) {
//output true
}
Or, you can use a property on your class with a getter:
get isElementLoaded() {
//Logic
return true;
}
// in a function
if (this.isElementLoaded) {
//output true
}
tl;dr: use this.isElementLoaded()
You are actually not calling the function you created there. You are using arrow notation to create a function, you could do the same with
function isElementLoaded(...){...}
which would it make more clear maybe that you have to call it to actually do something. By just calling the name of the function you reference to the function body - but that is not progressed in any way.
Here's a quick try-out example: https://jsbin.com/kekifugoqe/edit?js,console
Hit 'run' and you see the difference in the output.
I have problem and I don't know how to solve it:
this.bWords.push(word);
^
TypeError: Cannot read property 'push' of undefined
here is my code:
function multiWords(words) {
class AWords {
constructor(words = []) {
this.words = words;
this.addWordFn = () => {};
}
setAddWordFn(fn) {
this.addWordFn = fn;
}
passWords() {
this.words.forEach(word => this.addWordFn(word));
}
}
class BWords {
constructor() {
this.bWords = [];
}
addWord(word) {
this.bWords.push(word);
}
}
let x = new AWords(words);
let y = new BWords();
x.setAddWordFn(y.addWord);
x.passWords();
return y.bWords;
}
console.log(multiWords(["one", "two", "three"]));
Do you have any ideas why there is different this value?
Many thanks
Pati
It appears that the problem occurs here:
this.words.forEach(word => this.addWordFn(word));
because the function you've set for addWordFn here:
x.setAddWordFn(y.addWord);
Needs a different value of this than you are calling it with. You can fix it by binding the right value of this to your callback:
x.setAddWordFn(y.addWord.bind(y));
Remember that for regular functions, the value of this inside the function is determined by how the function is called. When you call a function with obj.method(), the this value inside of method() will be set to obj. So, you're calling addWord with the wrong this value because you've made it a method of some other object (that does not also have the data it needs) and are calling it off that object.
This question already has an answer here:
How to destructure option argument with all default values in ES6?
(1 answer)
Closed 3 years ago.
I have a problem with referencing to data with this while trying to declare default parameters combined with destructuring. Anyone knows how to do it?
activated () {
this.fillData()
},
data () {
return {
chartData: {
distributionInitialData: {
// data here
}
}
}
},
methods: {
fillData ({
must = this.chartData.distributionInitialData,
nice = this.chartData.distributionInitialData
}) {
// do something
// function doesn't even get here because it gets error:
// TypeError: Cannot read property 'must' of undefined
// at VueComponent.fillData (Component.vue?4e6a:230)
// at VueComponent.activated (Component.vue?4e6a:123)
}
}
You need to set the default value for the parameter(Object), but not to its properties.
methods: {
fillData ({must, nice} = {
must: this.chartData.distributionInitialData,
nice: this.chartData.distributionInitialData
}) {
// do something
}
}
Updated by Bergi's advice.
methods: {
fillData ({
must: this.chartData.distributionInitialData,
nice: this.chartData.distributionInitialData
} = {}) {
// do something
}
}
FYI, here is a simple codepen.
You can do.
fillData ({ must, nice}) {
must = must || this.chartData.distributionInitialData
nice = nice || this.chartData.distributionInitialData
// do something
}
So the issue has to do with how Vue binds the methods to the component as it is created. The function definition does not have access to the instance, but inside of it you may use this.
The easiest solution would be to do an immediate check for undefined and set based on the desired default.
fillData({ must, nice }) {
if (must === undefined) must = this.chartData.distributionInitialData;
if (nice === undefined) nice = this.chartData.distributionInitialData;
// any other code here
}
You might also try playing around with arrow functions to see if it fixes this, since that was introduced to be bound to this in a more concrete way. Perhaps something like this:
fillData = ({
must = this.chartData.distributionInitialData,
nice = this.chartData.distributionInitialData
}) => {
// any other code here
}
This question already has answers here:
Accessing an object property with a dynamically-computed name
(19 answers)
Closed 3 years ago.
i am writing a code to automate android and IOS using webdriverio
I have a piece of js code where i have defined some getter and depending upon the user input, i want to call a getter
var assert = require('assert')
const Page = require('./Page.js')
const SELECTORS = {
ANDROID: {
landing_header: '~landing_header',
sys_house: '~sys_house',
aero_center: '~aero_center',
},
IOS: {
//IOS Page Object
},
}
class Landing extends Page {
get landingHeader() {
return browser.isAndroid ? $$(SELECTORS.ANDROID.landing_header)[0] : $$(SELECTORS.ANDROID.landing_header)[0]
}
get sysHouseTile() {
return browser.isAndroid ? $$(SELECTORS.ANDROID.sys_house)[0] : $$(SELECTORS.ANDROID.sys_house)[0]
}
get settingsCenterTile() {
return browser.isAndroid ? $$(SELECTORS.ANDROID.aero_center)[0] : $$(SELECTORS.ANDROID.aero_center)[0]
}
navigateLandingPage(page) {
if(page=="settings") {
var lObj=eval(page+"CenterTile");
this.settingsCenterTile.click();//this is working fine
this.lObj.click();//this is not working
}
browser.pause(3000)
}
}
module.exports = new Landing()
navigateLandingPage() method is being called from another js file
Now my issue is depending on the page input,
i want to click any one method
and i can update the getter runtime, but don't know how to call it.
this.lObj.click()
Do not use eval() for this, you can use the computed property syntax object[propName] to dynamically invoke getters.
Just take a look at my sample code below, in the getters I am returning a function when it is invoked. When I accessed the property dynamically I invoked the getter and it returned me a function and i executed that:
class Landing{
get landingHeader() {
return () => {
console.log("landingHeader");
}
}
get sysHouseTile() {
return () => {
console.log("sysHouseTile");
}
}
get settingsCenterTile() {
return () => {
console.log("settingsCenterTile");
}
}
navigateLandingPage(page) {
if(page=="settings") {
var lObj = this[page+"CenterTile"]();
}
}
}
new Landing().navigateLandingPage("settings");