How to overcome "potentially invalid usage of this"? - javascript

I get potentially invalid usage of this when calling isDataMatchingnamespace how to overcome and how to call isDataMatchingnamespace in a proper way?
function Client() {
var namespace = "default";
this.addnamespaceTodata = function(data) {
data.namespace = namespace;
return data;
};
this.isdataMatchingnamespace = function(data) {
return data.namespace === namespace;
};
this.filterdatasBynamespace = function(datas) {
var result = [];
_.forEach(datas, function(data) {
if (this.isdataMatchingnamespace(data)) { // I get potentially invalid usage of this so how to overcome and how to call isDataMatchingnamespace in a proper way?
result.push(data);
}
});
}
}
module.exports = Client;

That is an invalid usage of this, since this is undefined inside that function.
underscore.js allows you to pass an optional additional argument to forEach to specify what this should be inside the function. If you want it to be the same as this from outside the function, then pass this as the third argument into _.forEach:
_.forEach(datas, function(data) {
if (this.isdataMatchingnamespace(data)) {
result.push(data);
}
}, this); // Added ", this"

there is other way also by storing this value into variable.
let's say var _thisRef = this; define this below var namespace = "default"; and use _thisRef.isdataMatchingnamespace(data) without changing your code
your updated code as follow :
function Client() {
var namespace = "default";
var _thisRef = this;
this.addnamespaceTodata = function(data) {
data.namespace = namespace;
return data;
};
this.isdataMatchingnamespace = function(data) {
return data.namespace === namespace;
};
this.filterdatasBynamespace = function(datas) {
var result = [];
_.forEach(datas, function(data) {
if (_thisRef.isdataMatchingnamespace(data)) { // I get potentially invalid usage of this so how to overcome and how to call isDataMatchingnamespace in a proper way?
result.push(data);
}
});
}
}
module.exports = Client;

Another better way would be to declare a variable at a class level like:
export class SomeComponent {someVarible = []; }
someFunction() { const tempVar = []; _.forEach(datas, function(data) {
tempVar.push(data) // now use this variable to pass or
assign the data
}, this);
this.someVarible = tempVar;
// OR
otherFunction(tempVar); // where you can use your varuiable
}

es6 arrow functions does that for you automatically !
Just change the syntax to arrow function syntax
data => {
your function goes here
}

Related

How to overwrite default value from properties of this object with call or apply method in JS

I've started learning call, apply and bind and now i want to use it but have some problems:
I have a function:
let addTableRow = function() {
let template = HtmlTemplatesTelephony.ks.renderTableRow;
this.id = "";
this.creationDate = "";
this.address = "";
this.numberOfUser = "";
this.accessExisting = true;
$(DOM.tableBodyContainer).append(template(this));
};
I declared the this properties as default values
Here i invoke the function either with .call() or without:
let updateTable = function() {
let locations = userData().locations;
if(locations.length > 0) {
for(let location of locations) {
UIController.addLocation.call(location);
}
} else {
UIController.addLocation();
}
};
My idea was to use the 'this' values in the 'addTableRow' as default values in case of invoking the function without .call(). And when calling it with .call() I want to overwrite the 'this' default values. But exactly the opposite happens.
I know I could pass the object as parameter and set default object, but is there an other way to do it with .call()? Or is it the wrong use case for .call()?
Thanks for help!
***** UPDATE ******
Sorry, it is written in module pattern and i forgot to mention that the function 'addTableRow' is called 'addLoaction' in the return object. Here's some more code:
My UI controller:
let UIController = (function() {
let DOM = {
tableHeadContainer: $('thead'),
tableBodyContainer: $('tbody'),
inputNumberLocations: $('#numberLocations'),
inputAddress: $('.js-location-address'),
inputUser: $('.js-location-user'),
inputVpn: $('.js-location-vpn'),
btnDeleteLocation: $('.js-delete-location'),
};
let addTableRow = function() {
let template = HtmlTemplatesTelephony.ks.renderTableRow;
this.id = "";
this.creationDate = "";
this.address = "";
this.numberOfUser = "";
this.accessExisting = true;
$(DOM.tableBodyContainer).append(template(this));
};
return {
getDOM: DOM,
addLocation: addTableRow,
removeLocation: removeTableRow
}
})();
And my main controller:
let Controller = (function(dataController, UIController) {
let updateTable = function() {
let locations = userData().locations; // locations has the same properties as in function 'addTableTow'
if(locations.length > 0) {
for(let location of locations) {
UIController.addLocation.call(location);
}
} else {
UIController.addLocation();
}
};
return {
init: function() {
setupEventListeners();
updateTable();
},
}
})(dataController, UIController);
I hope it's more clear now.
When you .call(obj), you are starting your function with this being equal to that object. When you say this.id = "", you are overriding the id that you already had.
This may be a solution to your problem:
if (!this.id) {
this.id = "";
}
if (!this.property) {
this.property = "default";
}
// etc.

