I have a modular JS project that I was given to tweak. It uses prototype inheritance as opposed to classes. Here is the code in question:
Constructor in hgManager.js:
export function HGManager() {
this.USER_ID = getURLParameter( "usr" ),
this.GAME_ID = getURLParameter( "game" )
};
getData() in hgManager.js:
getData: function(a, b) {
var c = this.API + "records/" + this.USER_ID + "/" + this.GAME_ID;
this.xhrLoad( "GET", c, a, b )
},
xhrLoad() in hgManager.js:
xhrLoad: function(a, b, c, d, e) {
var f = new XMLHttpRequest;
f.open(a, b, true),
e && f.setRequestHeader("Content-type", "application/json");
var g = this;
f.onload = function() {
if (4 == f.readyState && f.status >= 400 && f.status <= 599) { return d.call(g, f);
}
else {
var a = JSON.parse( f.responseText ).response;
return c.call(g, a, f)
}
},
f.onerror = function() {
return d.call(g, f)
},
e ? f.send( JSON.stringify( e ) ) : f.send()
}
A function that calls hgManager.getData():
loadPlayerData: function() {
var a = this;
this.game.hgManager.getData(
function( c ) { //param 1
if ( null === c ) {
return void console.log( "DataManager: Invalid response." ); //if there is no playerData
}
var d = JSON.parse( c.record );
void 0 === d || null === d || void 0 === d.selectedCharacter ? (console.log("DataManager: No data on backend, looking for data on local storage."), d = a._getLocalStorageData(), null !== d ? (console.log("DataManager: Data on localstorage found. Saving this to backend."), a.game.playerData = d) : console.log("DataManager: No data on localstorage. Saving default data to backend."), a.savePlayerData()) : console.log("DataManager: Data loaded from backend.");
var e = new Date,
f = e.getFullYear() + "-" + e.getMonth();
d.lastMonthPlayed != f && (d.lastMonthPlayed = f, d.loyaltyPoints = [], console.log("DataManager: New month, reset loyalty points.")),
a.game.playerData = d,
a.game.hasShownLoyaltyMessage = a.game.playerData.loyaltyPoints.length > 0,
a.game.hasShownPortalMessage = 9 == a.game.playerData.portalPieces.length
},
function() { //param 2
console.log("DataManager: Error loading user data"),
data = a._getLocalStorageData(),
null !== data ? (console.log("DataManager: Data on localstorage found."), a.game.playerData = data) : console.log("DataManager: No data on localstorage.")
}
)
},
The code that is throwing me off is return c.call(g, a, f) in xhrLoad(), and the corresponding first parameter function of loadPlayerData().
Where does the parameter 'c' in this.game.hgManager.getData(function( c ) { come from? It is clearly not defined in this scope, so I imagine it is a result of the call()?
How does loadPlayerData() read what appears to be undefined in the scope?
Given the function this.game.hgManager.getData( function(c), why would we reassign the parent object and call getData()? What is the intent?
It's quite difficult to deal with variables like a, b, c especially when they mean different things in different scopes.
But let's try to follow code and rename args to add some sence:
xhrLoad: function(method, target, callbackSuccess, callbackError, e/* idk what is it*/) {}
getData: function(callbackSuccess, callbackError) {
var target = this.API + "records/" + this.USER_ID + "/" + this.GAME_ID;
this.xhrLoad( "GET", target, callbackSuccess, callbackError )
},
this.game.hgManager.getData(
function( response ) { //param 1 callbackSucess
if ( null === response ) {
return void console.log( "DataManager: Invalid response." ); //if there is no playerData
}
},
function() { //param 2 callbackError
//
}
Now it's easier to understand.
getData() accepts as arguments two callback functions - one for successful response and one for error. First one must accept response as argument. It's your c from this.game.hgManager.getData(function( c ) { and it's defined right here. Since it's a function's argument, there is no need to define it in global scope.
And seems, that there is nothing to do with classes here. It's all about passing functions as arguments.
Related
I need to implement a Map API in an Angular 13 project but I have just a Javascript file. Apparently it's based on TypeScript then easily importable but I don't know how...
The file starts by this code :
!function() {
function e(t, n, i) {
function r(a, s) {
if (!n[a]) {
if (!t[a]) {
var l = "function" == typeof require && require;
if (!s && l)
return l(a, !0);
if (o)
return o(a, !0);
var u = new Error("Cannot find module '" + a + "'");
throw u.code = "MODULE_NOT_FOUND",
u
}
var h = n[a] = {
exports: {}
};
t[a][0].call(h.exports, function(e) {
var n = t[a][1][e];
return r(n || e)
}, h, h.exports, e, t, n, i)
}
return n[a].exports
}
for (var o = "function" == typeof require && require, a = 0; a < i.length; a++)
r(i[a]);
return r
}
return e
}()({
In the documentation, I need just do this :
var map = new f4.map.Map('#map');
But f4 is undefined...
How i can do this ?
Thanks
I try to use Javascript Ext libraries and call a rest service which returns a json response. Its an array with one element and it doesnt work when i use DataReader(Ext.data.DataReader) "readRecords" operation to process the json data and it returns null reference on the call back function.
It would be good if you provide some help or suggestions to fix the issue.
I tried to pass the array as the argument in the reader and it doesnt help.
Ext.namespace("cityDetails");
var a = "http://" + "Url address to get cityDetails";
var e = new Ext.data.ScriptTagProxy({
callbackParam: "callback",
url: a
});
var f = {
fields: [{
name: "Id"
}, {
name: "city"
}, {
name: "postcode"
}]
};
var d = new cityDetails.reader(f, {});
//call back function
var h = function(k, j, m) {
try {
// k returns null reference
if (k != null) {
alert("PostCode insider else::" + k.jsonObject.postcode)
}
}
} catch (l) {
alert(l)
}
};
var i = this;
e.load({
1: 1
}, d, h, i)
//readRecords from Data - Json Response an array of one objekt
Ext.extend(cityDetails.reader, Ext.data.DataReader, {
readRecords: function(b) {
this.jsonData = b;
alert("Meta ::" + this.meta);
//alert("Meta Root::"+this.meta.root);
var a = false;
return {
success: a,
jsonObject: this.getRoot(b),
totalRecords: 1
}
},
getRoot: function(d) {
if (this.meta && this.meta.root) {
var c = d;
var a = this.meta.root.split(".");
for (var b = 0; b < a.length; b++) {
c = c[a[b]]
}
return c
} else {
alert("Value Of d in reader::" + d);
if (d != null) {
alert("Objects Of d in reader::" + d.jsonObject.postcode);
}
return d
}
}
})
//Initial call.
cityDetails.reader = function(a, b) {
a = a || {};
cityDetails.reader.superclass.constructor.call(this, a, b || a.fields)
};
Sample response from rest service shown below.
[{
"Id": "121A",
"postcode": "1000",
"city": "ABBA"
}]
The call back function k should return the value of Json response. However it returns null.
My requirement is on phase change I have to construct method names and call the methods and subsequent methods also,here I am able to construct method name but it is as String and not able to call the method. I have followed some of the suggestion given but I couldn't achieve. Please help.
var previousPhase = $("#currentPhase").val();
var projectPhaseArray = ["requirement", "design", "construction", "testing", "release"];
var i = 0;
$("#currentPhase").change(function() {
alert(previousPhase);
i=projectPhaseArray.indexOf(previousPhase);
for (i; i < projectPhaseArray.length; i++) {
alert(projectPhaseArray[i]);
var phaseTimeLineToCall =
projectPhaseArray[i].concat("PhasePhaseTimeLines");
executeFunctionByName(phaseTimeLineToCall,window);
}
});
function executeFunctionByName(functionName, context /*, args */) {
return context[functionName].apply(context);
}
function requirementPhaseTimeLines(){
alert("In RequirementPhaseTimelines");
}
function designPhaseTimeLines(){
alert("In DesignPhaseTimelines");
}
Thanks.
Strings don't mutate, so you need to save the value back
projectPhaseArray[i] = projectPhaseArray[i].concat("PhasePhaseTimeLines");
You could use a javascript object where you store the function name as key and the function reference as value
var lookup = {
requirementPhaseTimeLines: requirementPhaseTimeLines,
designPhaseTimeLines: designPhaseTimeLines
}
Without Arguments
We have to modify executeFunctionByName slightly
function executeFunctionByName(functionName, lookup) {
return lookup[functionName]()
}
Working Example
function requirementPhaseTimeLines() {
return "In RequirementPhaseTimelines"
}
function designPhaseTimeLines() {
return "In DesignPhaseTimelines"
}
var lookup = {
requirementPhaseTimeLines: requirementPhaseTimeLines,
designPhaseTimeLines: designPhaseTimeLines
}
function executeFunctionByName(functionName, lookup) {
return lookup[functionName]()
}
console.log(
executeFunctionByName("requirementPhaseTimeLines", functions)
)
console.log(
executeFunctionByName("designPhaseTimeLines", functions)
)
With Arguments
If we want to pass in arguments we have to curry the functions we want to let execute.
function greet(word) {
return function(name) {
return word + ', ' + name + '.'
}
}
Second, we have to create a function where we can iterate trough the arguments and set each arguement value to the function we want to execute:
function setArguments(functionRef, args) {
return args.length === 1
? functionRef
: setArguments(functionRef(args[0]), args.slice(1))
}
Working Example
function greet(word) {
return function(name) {
return word + ', ' + name + '.'
}
}
var lookup = {
greet: greet
}
function getFunction(lookup, name) {
return lookup[name] || new Function()
}
function setArguments(functionRef, args) {
return args.length === 1
? functionRef
: setArguments(functionRef(args[0]), args.slice(1))
}
function executeFunctionByName(functionName, lookup, args) {
var functionRef = getFunction(lookup, functionName)
var functionRefWithArgs = setArguments(functionRef, args)
return functionRefWithArgs(args[args.length - 1])
}
console.log(
executeFunctionByName('greet', lookup, ['Hi', 'Jon'])
)
Here's my compose function, as a polyfill
Function.prototype.compose = function(prevFunc) {
var nextFunc = this;
return function() {
return nextFunc.call(this, prevFunc.apply(this,arguments));
}
}
These work:
function function1(a){return a + ' do function1 ';}
function function2(b){return b + ' do function2 ';}
function function3(c){return c + ' do function3 ';}
var myFunction = alert(function1).compose(function2).compose(function3);
myFunction('do');
var roundedSqrt = Math.round.compose(Math.sqrt)
roundedSqrt(6);
var squaredDate = alert.compose(roundedSqrt).compose(Date.parse)
quaredDate("January 1, 2014");
But this does not work!
var d = new Date();
var alertMonth = alert.compose(getMonth); <--
alertMonth(d); ^^^^
Error throws error "Uncaught ReferenceError: getMonth is not defined" in google chrome.
Now, if I try either of these instead:
var d = new Date();
function pluckMonth(dateObject) {return dateObject.getMonth();}
var alertMonth = alert.compose(pluckMonth);
var alertMonth2 = alert.compose(function(d){return d.getMonth()});
alertMonth(d);
alertMonth2(d);
They work.
Ok, so, why is that? I don't want to write extra functions, I want it to just work. The compose function uses the apply utility and just uses this for the thisArg, so it should work for object members as well as stand-alone functions, right??
i.e., these are equivalent
this.method()
method.call.apply(this)
jsFiddle: http://jsfiddle.net/kohq7zub/3/
If it comes to prototyping the Function object a whole bunch of libraries really do not acknowledge functions as methods.
The last entry in such arguments signatures always should be a target object, a method then can act upon. It solves the binding within the implementation; Thus avoiding to be forced later on having to use bind again and again as sole solution.
The given example slightly needs to be changed (jsFiddle) to ...
Function.prototype.compose = function(prevFunc, target) {
var nextFunc = this;
return function() {
return nextFunc.call(this, prevFunc.apply(target, arguments));
};
};
const d = (new Date);
let alertMonth;
// both variants do work.
alertMonth = alert.compose(d.getMonth, d);
alertMonth();
alertMonth = alert.compose(Date.prototype.getMonth, d);
alertMonth();
Within a next code iteration the above provided code then might result in something similar to the following implementation ...
const protoGetMonth = Date.prototype.getMonth;
let logMonth;
logMonth = ((month) => console.log(month))
.compose(protoGetMonth, (new Date));
logMonth();
logMonth = console.log
.compose(protoGetMonth, (new Date))
.bind(console);
logMonth();
let alertMonth = alert.compose(protoGetMonth, (new Date));
alertMonth();
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function (function_prototype) {
var
isFunction = (function (TYPEOF_FUNCTION_TYPE) {
return function (type) {
return (
(typeof type == TYPEOF_FUNCTION_TYPE)
&& (typeof type.call == TYPEOF_FUNCTION_TYPE)
&& (typeof type.apply == TYPEOF_FUNCTION_TYPE)
);
}
}(typeof function_prototype)),
getSanitizedTarget = function (target) {
return (target == null) ? null : target;
}
;
function_prototype.compose = function (previous, target) { // compose
var
previousTarget = getSanitizedTarget(target),
proceed = this
;
return (isFunction(previous) && isFunction(proceed) && function () {
return proceed.call(this, previous.apply(previousTarget, arguments));
}) || proceed;
};
}(Function.prototype));
</script>
I have a string like this "namespace.fun1.fun2.fun3" passed from the client. It's telling the server which function to use.
How do I safely run the function?
right now i'm doing:
var runthis = string.split('.')
namespace[runthis[1]][runthis[2]][runthis[3]]
How do I handle arbitrary depth of nesting safely?
A little function I wrote. I use it in most of my applications:
Object.lookup = (function _lookup() {
var cache = { };
return function _lookupClosure( lookup, failGracefully ) {
var check = null,
chain = [ ],
lastkey = '';
if( typeof lookup === 'string' ) {
if( cache[ lookup ] ) {
chain = cache[ lookup ].chain;
check = cache[ lookup ].check;
}
else {
lookup.split( /\./ ).forEach(function _forEach( key, index, arr ) {
if( check ) {
if( typeof check === 'object' ) {
if( key in check ) {
chain.push( check = check[ key ] );
lastkey = key;
}
else {
if( !failGracefully ) {
throw new TypeError( 'cannot resolve "' + key + '" in ' + lastkey );
}
}
}
else {
if( !failGracefully ) {
throw new TypeError( '"' + check + '" ' + ' does not seem to be an object' );
}
}
}
else {
lastkey = key;
chain.push( check = window[ key ] );
}
});
if( check ) {
cache[ lookup ] = {
chain: chain,
check: check
};
}
}
}
return {
execute: function _execute() {
return typeof check === 'function' ? check.apply( chain[chain.length - 2], arguments ) : null;
},
get: function _get() {
return check;
}
};
}
}());
usage:
Object.lookup( 'namespace.fun1.fun2.fun3' ).execute();
The first parameter is the object/method/property to resolve. The second (optional) parameter indicates whether or not the lookup() method shall fail silently or throw an exception if some property or object could not get resolved. default is 'throw'. To avoid that call
Object.lookup( 'namespace.fun1.fun2.fun3', true ).execute( 'with', 'paras' );
If .fun3 is a function, you can pass in any parameters into .execute() instead.
if you just want to receive the property value, you can also call .get() instead of .execute()
var val = Object.lookup( 'namespace.fun1.fun2.fun3' ).get();
(I may be misinterpreting the question, but this is what came to mind)
var s = "space.f.g.h.i.j.k.l.m",
a = s.split( "." ),
fn = eval( a[0] );
for ( var i = 1; i < a.length; i++ ) {
fn = fn[ a[i] ];
}
fn();
Note: this won't guard against the namespace being specified incorrectly or maliciously.
This should do:
var get = function(obj, key) {
var s = key.split('.')
, i = 1
, l = s.length;
for (; i < l; i++) {
obj = obj[s[i]];
if (!obj) return;
}
return obj;
};
get({hello:{world:{}}}, 'ns.hello.world');
edit: changed code a bit
Here's a simple for loop that should do find each object specified strating from the global scope, and then run the function it finds.
window.namespace = { fun1: { fun2: { fun3: function() { alert('hi!') }}}};
runFunc = function(address) {
var addressArray = address.split('.'),
current = window,
i = 0;
for (i = 0; i < addressArray.length; i++) {
current = current[addressArray[i]];
}
current();
};
runFunc('namespace.fun1.fun2.fun3');
http://jsfiddle.net/jfWra/1/
And here's some eror protection that will throw something meaningful if the value referenced doesnt exist or is not a function: http://jsfiddle.net/jfWra/2/
Here's another simple solution using a recursive function:
function run(str, context) {
var path = str.split(".")
if path.length == 1 {
context[path[0]].call()
return;
}
if(typeof context == 'undefined') {
context = window[path[0]]
} else {
context = context[path[0]]
}
run(path.splice(1).join('.'), context)
}
Two years later there is this module: https://github.com/daaku/nodejs-dotaccess.
var get = require("dotaccess").get;
var o = {a:{b:{c:'deep'}}};
console.log(get(o, 'a.b.c'));