I'm trying to add a hook to each of the functions in the object, follows are my code and it's runs good.
function foo(){}
foo.beforeMethodHook = function(){console.log('Hook was called.');}
foo.getInstance = function(){
var newInstance = new foo;
var funcNames = Object.getOwnPropertyNames(foo);
for(i in funcNames){
var funcName = funcNames[i];
if(funcName == 'getInstance' || funcName == 'beforeMethodHook' || Object.hasOwnProperty(funcName)) continue;
newInstance[funcName] = function (){
foo.beforeMethodHook();
return foo[this].apply(foo,arguments);
}.bind(funcName);
}
return newInstance;
}
foo.test1 = function(arg1){console.log('test1 was called. arg1 = ' + arg1);return true;}
foo.test2 = function(arg1,arg2){console.log('test2 was called. arg1 = ' + arg1 + ' , arg2 = ' + arg2);return true;}
//Test
var f = foo.getInstance();
f.test1('ahaha');
f.test2('heihei','houhou');
As IE10- don't support function(){}.bind(), I tried to change the .bind() to (function(){})() which follows
newInstance[funcName] = (function (){
foo.beforeMethodHook();console.log(arguments);
return foo[funcName].apply(foo,arguments);
})(funcName);
But the problem comes, I lose arguments that f.test1('ahaha') has passed. the arguments array only gives ["test1"] which is the function name.
How can I fix that? Thanks in advance.
You can implement your own bind. Easy version:
if (!Function.prototype.bind) {
Function.prototype.bind = function(that) {
var fn = this;
return function() {
fn.apply(that, arguments);
}
};
}
or proper version:
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
Code taken from:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
try using the alternative code for bind given over here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fbind
you should do something like this
newInstance[funcName] = (function (){
console.log(arguments);
return function(){
foo.beforeMethodHook();
foo[funcName].apply(foo,arguments);
}
})(funcName);
Related
I need to implement inheritance tree in JavaScript where each node can have more than 1 parent. We have to implement Object.Create and Object.call methods on our own. We are specifically not allowed to use new keyword. Here is what I have so far:
var myObject = {
hash:0,
parents: [],
create: function(args){
//TODO check if not circular
if(args instanceof Array){
for(i=0;i<args.length;i++){
this.parents.push(args[i]);
}
}
return this;
},
call : function(fun,args){
//TODO: dfs through parents
return this[fun].apply(this,args);
},
}
var obj0 = myObject.create(null);
obj0.func = function(arg) { return "func0: " + arg; };
var obj1 = myObject.create([obj0]);
var obj2 = myObject.create([]);
obj2.func = function(arg) { return "func2: " + arg; };
var obj3 = myObject.create([obj1, obj2]);
var result = obj0.call("func", ["hello"]);
alert(result);
//calls the function of obj2 istead of obj0
The problem with this code is that I get a call to obj2's function instead of obj0's. I'm suspecting that create() function should not return this, but something else instead (create instance of itself somehow).
In your current solution, you are not actually creating a new object with your myObject.create() function, you are just using the same existing object and resetting it's parent array. Then, when you define .func() you are overriding that value, which is why func2: appears in your alert.
What you need to do is actually return a brand new object. returning this in your myObject.create() will just return your existing object, which is why things are getting overridden.
To avoid using the new keyword, you'll want to do either functional inheritance or prototypal inheritance. The following solution is functional inheritance:
function myObject (possibleParents) {
//create a new node
var node = {};
//set it's parents
node.parents = [];
//populate it's parents if passed in
if (possibleParents) {
if (possibleParents instanceof Array) {
for (var index = 0; index < possibleParents.length; index++) {
node.parents.push(possibleParents[index]);
}
} else {
node.parents.push(possibleParents);
};
}
//
node.call = function(fun,args) {
return this[fun].apply(this,args);
};
return node;
};
var obj0 = myObject();
obj0.func = function(arg) { return "func0: " + arg; };
var obj1 = myObject([obj0]);
var obj2 = myObject();
obj2.func = function(arg) { return "func2: " + arg; };
var obj3 = myObject([obj1, obj2]);
var result = obj0.call("func", ["hello"]);
alert(result); // this will successfully call "func0: " + arg since you created a new object
I managed fix this problem only by using function instead of variable.
function myObject () {
this.parents = [];
this.setParents = function(parents){
if(parents instanceof Array){
for(i=0;i<parents.length;i++){
this.parents.push(parents[i]);
}
}
};
this.call = function(fun,args) {
return this[fun].apply(this,args);
};
}
var obj0 = new myObject();
obj0.func = function(arg) { return "func0: " + arg; };
var obj2 = new myObject();
obj2.func = function(arg) { return "func2: " + arg; };
var result = obj0.call("func", ["hello"]);
alert(result);
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I want to create an object that has modified versions of all of the methods in a source object, but I'm having trouble using for...in.
If this is my source object:
var raw = {};
raw.add = function(a,b){return a + b;}
raw.sub = function(a,b){return a - b;}
raw.neg = function(a){return -a;}
raw.sqrt = function(a){return Math.sqrt(a);}
It works if I recreate the list of properties in an array of strings:
var mod2 = Object.create(raw);
var proplist = ["add", "sub", "neg", "sqrt"];
proplist.forEach(function(prop){
mod2[prop] = function(){
var arglist = [].slice.apply(arguments);
var out = [];
if(arglist.length == 1){
[].concat(arglist[0]).forEach(function(d){ out.push(raw[prop](d)); });
}
else if(arglist.length == 2){
[].concat(arglist[0]).forEach(function(d1){
[].concat(arglist[1]).forEach(function(d2){
out.push(raw[prop](d1,d2));
})
});
}
return out;
}
});
But my attempt to use for..in doesn't work, all of the methods in the new object will do "sqrt":
var modified = Object.create(raw);
for(prop in raw){
modified[prop] = function(){
var arglist = [].slice.apply(arguments);
var out = [];
if(arglist.length == 1){
[].concat(arglist[0]).forEach(function(d){ out.push(raw[prop](d)); });
}
else if(arglist.length == 2){
[].concat(arglist[0]).forEach(function(d1){
[].concat(arglist[1]).forEach(function(d2){
out.push(raw[prop](d1,d2));
})
});
}
return out;
}
}
What is the best way to iterate through the methods automatically?
The issue with your second implementation is that you are using prop in your new method (which will be called sometime later), but the for loop that creates prop has already run to completion by the time that method is called sometime later so prop is not the right value any more (it will always be the last property). I fixed that in my implementation by capturing prop in an IIFE (immediately invoked function expression) so it would be frozen separately for each pass through the for loop. Your first implementation doesn't have that problem because you're using .forEach() on the array of properties which uses a callback function which captures the value of prop for you automatically into a closure.
So here's the result with these changes to your implementation:
Add an IIFE to freeze the value of prop for use in the new methods.
Add an extra check to make sure the methods we're copying are not inherited and are functions.
Initialized raw to a plain object as I don't see any reason to use Object.create() here.
The code:
var raw = {};
raw.add = function(a,b){return a + b;}
raw.sub = function(a,b){return a - b;}
raw.neg = function(a){return -a;}
raw.sqrt = function(a){return Math.sqrt(a);}
var modified = {};
for (prop in raw) {
if (raw.hasOwnProperty(prop) && typeof raw[prop] === "function") {
(function (prop) {
modified[prop] = function () {
var arglist = [].slice.apply(arguments);
var out = [];
if (arglist.length == 1) {
[].concat(arglist[0]).forEach(function (d) {
out.push(raw[prop](d));
});
} else if (arglist.length == 2) {
[].concat(arglist[0]).forEach(function (d1) {
[].concat(arglist[1]).forEach(function (d2) {
out.push(raw[prop](d1, d2));
})
});
}
return out;
}
})(prop);
}
}
Working demo: http://jsfiddle.net/jfriend00/5LcLh/
<script>
var raw = {};
raw.add = function () { console.log('add default method'); }
raw.sub = function () { console.log('sub default method'); }
raw.neg = function () { console.log('neg default method'); }
raw.sqrt = function () { console.log('sqrt default method'); }
console.log('*****************');
console.log('before modifying');
console.log('*****************');
raw.add();
raw.sub();
raw.neg();
raw.sqrt();
var proplist = ["add", "sub", "neg", "sqrt"];
console.log('*****************');
console.log('after modifying');
console.log('*****************');
console.log('');
var modified = Object.create(raw);
for (prop in proplist) {
if (prop == 0)
console.log('rewriting methods and calling methods inside loop................');
modified[proplist[prop]] = function () { console.log(proplist[prop] + ' method modified, ' + proplist.length + ' argument passed') }
modified[proplist[prop]]();
}
console.log('');
console.log('trying call methods after loop is done................');
modified.add();
modified.sub();
modified.neg();
modified.sqrt();
console.log('...it is becaouse "prop" variable in loop holding last count number ' + prop);
</script>
thanks to arnold.NET.JS's response clarifying the problem, I see that closure is one way to do it:
var raw = {};
raw.add = function(a,b){return a + b;}
raw.sub = function(a,b){return a - b;}
raw.neg = function(a){return -a;}
raw.sqrt = function(a){return Math.sqrt(a);}
var mod = Object.create(raw);
for(prop in raw){
mod[prop] = (function(){
var propname = prop;
function f(){
var arglist = [].slice.apply(arguments);
var out = [];
if(arglist.length == 1){
[].concat(arglist[0]).forEach(function(d){ out.push(raw[propname](d)); });
}
else if(arglist.length == 2){
[].concat(arglist[0]).forEach(function(d1){
[].concat(arglist[1]).forEach(function(d2){
out.push(raw[propname](d1,d2));
})
});
}
return out;
}
return f;
})();
}
This code will pass the last value created by the loop the eventListener function, I need the value at the time the eventListener was created to be attached.
window.onload = function() {
var el = document.getElementsByClassName('modify');
for (var i = 0; i < el.length; i++) {
var getId=el[i].id.split("_");
document.getElementById("modify_y_"+getId[2]).addEventListener('mouseover', function() {
document.getElementById("modify_x_"+getId[2]).style.borderBottom = '#e6665 solid 3px';
}, false);
}
}
You can use the bind prototype which exists on all functions in modern browsers
window.onload = function() {
var el = document.getElementsByClassName('modify');
for (var i = 0; i < el.length; i++) {
var getId=el[i].id.split("_");
document.getElementById("modify_y_"+getId[2]).addEventListener('mouseover', function(theid) {
document.getElementById("modify_x_"+getId[2]).style.borderBottom = '#e6665 solid 3px';
}.bind(null,getId[2]), false);
}
}
If you need to support older browsers that doesn't have bind nativly built in, you can use this poly-fill taken from MDN where you will also find documentation on the bind prototype function
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
You do that by using a builder function:
window.onload = function () {
var el = document.getElementsByClassName('modify');
for (var i = 0; i < el.length; i++) {
var getId = el[i].id.split("_");
document.getElementById("modify_y_" + getId[2]).addEventListener('mouseover', makeHandler(getId[2]), false);
}
function makeHandler(theId) {
return function () {
document.getElementById("modify_x_" + theId).style.borderBottom = '#e6665 solid 3px';
};
}
};
The function returned by makeHandler closes over the theId argument, which doesn't change.
I have this code (JSFiddle)
var OBJ = function(){
var privateVar = 23;
var self = this;
return {
thePrivateVar : function() {
return privateVar;
},
thePrivateVarTimeout : function() {
setTimeout(function() { alert(self.thePrivateVar()); } , 10);
}
}
}();
alert(OBJ.thePrivateVar());
OBJ.thePrivateVarTimeout();
This is an abstraction of a real problem I'm having.
So - I would expect the call to OBJ.thePrivateVarTimeout() to wait 10 and then alert with 23 (which I want it to access through the other exposed method).
However self doesn't seem to be setting correctly. When I am setting self = this it appears that this isn't a reference to the function but a reference to the global object. Why is this?
How can I make the public method thePrivateVarTimeout call the other public method thePrivateVar?
var OBJ = (function(){
var privateVar = 23;
var self = {
thePrivateVar : function() {
return privateVar;
},
thePrivateVarTimeout : function() {
setTimeout(function() { alert(self.thePrivateVar); } , 10);
}
};
return self;
}());
this === global || undefined inside an invoked function. In ES5 it's whatever the global environment is, in ES5 strict it is undefined.
More common patterns would involve using the var that = this as a local value in a function
var obj = (function() {
var obj = {
property: "foobar",
timeout: function _timeout() {
var that = this;
setTimeout(alertData, 10);
function alertData() {
alert(that.property);
}
}
}
return obj;
}());
or using a .bindAll method
var obj = (function() {
var obj = {
alertData: function _alertData() {
alert(this.property);
}
property: "foobar",
timeout: function _timeout() {
setTimeout(this.alertData, 10);
}
}
bindAll(obj)
return obj;
}());
/*
bindAll binds all methods to have their context set to the object
#param Object obj - the object to bind methods on
#param Array methods - optional whitelist of methods to bind
#return Object - the bound object
*/
function bindAll(obj, whitelist) {
var keys = Object.keys(obj).filter(stripNonMethods);
(whitelist || keys).forEach(bindMethod);
function stripNonMethods(name) {
return typeof obj[name] === "function";
}
function bindMethod(name) {
obj[name] = obj[name].bind(obj);
}
return obj;
}
Is there a way to specify something similar to the following in javascript?
var c = {};
c.a = function() { }
c.__call__ = function (function_name, args) {
c[function_name] = function () { }; //it doesn't have to capture c... we can also have the obj passed in
return c[function_name](args);
}
c.a(); //calls c.a() directly
c.b(); //goes into c.__call__ because c.b() doesn't exist
Mozilla implements noSuchMethod but otherwise...no.
No, not really. There are some alternatives - though not as nice or convenient as your example.
For example:
function MethodManager(object) {
var methods = {};
this.defineMethod = function (methodName, func) {
methods[methodName] = func;
};
this.call = function (methodName, args, thisp) {
var method = methods[methodName] = methods[methodName] || function () {};
return methods[methodName].apply(thisp || object, args);
};
}
var obj = new MethodManager({});
obj.defineMethod('hello', function (name) { console.log("hello " + name); });
obj.call('hello', ['world']);
// "hello world"
obj.call('dne');
Almost 6 years later and there's finally a way, using Proxy:
const c = new Proxy({}, {
get (target, key) {
if (key in target) return target[key];
return function () {
console.log(`invoked ${key}() from proxy`);
};
}
});
c.a = function () {
console.log('invoked a()');
};
c.a();
c.b();
No.