private object not setting data

Hi I'm trying to implement a LinkedList in Javascript. When i assign a value to my node it doesn't seem to store it when I use my getter. For example:
var Node =function() {
var _data;
var _next ={};
var that = this;
that.getData = function() {
return _data;
};
that.setData = function(data) {
that._data = data;
};
that.getNext = function() {
return _next;
};
that.setNext = function(next) {
that._next = next;
};
return that;
};
Will not work with:
var nodeObj = new Node();
nodeObj.setData("hello");
console.log(nodeObj.getData());
_data is not the same as that._data, you must do this:
that.getData = function() {
return that._data;
};
OR you could do this instead:
that.setData = function(data) {
_data = data;
};
the benefit of the second approach being that you're simulating a private variable (because you cannot do nodeObj._data in the second case but you can in the first)
also var that = this; is unnecessary, you can simply do this._data in this case.
For your case here, you can assume that if you're calling a function like yourObject.someFunction(), then within someFunction the value of this equals yourObject. (And this isn't always true in javascript but since you're starting off you should think about it this way for now. If you pass a function to another function as a variable and then call it then this wouldn't be the case).

javascript scope and global variables

I'm trying to avoid using global variable when using functions within objects.
I want to invoke a function inside other function and use a variable from the first function's scope.
For example:
var showForecast = {
'init': function () {
this.getData();
},
'buildView': function(){
var code = "Hey, you're from " + this.data.city;
$('body').append(code);
},
'getData': function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
showForecast.buildView();
})
}
}
Clearly it's not working. I want to use data inside buildView without making data a global variable.
I thought using this would be the right course of action because I'm calling buildView from a function where data is defined.
How can this be achieved? Thanks.
You can pass the information along:
var showForecast = {
'init': function () {
this.getData();
},
'buildView': function(data){
var code = 'Hey, you\'re from ' + data.city;
$('body').append(code);
},
'getData': function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
showForecast.buildView(data);
})
}
}
There is no way to access the data variable itself. That is locally scoped to the anonymous function you pass to getJSON (and getJSON passes it as an argument, which is beyond your control).
You have to copy the value somewhere.
In your particular example, there are no scopes shared between getData and buildView other than the global scope. So if you want to pass the value through scopes, then a global is your own (terrible) option.
You can simply pass it as an argument:
showForecast.buildView(data);
Or you can store it as a property:
showForecast.myData = data;
I like Vinny's answer.
One round-bout way is to make a module out of it:
var showForecast = function(){
var data;
var init = function () {
this.getData();
};
var buildView = function(){
var code = 'Hey, you\'re from ' + this.data.city;
$('body').append(code);
};
var getData = function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
this.data = data;
showForecast.buildView();
})
};
return {
'init': init,
'buildView': buildView,
'getData': getData
};
}();
This way the scope of var data is limited to the function. It's like a private variable.
As you are trying to avoid global, you should consider using namespaces. There is no such thing called namespace in Javascript. But you can define yourself using small utility method mentioned here.
http://www.zachleat.com/web/namespacing-outside-of-the-yahoo-namespace/
A utility method which helps creating custom namespaces.
jQuery.namespace = function() {
var a=arguments, o=null, i, j, d;
for (i=0; i<a.length; i=i+1) {
d=a[i].split(".");
o=window;
for (j=0; j<d.length; j=j+1) {
o[d[j]]=o[d[j]] || {};
o=o[d[j]];
}
}
return o;
};
Define name space
jQuery.namespace( 'jQuery.showForecast' );
Define methods using revealing module pattern
https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript
jQuery.showForecast = (function() {
var data;
var init = function() {
getData();
}
var buildView = function() {
var code = "Hey, you're from " + data.city;
$('body').append(code);
}
var getData = function() {
$.getJSON('http://ipinfo.io/', function(_data) {
console.log(data);
data = _data;
buildView();
})
}
return {
init: init
};
})(); // Execute it immediately
Usage:
You can access only init method as it is exposed to outside.
jQuery.showForecast.init()
Define another namespace
jQuery.namespace( 'jQuery.showForecast.extended' );
jQuery.showForecast.extended = {
// Define some more
};

