Change function behavior without proxy - javascript

I can do something like this:
var foo = ...// some function assignment
var fooString = foo.toString()
...
// add some alert to foo
...
var fooWithAlert = new Function(forStringWithAlert)
Is there a way to mutate first foo instead of creating new function?
I need it to monkey patch some dependency without recreating whole hierarchy of objects.
I need to patch a constructor function in a library, just add an alert on every call. But without juggling with prototypes

No, you can't modify the function foo refers to. You can only make foo refer to a new function that does what you want. One way to do that is to use your toString approach, but it's best to avoid that if at all possible, because the function you get as a result will not be the same as the original; the scope it has access to will be different.
Usually, you do want a proxy/wrapper, e.g.:
// The original foo
var foo = function(arg) {
return "original foo says '" + arg + "'";
};
console.log(foo("bar"));
// Let's wrap it
(function() {
var originalFoo = foo;
foo = function() {
return originalFoo.apply(this, arguments) + " plus updated foo";
};
})();
console.log(foo("bar"));
This doesn't create a hierarchy of objects or similar, it just wraps foo.
If foo is a constructor function (let's call it Foo), you'll also want to copy Foo.prototype:
// The original Foo
var Foo = function(arg) {
this.value = "original foo";
this.arg = arg;
};
Foo.prototype.getArg = function() {
return this.arg;
};
var f1 = new Foo("bar");
console.log(f1.getArg());
// Let's wrap it
(function() {
var originalFoo = Foo;
Foo = function() {
var rv = originalFoo.apply(this, arguments);
this.arg += " (plus more from augmented foo)";
return rv;
};
Foo.prototype = originalFoo.prototype;
})();
var f2 = new Foo("bar");
console.log(f2.getArg());
And of course, if you need to wrap a function on Foo.prototype, you can do it just like foo in my first example:
// The original Foo
var Foo = function(arg) {
this.value = "original foo";
this.arg = arg;
};
Foo.prototype.getArg = function() {
return this.arg;
};
var f = new Foo("bar");
console.log(f.getArg());
// Let's wrap its getArg
(function() {
var originalGetArg = Foo.prototype.getArg;
Foo.prototype.getArg = function() {
return originalGetArg.apply(this, arguments) + " updated";
};
})();
console.log(f.getArg());
Note how it doesn't matter that we wrapped the prototype function after creating the f object.

Related

How to add Object.prototype.x? set to enumerable:false

$('example')
console logs foo {word: "example", letter: function}
^
The log showed me letter is a prototype function of foo
var foo,$;
(function() {
foo = $ = function(word) {
return new foo(word);
};
var foo = function(word) {
//#A
this.word=word;
console.log(this.word);
return this;
};
foo.fn = foo.prototype = {
letter: function(n) {
console.log(this.word[n]);
return this;
}
};
}());
I can't seem to look at $.fn or foo.fn
console.log($.fn); //fn is undefined
console.log(foo.fn); //foo is undefined
console.log($.prototype); //shows me constructor and __proto__
Why? What if I want add more prototype functions later in the code? Why does it work like a charm with Array.prototype.something=function... but, not this code?
I am wondering how this would be done as I said above I can see the letter function but if instead of putting the delaration of letter inside the prototype I replace //A with
Object.defineProperty(this,"letter",{
value:
function(n) {
console.log(this.word[n]);
return this;
}
,enumerable:false});
that hides letter from appering Inside the foo object and it leaves the prototype empty. I don't seem to be able to access the prototype object and I don't want to see extra function junk appering in console logs If I did add something to it I would see it as junk unless I added it where //A is..
But then I am left with something that I can't (understand how to) modify with new methods.
The title of your question doesn't seem to be related to the problem, but here we go:
foo = $ = function(word) {
return new foo(word);
};
var foo = function(word) {
...
};
is equivalent to
var foo;
foo = $ = function(word) {
return new foo(word);
};
foo = function(word) {
...
};
I.e. you are creating a local variable foo which shadows the outer one. Thus you are never assigning to the global foo and never exposing foo.fn outside of the function.
You are assigning a function to $, but you are never assigning fn to that function.
If I understand correctly what you want to do, then you have to rename the variable so that it doesn't shadow the outer variable, and expose fn on the correct function (which will happen automatically if you renamed the variable):
var foo,$;
(function() {
foo = $ = function(word) {
return new InnerFoo(word);
};
var InnerFoo = function(word) {
//#A
this.word=word;
console.log(this.word);
return this;
};
foo.fn = InnerFoo.prototype = {
letter: function(n) {
console.log(this.word[n]);
return this;
}
};
}());

How do I use 'this' when converting code into an object?

So I can reuse code in more than one place on a web page, I am converting it to Object Oriented Javascript. The original code looks like this:
foo = 42;
function bar(a,b) {
foo = a * foo + b;
}
...
bar(1,1)
I think I want it to look like this:
function Example() {
this.foo = 42;
this.bar = function() {
this.foo = a * this.foo + b;
}
}
var one = new Example();
var two = new Example();
...
one.bar(1,1);
two.bar(2,3);
However, I'm not sure I'm using 'this' correctly inside the nested function. I've noticed that a few of the functions I'm going to convert are event handlers that already refer to 'this' in their body. How do I distinguish the 'this' already in the event handler from the 'this' I want to use to access my local variables?
Create a local var in the Example class/function and then you can reference the appropriate this object.
function Example() {
var self = this;
this.foo = 42;
this.bar = function() {
self.foo = a * self.foo + b;
}
}

instanceof, scopes and objects

could anyone explain this behaviour to me, please?
'use strict'
var fooFactory = function() {
function foo() {
var name = "foo object";
function getName() {
return name;
}
return {
getName: getName
}
}
function getFoo() {
return new foo;
}
return {
getFoo: getFoo,
foo: foo
}
};
var factory = new fooFactory();
var bar = factory.getFoo();
console.log("is " + bar.getName() + " instance of foo: " + (bar instanceof factory.foo));
// outputs "is foo object instance of foo: false"
i don't understand, why the foo object is no instance of the foo constructor i declare inside the fooFactory.
You're explicitly returning an object from the foo function. That object is not an instance of foo. Hence bar is not an instance of foo. This is how I would rewrite your code:
var foo = fooFactory();
var bar = new foo;
console.log("is " + bar.getName() + " instance of foo: " +
bar instanceof factory.foo);
function fooFactory() {
return function () {
var name = "foo object";
this.getName = function () {
return name;
};
};
}
The reason I would write it like this is as follows:
A factory function should not be called with new. Only constructor functions should be called with new. Furthermore even if you do call fooFactory with new it would make absolutely no difference as you're explicitly returning an object literal anyway. In fact it would be slower.
Instead of returning an object with the constructor function and another function which instantiates the constructor wouldn't it be better to simply return the newly created constructor itself? Since your factory is named fooFactory I would assume that it returns foo and not an object which has a foo property.
If you're going to create a function and use it as a constructor then don't explicitly return an object from it. When you call a function using new JavaScript automatically creates a new instance of the prototype of that function and binds it to this inside the function. As long as you don't explicitly return an object from the constructor function this is returned automatically.
Hope that helps.
To clarify Aadit's answer, the problem is this line:
return {
getName: getName
}
Remove that and change "getName" to be "this.getName" and your function will work as you're expecting.
var fooFactory = function() {
function foo() {
var name = "foo object";
// changed!
this.getName = function() {
return name;
}
// no return
}
function getFoo() {
return new foo;
}
return {
getFoo: getFoo,
foo: foo
}
};
var factory = new fooFactory();
var bar = factory.getFoo();
console.log("is " + bar.getName() + " instance of foo: " + (bar instanceof factory.foo));
// true!
remove return statement in function foo()
return {
getName: getName
}
new operator implicit return child instance of function foo,
but you did explicit return object { getName: getName }
{ getName: getName } is not child of function foo

Is it possible for a function called from within an object to have access to that object's scope?

I can't think of a way to explain what I'm after more than I've done in the title, so I'll repeat it. Is it possible for an anonymous function called from within an object to have access to that object's scope? The following code block should explain what I'm trying to do better than I can:
function myObj(testFunc) {
this.testFunc = testFunc;
this.Foo = function Foo(test) {
this.test = test;
this.saySomething = function(text) {
alert(text);
};
};
var Foo = this.Foo;
this.testFunc.apply(this);
}
var test = new myObj(function() {
var test = new Foo();
test.saySomething("Hello world");
});
When I run this, I get an error: "Foo is not defined." How do I ensure that Foo will be defined when I call the anonymous function? Here's a jsFiddle for further experimentation.
Edit: I am aware of the fact that adding the line var Foo = this.Foo; to the anonymous function I pass in to my instance of myObj will make this work. However, I'd like to avoid having to expose the variable inside the anonymous function--do I have any other options?.
Should be this.Foo:
var test = new myObj(function() {
var test = new this.Foo();
test.saySomething("Hello world");
});
http://jsfiddle.net/grzUd/5/
Or alternatively using with:
var test = new myObj(function() {
with (this) {
var test = new Foo();
test.saySomething("Hello world");
}
});
http://jsfiddle.net/grzUd/6/
Change var test = new Foo(); to var test = new this.Foo();.
Edit: Or you could pass it as a parameter.
function myObj(testFunc) {
this.testFunc = testFunc;
var Foo = function (test) {
this.test = test;
this.saySomething = function(text) {
alert(text);
};
};
this.testFunc(Foo);
}
var test = new myObj(function(Foo) {
var test = new Foo();
test.saySomething("Hello world");
});
You seem to be confused about the difference between identifier resolution on the scope chain and property resolution.
Foo is a property of an instance of myObj (i.e. it's an object property). Calling new Foo will resolve Foo as a variable on the scope chain, which isn't the right place to look for it. That's why Petah's answer tries to use with, to put the object properties of the this object on the scope chain.

JS: Passing a scope as argument, binding it to a function that will be returned

Is it possible to define a function in the scope of a parent function and bind an argument passed through the parent function as its scope before returning it?
Here is an example:
var myObject = {
foo: "bar"
};
var myFunction = (function() {
return function() {
return this.foo;
};
}).call(myObject);
myFunction.call(myObject); // I'd like to bind myObject as this does ...
myFunction(); // ... but I'd like to do it before invoking it, without .call() or .apply()
Or another complex example, that describes what I'm trying to do:
var createMyCopy = function(original, self) {
var copy;
eval("copy=" + original.toString());
console.log(copy()); // returns undefined
};
(function() {
var self = "Hello world",
myFunction = function() {
return self;
};
console.log(myFunction()); // returns "Hello world"
createMyCopy(myFunction);
})();
I'm trying to create a copy of a function, so that I can make changes to it without changing the original one, but I'd like to have the variables that are defined in the original one in the copy as well...
Do you mean like this?
var myObject = {
foo: "bar"
};
var myFunction = (function() {
var self = this;
return function() {
return self.foo;
};
}).call(myObject);
I think you're getting scope and context mixed up in your question.
I think you mean this:
var myFunction = (function(obj) {
return function() {
return obj.foo;
};
})(myObject);
The immediately invoked function expression is passed myObject and returns a new function in which that parameter is bound as the variable obj.
I'm not sure what you mean, but you can do this:
var myObject = {
foo: "bar"
};
function myFunction() {
return this.foo;
}
console.log(myFunction.apply(myObject)); // bar
The first argument of apply is the context (ie. what this refers to). The second argument of apply is an array of arguments (but I've omitted that here as there are no arguments).

Categories

Resources