i am trying to get every image download URL from Firebase. It seems that the first 'this' is different from the second one. If i want to let the second 'this' equals to the value of the first, what should i do? Thank you so much!
getAllURL = product => {
// Get all the images from the firebase
var storage = firebase.storage();
console.log(this) // first this
const storageRef = storage.ref(`image/${product}`)
storageRef.listAll().then(function(result) {
result.items.forEach(function(imageRef) {
imageRef.getDownloadURL().then(function(url) {
console.log(this) // second this is undefined
}).catch(function(error) {});
})
})
}
componentDidMount() {
axios.get('/user/product')
.then(res=>{
if(res.data.code==0) {
this.setState({data:res.data.data},function(){
for (var i = 0; i < this.state.data.length; i++){
this.getAllURL(this.state.data[i].productName)
}
})
}
})
}
this is one the most confusing features in Javascript. I'd recommended you to look more into the topic.
As for a shortcut, there're many ways to take care of that.
First method: just assigned the first this into some variable first.
getAllURL = product => {
// Get all the images from the firebase
var storage = firebase.storage();
console.log(this) // first this
var firstThis = this; // some people prefered to assign "var that = this;", lol
const storageRef = storage.ref(`image/${product}`)
storageRef.listAll().then(function(result) {
result.items.forEach(function(imageRef) {
imageRef.getDownloadURL().then(function(url) {
console.log(firstThis); // use the new variable to refer to the firstThis
}).catch(function(error) {});
});
});
}
Second method: make use of bind function in javascript (a bit more advanced, and much better received from a functional programming perspective)
getAllURL = product => {
// Get all the images from the firebase
var storage = firebase.storage();
console.log(this) // first this
const storageRef = storage.ref(`image/${product}`)
storageRef.listAll().then((function(result) {
result.items.forEach((function(imageRef) {
imageRef.getDownloadURL().then((function(url) {
console.log(this);
}).bind(this)).catch(function(error) {}); // yet another binding to propagate "this" inside
}).bind(this)); // another binding to force "this" on this yet another inline function to equal the first this (propagate it down)
}).bind(this)); // this binding will force "this" inside this inline function to equals the firstThis
}
Note: It might get less confusing if amount of inline functions is reduced
getAllURL = product => {
// Get all the images from the firebase
var storage = firebase.storage();
console.log(this) // first this
const storageRef = storage.ref(`image/${product}`)
storageRef.listAll().then(listAllCallback.bind(this));
}
function listAllCallback(result) {
for (var i = 0; i<result.items.length; i++) {
var imageRef = result.items[i];
imageRef.getDownloadURL()
.then(downloadUrlCallback.bind(this))
.catch(function(error) {});
}
}
function downloadUrlCallback(url) {
console.log(this); // second this
}
Related
I'm working on a pet project, a little front-end library for students. It reads variables/code in a JS file and tests it, outputting some panels. The code itself roughly follows the Jest framework.
My problem is that I'm trying to create a function that watches the execution of other functions, counts them, and lets me access the count.
function watchFunction(funcName){
let originalFunction = window[funcName];
let counter = 0;
// Wrap the function, counts when called
window[funcName] = function(...args){
console.log("watching");
counter++;
return originalFunction(...args);
}
return {
getCount: () => {return counter},
reset: () => {
// Unwrap the function
window[funcName] = originalFunction
}
}
}
This seems to work for methods like Number() or parseInt(), but I don't know how I would go about accessing methods like Math.floor(), or prototype methods like Array.prototype.map().
I've tried passing in the function reference instead of using window["funcNameString"], but that doesn't seem to work.
Does anyone have suggestions or tips for wrapping functions or watching functions like this?
EDIT:
It appears a solution was found!
function watchFunction(obj, fName) {
let counter = 0;
const originalFunction = obj[fName];
obj[fName] = (...args) => {
counter++;
return originalFunction.bind(obj)(...args);
};
return {
removeWatcher: () => (obj[fName] = originalFunction),
resetCount: () => (counter = 0),
getCount: () => counter,
};
}
Example of use:
// Array.prototype.push
const arrayPushWatcher = watchFunction(Array.prototype, "push");
let arr = [];
// 0
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arr.push(1);
// 1
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arr.push(1);
// 2
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arrayPushWatcher.removeWatcher();
arr.push(1);
// 2 (stopped counting)
console.log("Array.prototype.push", arrayPushWatcher.getCount());
How to watch for any function call
Is that what you want? I can also write a block for this function so that it determines whether an object has been passed in or a string. If string -> run this function on window as a property "objectThatStoresFunction".
I've tried playing around with the Function.prototype, but it doesn't really work. So the function turned out a bit more complex.
This code below works both with functions / objects on window Array.prototype.map (Prototype / Class functions)
function watchFunction(objectThatStoresFunction, functionName) {
let counter = 0;
const originalFunction = objectThatStoresFunction[functionName];
objectThatStoresFunction[functionName] = (...args) => {
counter += 1;
return originalFunction(...args);
}
return {
getCount: () => {
return counter
}
}
}
const mathRoundWatcher = watchFunction(Math, 'round');
// 0
console.log(mathRoundWatcher.getCount());
// 1
Math.round(99666.9999999);
console.log(mathRoundWatcher.getCount());
// 2
Math.round(999999999.99);
console.log(mathRoundWatcher.getCount());
function watchFunction(objectThatStoresFunction, functionName, optionalOriginalFunction) {
const self = this;
if (optionalOriginalFunction) {
objectThatStoresFunction = this.window;
functionName = optionalOriginalFunction.name;
}
let counter = 0;
const originalFunction = objectThatStoresFunction[functionName] || optionalOriginalFunction;
objectThatStoresFunction[functionName] = (...args) => {
counter += 1;
return originalFunction.bind(self)(...args);
}
return {
// should it remove the watcher or reset the count?
reset: () => objectThatStoresFunction[functionName] = originalFunction,
getCount: () => {
return counter;
}
}
}
const arrayMapWatcher = watchFunction(Array.prototype, 'map');
// 0
console.log('Array.prototype.map', arrayMapWatcher.getCount());
[-99].map(() => {});
// 1
console.log('Array.prototype.map', arrayMapWatcher.getCount());
const mathRoundWatcher = watchFunction(Math, 'round');
// 0
console.log('Math.round', mathRoundWatcher.getCount());
// 1
Math.round(99666.9999999);
console.log('Math.round', mathRoundWatcher.getCount());
// 2
Math.round(999999999.99);
console.log('Math.round', mathRoundWatcher.getCount());
const alertWatcher = watchFunction(null, null, window.alert);
// 0
console.log('window.alert', alertWatcher.getCount());
// 1
window.alert('1');
console.log('window.alert', alertWatcher.getCount());
// 2
alert('2')
console.log('window.alert', alertWatcher.getCount());
// reset the alertWatcher counter
alertWatcher.reset();
This code above breaks the stacksnippets.com when used with Array.prototype.map for some reason, please see this JsFiddle link:
https://jsfiddle.net/ctbjnawz/3/
Do you mean a method of an instance or object? One way is to create a new function. e.g
function WatchInstanceMethods(instance, functionName){
let originalFunction = window[instance][funcName];
let counter = 0;
window[instance][functionName] = function(...args){
console.log("watching");
counter++;
return originalFunction(...args);
}
return {
getCount: () => {return counter},
reset: () => {
// Unwrap the function
window[funcName] = originalFunction
}
}
}
although adding support for chaining methods will get difficult with more nested methods but you can pass a string for functionName name and split it to have each layer of calling instance for function and repeat the logic above.
So I've been using a bunch of these in my main javascript initiation file, there's like 20-30 of these that I need. Is there a way that I can export these from a different files so I can clear up my main file?
Reflect.defineProperty(numerator, 'getBalance', {
value: function getBalance(id) {
const TEMPLATEUser = numerator.get(id);
return TEMPLATEUser ? TEMPLATEUser.balance : 0;
},
});
I think you can create a function to abstract away what you're doing. Suppose you define this function in a file called file1.js and you want to use it in file2.js.
// file1.js
module.exports.defineGetBalance = obj => {
Reflect.defineProperty(obj, 'getBalance', {
value: function getBalance(id) {
const TEMPLATEUser = obj.get(id);
return TEMPLATEUser ? TEMPLATEUser.balance : 0;
},
});
};
Now you can call defineGetBalance() as many times as you can, you just have to pass the object you want to assign that getBalance function into.
// file2.js
const { defineGetBalance } = require('./file1');
// ...
defineGetBlance(obj1);
defineGetBlance(obj2);
// now both obj1 and obj2 have a getBalance() function
const balance1 = obj1.getBalance(id1)
const balance2 = obj2.getBalance(id2)
// you a for loop if you can
for (const obj of arrObj) {
defineGetBalance(obj);
}
// ...
I need to make reset button which makes Resetting Scores. Can anyone help me?
I tried all my best but I don't know how to make it.
https://github.com/SandroGamrekelashvili/New-folder
const game = () => {
let pScore = 0;
let cScore = 0;
});
const startGame = () => {
const playBtn = document.querySelector(".intro button");
const introScreen = document.querySelector(".intro");
const match = document.querySelector(".match");
There were a few things you needed to get done to make the reset work.
1.) Assign reset button element to a const.
2.) Move your score elements to parent scope.
const game = () => {
let pScore = 0;
let cScore = 0;
const resetBtn = gameContainer.querySelector("button.startOver");
const playerScore = document.querySelector(".player-score p");
const computerScore = document.querySelector(".computer-score p");
// The rest of your code...
2.) Attach event listener to reset button.
const startGame = () => {
playBtn.addEventListener("click", () => {
introScreen.classList.add("fadeOut");
match.classList.add("fadeIn");
});
resetBtn.addEventListener("click", () => {
playerScore.innerText = '0';
computerScore.innerText = '0';
pScore = cScore = 0;
});
};
Here is a JSFiddle with a working example.
I think what you need to solve your problem is very well explained in this other questions here.
The idea is that instead of declaring your variable inside your main function, you would create a variable that refer to your functions related to your score outside of it that can then be called when you need it. To avoid global conflict, you would have that function return an object with functions inside for getters and setters. In your case, I would do something like this:
const scoreModule = () => {
let pScore = 0;
let cScore = 0;
return {
getPScore: () => pScore,
getCScore: () => cScore,
setPScore: value => pScore = value,
setCScore: value => cScore = value,
}
}
Because you defined the scoreModule as a global object, you can then use it wherever you want. And, because you returned only getters and setters (not the actual value, but rather a mean to get or change them) your object is protected and you avoid the bad practice of globally available variables. Then in any of your other functions, when you want to use them either to get or set them, you simply:
const game = () => {
// To get a score
const currentPScore = scoreModule().getPScore()
// To set a score
scoreModule().setPScore(newScore)
});
I've come across a weird issue where a new variable is being created in local scope even if it is defined outside,
from the below code
after I call buildMeta() and check the contents of "data", it is always empty
implying that it's not being modified at all, even if I've specifically targeted "that.data" where that refers to the class' object.
I'd appreciate if anyone would point out what I am doing wrong.
class meta {
constructor(files) {
if(!files) throw Error("files not specified");
this.data = {};
this.ls = files;
}
buildMeta() {
var that = this;
for(let i = 0; i < that.ls.length; i++) {
mm.parseFile(that.ls[i]).then(x => {
var info = x.common;
that.data[info.artist] = "test";
}).catch((x) => {
console.log(x);
});
}
}
}
const mm = new meta(indexer); // indexer is an array of file paths
mm.buildMeta();
console.log(mm.data);
You're mixing sync with async code here.
The for loop won't wait for the parseFile promises to resolve.
You could use Promise.all to fill in the data when the files are parsed.
// Class names should be written using a capital letter
class Meta {
...
buildMeta() {
// You don't need this assignment since you're using arrow functions
// var that = this;
const promises = this.ls.map(filePath => mm.parseFile(filePath));
return Promise.all(promises).then(resolvedPromises => {
resolvedPromises.map(({ parsedFile }) => {
this.data[parsedFile.common.artist] = "test";
});
return this.data;
}).catch(console.error);
}
...
const mm = new meta(indexer); // indexer is an array of file paths
mm.buildMeta().then(data => {console.log(data)});
Hope this helps.
You are logging mm.data before parseFile has finished. Your code implies that it returns a promise, so your insertion into that.data will happen after your console.log(mm.data) executes.
You need to return a promise from buildMeta, so that you can do...
const mm = new meta(indexer);
mm.buildMeta().then(() => {
console.log(mm.data);
})
Here's a buildMeta that should do what you need. This returns a promise that waits for all of the parseFile invocations to do their work and update this.data...
buildMeta() {
return Promise.all(this.ls.map(f => mm.parseFile(f).then(x => {
var info = x.common;
this.data[info.artist] = "test";
})))
}
I am having an issue with some code which I would expect to work. I have a variable defined outside a function and as such would expect that to be available to the function through inheritance. I console log the variable outside the function and get a value and console log inside the function and get undefined. I have used comments in the code to show these console logs. Any help here would be great. Please see code snippet below. Thanks Ant
for (var i = 0; i < parseResult.length; i++) {
var destination = parseResult[i].attributes.userInfo;
for (var i = 0; i < firebaseResult.length; i++) {
if (firebaseResult[i].$id == parseResult[i].attributes.facebookID) {
parseResult[i].attributes.convoID = firebaseResult[i].convoID;
console.log(firebaseResult[i].time); // this returns the timestamp value
parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
.then(function(lastMessage) {
console.log(firebaseResult[i].time); // this returns undefined
if (!(0 in lastMessage)) {
var returnValue = 'Matched on ' + firebaseResult[i].time;
} else if (0 in lastMessage) {
var returnValue = lastMessage[0].message;
}
return returnValue;
})
.catch(function(error) {
console.log("Error:", error);
})
}
}
}
It is often not reliable to use loop iterator to access things in async callback, because when the callback come back, i would have been increased to the value that let it exit the loop.
The fix is assigning it to a variable for anything you want to hold.
console.log(firebaseResult[i].time); // this returns the timestamp value
var time = firebaseResult[i].time;
parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
.then(function(lastMessage) {
console.log(firebaseResult[i].time); // this returns undefined
console.log(time);
It's because you're in the callback of your FirebaseAPI.getLastMessage() call. If you log this (your scope) in it, you'll get something like a FirebaseAPI object or something.
What you can do is the classic var self = this; trick to keep your context stored in a variable accessible through scopes.
This would look like:
for (var i = 0; i < parseResult.length; i++) {
var destination = parseResult[i].attributes.userInfo;
for (var i = 0; i < firebaseResult.length; i++) {
if (firebaseResult[i].$id == parseResult[i].attributes.facebookID) {
parseResult[i].attributes.convoID = firebaseResult[i].convoID;
console.log(firebaseResult[i].time); // this returns the timestamp value
// Store your context here
var self = this;
this.results = firebaseResult[i].time;
parseResult[i].attributes.lastMessage = FirebaseAPI.getLastMessage(firebaseResult[i]).$loaded()
.then(function(lastMessage) {
console.log(firebaseResult[i].time); // this returns undefined
// Get your values
console.log(this.results); // this returns your values.
if (!(0 in lastMessage)) {
var returnValue = 'Matched on ' + firebaseResult[i].time;
} else if (0 in lastMessage) {
var returnValue = lastMessage[0].message;
}
return returnValue;
})
.catch(function(error) {
console.log("Error:", error);
})
}
}
}
I see in your tags that you're using Angularjs, so you could use the $scope object to store your scope variable and easily deal with promises and such.
Hope this helps :)