Using object from different module

I've the following code which is parse object and and provide getters to it like following and this is working,The problem is that I want to access to this object from different module and avoid the parsing again ,how can I do that without define global var?
var ymlParser = require('yamljs');
function ymlParse(src) {
if (!(this instanceof ymlParse)) return new ymlParse(src);
this.data = ymlParser.parse(src);
}
ymlParse.prototype = {
getA: function () {
return this.data.default_A;
},
getB: function () {
return this.data._B;
}
};
module.exports = ymlParse;
Lets say I want to access to A from module A and B from module B,how can I do that without sending the src again when I call to getB since when I call to getA I already pass the src...
You can use Memoization pattern - http://addyosmani.com/blog/faster-javascript-memoization/. The one issue with implementation in others answers is not hashing the arguments. So you should have something like this:
var ymlParser = require('yamljs');
function ymlParse(src) {
var hash = JSON.stringify(src);
if (!(this instanceof ymlParse)) return new ymlParse(src);
if (this.cache[hash]) {
this.data = this.cache[hash];
} else {
this.data = ymlParser.parse(src);
this.cache[hash] = this.data;
}
}
ymlParse.prototype = {
cache: {},
getA: function () {
return this.data.default_A;
},
getB: function () {
return this.data._B;
}
};
module.exports = ymlParse;
Take a closer look to JSON.stringify method. You must implement hashing algorithm here that will be associate hash with src data as unique identifier. Usually is JSON.stringify but you can use your own.
Or another solution in functional style:
var _ = require('lodash');
var ymlParser = require('yamljs');
function ymlParse(src) {
var result = ymlParser.parse(src);
return {
getA: function() {
return result.default_A;
},
getB: function() {
return result._B;
}
};
}
module.exports = _.memoize(ymlParse);
Usage the same, you just call exported function as usual function.
You can make caching in your ymlParse class (it's designed like class, isn't it?).
Just store src objects and results of parsing. If ymlParse will be executed with cached src just return stored result without parsing.
Try to change your code like this:
var ymlParser = require('yamljs');
function ymlParse(src) {
if (!(this instanceof ymlParse)) return new ymlParse(src);
if (this.cache[src]) {
this.data = this.cache[src];
} else {
this.data = ymlParser.parse(src);
this.cache[src] = this.data;
}
}
ymlParse.prototype = {
cache: {},
getA: function () {
return this.data.default_A;
},
getB: function () {
return this.data._B;
}
};
module.exports = ymlParse;
Notice that I'm not using deep copy of this.data object. If it's not read-only it may cause some problems.

Rewiring a JavaScript function

