Friends
How, after defining a function, change a variable inside it without redefining the function.
For example, I have this function
Data = function () {
function n() {
var n = this;
this.same = function (n) {
this.search = function (n, t, i, r) { ... }
this.go = function (n, t) { ... }
this.get = function (n, t) {... }
this.run = function (n, t) { console.log("test") } //change this item
}
}
}()
And I want it to become the following function.
Data = function () {
function n() {
var n = this;
this.same= function (n) {
this.search = function (n, t, i, r) { ... }
this.go = function (n, t) { ... }
this.get = function (n, t) {... }
this.run = function (n, t) { alert("test ok") } // changed
}
}
}()
I don't think the other answer totally achieves what you want. If so, this might be a better alternative:
var Data = function () {
this.run = function (n, t) { console.log("test") } // change this later
var dataSelf = this;
this.n = function () {
var n = this;
this.same = function (n) { }
this.search = function (n, t, i, r) { }
this.go = function (n, t) { }
this.get = function (n, t) { }
this.run = dataSelf.run
};
}
const en = new Data()
new en.n().run() // Logs "test"
en.run = function(n, t) { alert("test ok"); }
new en.n().run() // Alerts "test ok"
Related
I have a function, which calls another function and so on like
var fn1 = function() { return 'bar'; };
var fn2 = function() {
return fn1;
};
var fn3 = function() {
return fn2;
};
now this can keep on going.
Therefore I would like to recursively call a method, till I get a string/value i.e. the innermost return statement.
I tried like this:
function p (val){
var res = val;
while(typeof(res)=="function"){res = p()}
return res;
}
and calling it like p(fn2); but it doesn't seem to work. What am I missing.
You need to reassign the res (or val) inside the loop, then return it (don't return the p) at the end:
var fn1 = function() {
return 'bar';
};
var fn2 = function() {
return fn1;
};
var fn3 = function() {
return fn2;
};
function p(val) {
while (typeof val === "function") {
val = val();
}
return val;
}
console.log(p(fn3));
You can try this:
var fn1 = function() {
return 'bar';
};
var fn2 = function() {
return fn1;
};
var fn3 = function() {
return fn2;
};
function p(val) {
if (typeof val === "function") {
return p(val())
}
return val;
}
console.log(p(fn3));
I want to generate a GUID string via the answer.
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
Now, I want to put it into toString function, like: GUID.NewGuid().toString().
I've tried (not working):
let GUID = function () {};
GUID.NewGuid = function () {};
GUID.NewGuid.prototype.toString = function () {
let guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
return guid;
};
Uncaught TypeError: Cannot read property 'toString' of
undefined console.log(GUID.NewGuid().toString());
What I want to achieve: using syntax GUID.NewGuid().toString() to generate an id.
How to fix it?
You need an instance of the class.
var guid = new GUID.NewGuid;
let GUID = function () {};
GUID.NewGuid = function () {};
GUID.NewGuid.prototype.toString = function () {
let guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
return guid;
};
var guid = new GUID.NewGuid;
console.log(guid.toString());
To make #Nina's code act the way I would expect, and you want the late evaluation during toString,. One idea is to alter the object's toString after evaluation, basically create a late binding function on the object, rather than the prototype.
I use this technique often to create late binding methods, some methods could be expensive and doing init's during construction could be time consuming. In this case I'm not sure there is a massive performance boost, but it's a good example of doing it.
let GUID = function () {};
GUID.NewGuid = function () {};
GUID.NewGuid.prototype.toString = function () {
let guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
let r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
this.toString = function() { return guid; }; //add this..
return guid;
};
var guid = new GUID.NewGuid;
console.log(guid.toString()); //these two
console.log(guid.toString()); //want to equal the same
guid = new GUID.NewGuid;
console.log(guid.toString()); //now I want a new one.
window[Symbol.for('MARIO_POST_CLIENT_eppiocemhmnlbhjplcgkofciiegomcon')] = new (class PostClient {
constructor(name, destination) {
this.name = name;
this.destination = destination;
this.serverListeners = {};
this.bgRequestsListeners = {};
this.bgEventsListeners = {};
window.addEventListener('message', (message) => {
const data = message.data;
const isNotForMe = !(data.destination && data.destination === this.name);
const hasNotEventProp = !data.event;
if (isNotForMe || hasNotEventProp) {
return;
}
if (data.event === 'MARIO_POST_SERVER__BG_RESPONSE') {
const response = data.args;
if (this.hasBgRequestListener(response.requestId)) {
try {
this.bgRequestsListeners[response.requestId](response.response);
}
catch (e) {
console.log(e);
}
delete this.bgRequestsListeners[response.requestId];
}
}
else if (data.event === 'MARIO_POST_SERVER__BG_EVENT') {
const response = data.args;
if (this.hasBgEventListener(response.event)) {
try {
this.bgEventsListeners[data.id](response.payload);
}
catch (e) {
console.log(e);
}
}
}
else if (this.hasServerListener(data.event)) {
try {
this.serverListeners[data.event](data.args);
}
catch (e) {
console.log(e);
}
}
else {
console.log(`event not handled: ${data.event}`);
}
});
}
emitToServer(event, args) {
const id = this.generateUIID();
const message = {
args,
destination: this.destination,
event,
id,
};
window.postMessage(message, location.origin);
return id;
}
emitToBg(bgEventName, args) {
const requestId = this.generateUIID();
const request = { bgEventName, requestId, args };
this.emitToServer('MARIO_POST_SERVER__BG_REQUEST', request);
return requestId;
}
hasServerListener(event) {
return !!this.serverListeners[event];
}
hasBgRequestListener(requestId) {
return !!this.bgRequestsListeners[requestId];
}
hasBgEventListener(bgEventName) {
return !!this.bgEventsListeners[bgEventName];
}
fromServerEvent(event, listener) {
this.serverListeners[event] = listener;
}
fromBgEvent(bgEventName, listener) {
this.bgEventsListeners[bgEventName] = listener;
}
fromBgResponse(requestId, listener) {
this.bgRequestsListeners[requestId] = listener;
}
generateUIID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
})('MARIO_POST_CLIENT_eppiocemhmnlbhjplcgkofciiegomcon', 'MARIO_POST_SERVER_eppiocemhmnlbhjplcgkofciiegomcon')
function modifyFunction(f) {
return function () {
var returnValue = f.apply(this, arguments);
console.log(returnValue);
if (returnValue == undefined) {
return this;
} else {
return returnValue;
}
};
}
function modifyMethod(o, m) {
if (o.hasOwnProperty(m)) {
if (o[m] instanceof Function) {
o[m] = modifyFunction(m);
}
}
}
var o = {
num: 0,
add: function (x) {
return this.num += x;
},
sub: function (x) {
return this.num -= x;
}
};
modifyMethod(o, "add");
o.add(2).add(4);
console.log(o.num); // o.num = 6
modifyMethod(o, "sub");
o.sub(1).add(3).sub(5);
console.log(o.num); // o.num = 3
How would I make it so that in modifyMethod function inside of the "if(o[m] instanceof Function)" would be equal to what returns by the modifyFunction function, when sending it o[m]? I am trying to make it so that it is chainable, yet I am having a very difficult time getting my head around this.
To make o.add(2).add(4); equal to o.add(2); o.add(4); We can observe that what o.add(2) returns should be o. So your modifyFunction should return a function that :
call the passed in function with given parameters.
Return the caller.
So instead of return that returnValue(which is o.num, a number), you should always return this.
Another point is that in your modifyMethod, after you checked if o[m] is a function, you should pass in that function, not m which is just the key. So it should be o[m] = modifyFunction(o[m]);
function modifyFunction(f) {
return function () {
var returnValue = f.apply(this, arguments);
// Return the object that call this function,
// so it becomes chainable.
return this;
};
}
function modifyMethod(o, m) {
if (o.hasOwnProperty(m)) {
if (o[m] instanceof Function) {
// Pass o[m], not m, m is a string.
o[m] = modifyFunction(o[m]);
}
}
}
var o = {
num: 0,
add: function (x) {
return this.num += x;
},
sub: function (x) {
return this.num -= x;
}
};
modifyMethod(o, "add");
o.add(2).add(4);
console.log(o.num); // o.num = 6
modifyMethod(o, "sub");
o.sub(1).add(3).sub(5);
console.log(o.num); // o.num = 3
-----------------File1.html-------------------------
<body>
<div id="tooltip"></div>
<script src="lib/d3.js"></script>
<script src="lib/underscore.js"></script>
<script src="js/mapper.js"></script>
<script>
d3.json('data/readme.json', function (error, data) {
var mpr = chordMpr(data);
_.each(data, function (elem) {
mpr.addToMap(name(elem.name))
})
mpr.setFilter(function (row, a, b) {
/* alert(JSON.stringify(a) +"-------"+JSON.stringify(row)+"-------"+JSON.stringify(b));
alert(a.name + "--" + row.name + "--" + b.name); */
return (name(row.name) === a.name)
})
.setAccessor(function (recs, a, b) {
if (!recs[0]) return 0;
var n = 0;
_.each(recs, function (r) {
//alert(r.imports)
_.each(r.imports, function (i) {
// alert(JSON.stringify(b) + "----" + i);
if (name(i) === b.name) n++;
});
});
return n;
});
alert(/* "*****" + mpr.getMatrix() + "-----" + */ JSON.stringify(mpr.getMap()));
drawChords(mpr.getMatrix(), mpr.getMap());
});
function name(name) {
return name.substring(0, name.lastIndexOf(".")).substring(6);
}
---------------------------File1.html-----------------------------
------------------------------mapper.js---------------------------
//*******************************************************************
// CHORD MAPPER
//*******************************************************************
function chordMpr (data) {
alert(data + "kimi is faster than you");
var mpr = {}, mmap = {}, n = 0,
matrix = [], filter, accessor;
mpr.setFilter = function (fun) {
alert("inside filter");
filter = fun;
return this;
},
mpr.setAccessor = function (fun) {
accessor = fun;
return this;
},
mpr.getMatrix = function () {
matrix = [];
_.each(mmap, function (a) {
if (!matrix[a.id]) matrix[a.id] = [];
_.each(mmap, function (b) {
var recs = _.filter(data, function (row) {
return filter(row, a, b);
})
matrix[a.id][b.id] = accessor(recs, a, b);
});
});
return matrix;
},
mpr.getMap = function () {
return mmap;
},
mpr.printMatrix = function () {
_.each(matrix, function (elem) {
console.log(elem);
})
},
mpr.addToMap = function (value, info) {
if (!mmap[value]) {
mmap[value] = { name: value, id: n++, data: info }
}
},
mpr.addValuesToMap = function (varName, info) {
alert("inside addValuesToMap " + varName + info);
var values = _.uniq(_.pluck(data, varName));
alert(values);
//values is the list of countries in the importer[i] column in CSV form
_.map(values, function (v) {
//v is individual country in the importer[i] column
if (!mmap[v]) {
mmap[v] = { name: v, id: n++, data: info }
}
});
return this;
}
return mpr;
}
//*******************************************************************
// CHORD READER
//*******************************************************************
function chordRdr (matrix, mmap) {
return function (d) {
var i,j,s,t,g,m = {};
if (d.source) {
i = d.source.index; j = d.target.index;
s = _.where(mmap, {id: i });
t = _.where(mmap, {id: j });
m.sname = s[0].name;
m.sdata = d.source.value;
m.svalue = +d.source.value;
m.stotal = _.reduce(matrix[i], function (k, n) { return k + n }, 0);
m.tname = t[0].name;
m.tdata = d.target.value;
m.tvalue = +d.target.value;
m.ttotal = _.reduce(matrix[j], function (k, n) { return k + n }, 0);
} else {
g = _.where(mmap, {id: d.index });
m.gname = g[0].name;
m.gdata = g[0].data;
m.gvalue = d.value;
}
m.mtotal = _.reduce(matrix, function (m1, n1) {
return m1 + _.reduce(n1, function (m2, n2) { return m2 + n2}, 0);
}, 0);
return m;
}
}
I am having difficulty in analysing the code flow in the above code.
In the mpr.setFilter(function (row, a, b)) call, what are row, a and b? I can't find any variables by this name. Also how are the calls translating from one function to other.
Please help.
setFilter is a function which takes an argument of a function that is stored to a "member" variable filter. When the user calls setFilter and passes their function they must know the contract that it takes three arguments. These are your row, a, and b arguments, they aren't assigned here, just there to make the function match later on.
If you trace this a little further that anonymous function passed into setFilter and stored in the variable filter is then called in the getMatrix function in mapper.js. You'll see when it is called the arguments of row, a and b are passed in.
Ain't JavaScript fun?
I took js-combinatorics code and produced this:
(function(global) {
'use strict';
if (global.Combinatorics) return;
/* common methods */
var addProperties = function(dst, src) {
Object.keys(src).forEach(function(p) {
Object.defineProperty(dst, p, {
value: src[p]
});
});
};
var hideProperty = function(o, p) {
Object.defineProperty(o, p, {
writable: true
});
};
var toArray = function(f) {
var e, result = [];
this.init();
while (e = this.next()) result.push(f ? f(e) : e);
this.init();
return result;
};
var common = {
toArray: toArray,
map: toArray,
forEach: function(f) {
var e;
this.init();
while (e = this.next()) f(e);
this.init();
},
filter: function(f) {
var e, result = [];
this.init();
while (e = this.next()) if (f(e)) result.push(e);
this.init();
return result;
}
};
/* Cartesian Product */
var arraySlice = Array.prototype.slice;
var cartesianProduct = function() {
if (!arguments.length) throw new RangeError;
var args = arraySlice.call(arguments);
args = args[0];
console.log(args);
var
size = args.reduce(function(p, a) {
return p * a.length;
}, 1),
sizeOf = function() {
return size;
},
dim = args.length,
that = Object.create(args, {
length: {
get: sizeOf
}
});
if (!size) throw new RangeError;
hideProperty(that, 'index');
addProperties(that, {
valueOf: sizeOf,
dim: dim,
init: function() {
this.index = 0;
},
get: function() {
if (arguments.length !== this.length) return;
var result = [];
arguments.forEach(function(element,index,array) {
var i = arguments[index];
if(i >= this[index].length) return;
result.push(this[index][i]);
});
return result;
},
nth: function(n) {
var result = [];
arguments.forEach(function(element,index,array) {
var l = this[index].length,
i = n % l;
result.push(this[index][i]);
n -= i;
n /= l;
});
return result;
},
next: function() {
if (this.index >= size) return;
var result = this.nth(this.index);
this.index++;
return result;
}
});
addProperties(that, common);
that.init();
return that;
};
/* export */
addProperties(global.Combinatorics = Object.create(null), {
cartesianProduct: cartesianProduct
});
})(this);
var _ = [];
_[1] = [1,4];
_[7] = [2,9];
cp = Combinatorics.cartesianProduct(_);
console.log(cp.toArray());
I expect to get this result in the end:
[[1,2],[1,9],[4,2],[4,9]]
But keep getting Uncaught TypeError: undefined is not a function in Chrome and TypeError: arguments.forEach is not a function in Firefox every time I use forEach in this part of code:
nth: function(n) {
var result = [];
arguments.forEach(function(element,index,array) {
var l = this[index].length,
i = n % l;
result.push(this[index][i]);
n -= i;
n /= l;
});
return result;
}
Keeping indexes of _ array is a must.
arguments is not an Array, so it doesn't have a forEach method.
You can convert it to an array just like you did in var args = arraySlice.call(arguments);, or you use a for loop to iterate over its elements.
I needed to post the _ array with non-strict indexation:
var _ = [];
_[1] = [1,4];
_[7] = [2,9];
The default solutions are no-go, because they do not handle such arrays. So I had to tweak Bergi's idea found here:
function cartesian(arg) {
var r = [], max = arg.length-1;
function helper(arr, i) {
while(typeof arg[i] === "undefined") {
i += 1;
}
for (var j=0, l=arg[i].length; j<l; j++) {
var a = arr.slice(0); // clone arr
a.push(arg[i][j]);
if (i==max) {
r.push(a);
} else
helper(a, i+1);
}
}
helper([], 0);
return r;
}