Let's say I have a function named fna() that does a simple thing such as:
var fna = function(ar) {
console.log("argument: ", ar);
return "return value is argument too: " + ar;
}
fna() is coded by some other developer and I can't access to it. He didn't bother casting any events and when it is called, I have to be aware of it. Hopefully, his method is accessible by window.fna().
I want some additional code to be executed. Let's say, add this console.log
var fna = function(ar) {
console.log("Hola, I am some additional stuff being rewired");
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
And I want this to be executed even when called from fnb() by some other part of the code.
var fnb = function() {
return fna("Bonjour, I am fnb and I call fna");
}
Here is a way I found, using the utils.rewire() method. utils is just some utility belt, and it could be added to your favorite framework as a plugin. Unfortunately, it only works on Firefox.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(functionFullName, callback) {
var rewired = window[functionFullName];
console.log("%s() is being rewired", functionFullName)
window[functionFullName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
Use it like this.
utils.rewire("fna",function(){
console.log("Hola, I am some additional stuffs being rewired");
});
This seems to work such as shown in this jsbin, but (and here is my question:) How do I rewire obja.fna()?
var obja = {
fna = function(ar) {
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
};
I cannot make it work to rewire the some.object.method() method.
Extra bonus question: Is there a more cleaner way to do this? Out-of-the-box clean concise and magic library?
Refactor rewire into a rewireMethod function which acts on any given object:
var utils = utils || {};
utils.rewireMethod = function (obj, functionName, prefunc) {
var original = obj[functionName];
obj[functionName] = function () {
prefunc();
return original.apply(this, arguments);
};
};
Note that rewire can now be written as:
utils.rewire = function (functionName, prefunc) {
utils.rewireMethod(window, functionName, prefunc);
};
Then you just call it as:
utils.rewireMethod(obja, "fna", function () {
console.log("Hola, I am some additional stuff being rewired");
});
Note that nothing special is required if you have a method like window.ideeli.Search.init(). In that case, the object is window.ideeli.Search, and the method name is init:
utils.rewireMethod(window.ideeli.Search, "init", function () {
console.log("Oh yeah, nested objects.");
});
Add a parameter to rewire that is the object containing the function. If it's a global function, pass in window.
var utils = utils || {};
// let's rewire a function. i.e. My.super.method()
utils.rewire = function(object, functionName, callback) {
var rewired = object[functionName];
console.log("%s() is being rewired", functionName)
object[functionName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
utils.rewire(some.object, "method", function(){} );
You can simply use a closure to create a generic hook function that allows you to specify another function to be called immediately before or after the original function:
function hookFunction(fn, preFn, postFn) {
function hook() {
var retVal;
if (preFn) {
preFn.apply(this, arguments);
}
retVal = fn.apply(this, arguments);
if (postFn) {
postFn.apply(this, arguments);
}
return retVal;
}
return hook;
}
So, for any function that you want to hook, you just call hookFunction and pass it the function you want to hook and then an optional pre and post function or yours. The pre and post function are passed the same arguments that the original function was.
So, if your original function was this:
var fna = function(ar) {
console.log("argument:",ar);
return "return value is argument too:"+ar;
}
And, you want something to happen every time that function is called right before it's called, you would do this:
fna = hookFunction(fna, function() {
console.log("Hola, I am some additional stuff being rewired right before");
});
or if you wanted it to happen right after the original was called, you could do it like this:
fna = hookFunction(fna, null, function() {
console.log("Hola, I am some additional stuff being rewired right after");
});
Working demo: http://jsfiddle.net/jfriend00/DMgn6/
This can be used with methods on objects and arbitrary nesting levels of objects and methods.
var myObj = function(msg) {
this.greeting = msg;
};
myObj.prototype = {
test: function(a) {
log("myObj.test: " + this.greeting);
}
}
var x = new myObj("hello");
x.test = hookFunction(x.test, mypreFunc2, myPostFunc2);
x.test("hello");
Based on Claudiu's answer, which seems to be the most appreciated way, here is a solution using a for loop and proxying the context... But still, I find this ugly.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(method, callback) {
var obj = window;
var original = function() {};
var tree = method.split(".");
var fun = tree.pop();
console.log(tree);
// Parse through the hierarchy
for (var i = 0; i < tree.length; i++) {
obj = obj[tree[i]];
}
if(typeof(obj[fun]) === "function") {
original = obj[fun];
}
var cb = callback.bind(obj);
obj[fun] = function(ar) {
cb();
return original.apply(this, arguments);
}
}
Well, this looks strange. Consider this
function wrap(fn, wrapper) {
return function() {
var a = arguments;
return wrapper(function() { return fn.apply(this, a) })
}
}
Example:
function foo(a, b) {
console.log([a, b])
return a + b
}
bar = wrap(foo, function(original) {
console.log("hi")
var ret = original()
console.log("there")
return ret
})
console.log(bar(11,22))
Result:
hi
[11, 22]
there
33
To wrap object methods, just bind them:
obj = {
x: 111,
foo: function(a, b) {
console.log([a, b, this.x])
}
}
bar = wrap(obj.foo.bind(obj), function(fn) {
console.log("hi")
return fn()
})

Categories

Resources