I'm not sure if I phrased the question title correctly; please consider the following to clarify...
(function() {
var foo = {
bar: function() {
// Is it possible to reference 'this' as the
// initializing 'object' aka 'e' and not 'foo' ?
// The easy part, currently because 'this' refers to 'foo',
// is returning 'this' aka 'foo' so that chaining can occur
return this;
},
other: function() {
return this;
}
};
Event.prototype.foo = foo;
}());
// usage
document.onmousemove = function(e) {
e.foo.bar().other();
};
How would I go about having access to this within the methods / props of foo but having this refer to the initial object aka e and not foo ?
The best that I have come up with is this
(function() {
var foo = function() {
var _foo = this.foo;
_foo._this = this; //recursive reference that I am VERY worried about
return _foo;
};
foo.bar = function() {
var _this = this._this; //_this refers to initial 'object', 'e'
return this; //return 'foo' aka 'this' for function chaining
};
foo.other = function() {
var _this = this._this;
return this;
};
Event.prototype.foo = foo;
}());
// usage
document.onmousemove = function(e) {
e.foo().bar().other();
};
What I have currently works but I am worried about a couple of things...
1. The recursive reference of assigning e to e.foo._this
and
2. The redundancy of assigning e to e.foo._this, if this could be accessed as e instead of foo it would make 'things' more performant, especially in regards to something like a mousemove event.
jsFiddle Here
Also, Im trying to avoid something like this...
document.onmousemove = function(e) {
e.foo.bar.call(e);
};
All suggestions are appreciated, Thanks for your time.
With a subtle change to what you have you can make things simpler:
(function() {
var foo = function() {
this.foo.event = this;
return this.foo;
};
foo.bar = function() {
/// the event can be found in this.event
return this;
};
foo.other = function() {
/// the event can be found in this.event
return this;
};
Event.prototype.foo = foo;
}());
// usage
document.onmousedown = function(e) {
e.foo().bar().other();
};
This however is making a change to the shared object foo, you may wish to rewrite things so that e.foo() returns a new instance of foo instead, and move your other methods to foo's prototype.
(function() {
var foo = function(event) {
this.event = event;
};
foo.prototype.bar = function() {
/// the event can be found in this.event
return this;
};
foo.prototype.other = function() {
/// the event can be found in this.event
return this;
};
Event.prototype.foo = function() {
return new foo(this);
};
}());
This way you are creating a new instance of foo each time, but it means your addition of the event property is localised to that instance; the prototyped methods will be shared across all instances so it's not too bad from an optimisational point of view.
Maybe that would work for you:
Use the apply method to change the this context in the called method and use this.foo to refer to foo:
(function () {
var foo = function () {
console.log(this);
return this.foo;
};
foo.bar = function () {
console.log(this);
return this.foo;
};
foo.other = function () {
console.log(this);
return this.foo;
};
Event.prototype.foo = foo;
}());
// usage
document.onclick = function (e) {
console.log(
e.foo.apply(e).bar.apply(e).other.apply(e)
);
};
FIDDLE
Maybe it would be simpler to bind your function to its object :
someElement.onEvent = myObject.myHandlerFunction.bind(myObject);
so when this function will be called, its 'this' will be myObject.
Then you can make use of e.target to access the element.
Related
I am trying to create a simple callback system that would fire upon hitting a button. Rather than the callback being a factory function, it is a prototype method of a different object. I've gotten it to work but I don't understand it. Why do I need to use .bind(object) to get the object to fire its method? Originally I tried no bind, and then bind(this), which both failed.
function Bar() {}
Bar.prototype = {
getStuff: function () {
return "Hello";
},
setStuff: function () {
console.log( this.getStuff() );
}
}
function Foo() {
this.afterSubmit = null;
var self = this;
$('button').click(function () {
self.submit()
});
return this;
}
Foo.prototype = {
submit: function () {
if (this.afterSubmit !== null) {
this.afterSubmit();
}
$('#msg').append('clicked ');
return this;
},
setAfterSubmit: function (callback) {
this.afterSubmit = callback;
return this;
}
}
var bar = new Bar();
var foo = new Foo().setAfterSubmit(bar.setStuff.bind(bar));
// Why do I need to bind bar ?
Please take a look at my fiddle
https://jsfiddle.net/j5qfuzna/
this.afterSubmit();
This is setting the context to the Foo instance. Binding it to the Bar instance prevents that from happening.
Is it possible to change a constructor so that some extra action is run after an object is created. I tried something like:
var origFoo = Foo
Foo = function() {
origFoo.apply(this, arguments);
/* extra actions */
}
Foo.prototype = new origFoo();
but this has several problems like the constructor being run twice or changing the prototype chain.
You are very close. You should assign the Foo.prototype to the origFoo.prototype in order to get the same prototype chain. Everything else is spot on!
Example:
var Foo = function () {
console.log('OriginalFoo');
};
Foo.prototype.method1 = function () {
console.log('Method1');
};
OriginalFoo = Foo;
Foo = function () {
OriginalFoo.apply(this, arguments);
console.log('NewFoo');
};
Foo.prototype = OriginalFoo.prototype;
Foo.prototype.method2 = function () {
console.log('Method2');
};
var x = new Foo();
x.method1();
x.method2();
Demo: http://jsbin.com/ibatah/1/edit?js,console,output
PS: There still is the problem of static-like properties (Foo.prop), but i'm afraid i don't have a solution for that other than copying them one at a time.
EDIT: Solution for special constructors.
Indeed there are constructors which don't like to be called as functions ex: Image. To get over it, you can do the more awkard solution below. You take advantage of the fact that you can return an object from the constructor and it takes the place of the one created with new. In the overridden constructor you must always use this new object when calling methods instead of this.
var Foo = function(a,b,c) {
console.log('OriginalFoo',a,b,c);
};
Foo.prototype.prop1 = 'Property1';
Foo.prototype.method1 = function() {
console.log('Method1', this.prop1);
};
OriginalFoo = Foo;
Foo = function(a,b,c) {
var obj = new OriginalFoo(a,b,c);
obj.init('Changed...'); // or this.init.call(obj,'Changed!');
this.init('Not Changed'); // applies to a discarded object, has no effect
console.log('NewFoo');
return obj;
};
Foo.prototype = OriginalFoo.prototype;
Foo.prototype.prop2 = 'Property2';
Foo.prototype.method2 = function() {
console.log('Method2', this.prop2);
};
Foo.prototype.init = function(param) {
this.prop2 = param;
};
var x = new Foo('1','2','3');
console.log(x.prop1);
console.log(x.prop2);
x.method1();
x.method2();
Demo: http://jsbin.com/ibatah/2/edit?js,console,output
I have a class method and a closure within this method. How I can access to class member from closure?
Person = function(x) {
this.x = x;
}
Person.prototype = {
myMethod: function() {
$('#myBtn').click( function() {
// how to access to this.x? the this reference points in another context
});
}
}
Use of Function.prototype.bind will help you here
Person = function(x) {
this.x = x;
}
Person.prototype.myMethod = function() {
$('#myBtn').click(function() {
this.x;
}.bind(this));
};
You can use some better separation of code here too
Person = function(x) {
this.x = x;
};
Person.prototype.myMethod = function {
$('#myBtn').click(this.clickHandler.bind(this));
};
Person.prototype.clickHandler = function(event) {
console.log(this.x);
};
Note if you want to support older browsers, check out es5-shim
EDIT
I'm revisiting this after ~6 months and I would probably write the above code differently. I like the private/public exposure here. Also, no need for any fanciful binds or anything like that ^.^
function Person(x, $button) {
// private api
function onClick(event) {
console.log(x);
}
function myMethod() {
$button.click();
}
// exports
this.x = x;
this.myMethod = myMethod;
// init
$button.click(onClick);
}
var b = $("#myBtn"),
p = new Person("foo", b);
p.x; // "foo"
p.myMethod(); // "foo"
btn.click(); // "foo"
Just assign this to some other variable, for example _this:
Person = function(x) {
this.x = x;
}
Person.prototype = {
myMethod: function() {
var _this = this;
$('#myBtn').click( function() {
console.log(_this.x);
});
}
}
Person = function(x) {
this.x = x;
}
Person.prototype = {
myMethod: function() {
var self = this;
$('#myBtn').click( function() {
// Access to self.x
});
}
}
A proxy would be very useful here.
Right now, you're assigning an anonymous function to the click event. By default the context will be the event's and separate from your object.
With a proxy you can assign a particular context to a (callback) function. Thus, when the event fires, you're dealing with your person object.
Assign the event handler in a separate function like initialize(), and have myMethod() be the handler.
Use a JQuery.proxy() to assign the object`s context to the event handler.
Person.prototype = {
initialize: function() {
$('#myBtn').click($.proxy(this.myMethod, this));
},
myMethod: function(event) {
...
console.log(this); // Person object
}
}
Just to elicit the difference between my and #naomik's solution:
JQuery.proxy() is a temporary or narrow assignment to a context.
Function.prototype.bind() is a strong context assignment. The method will be "forever" bound to the context you give it.
How can i get variable in handler function of obj? Without reference of the obj in MyClass.
var obj = {
func: function(){
var myClass = new MyClass();
myClass.handler = this.handler;
myClass.play();
},
handler: function(){
//Here i don't have access to obj
console.log(this); //MyClass
console.log(this.variable); //undefined
},
variable:true
};
function MyClass(){
this.play = function(){
this.handler();
};
this.handler = function(){};
};
obj.func();
That's construction need you, if you use Base.js or another similar way of oop.
_.bindAll(obj) (underscore metod) also not suitable. It's break overriding in Base.js.
Bind only handler method: http://jsfiddle.net/uZN3e/1/
var obj = {
variable:true,
func: function(){
var myClass = new MyClass();
// notice Function.bind call here
// you can use _.bind instead to make it compatible with legacy browsers
myClass.handler = this.handler.bind(this);
myClass.play();
},
handler: function(){
console.log(this.variable);
}
};
function MyClass(){
this.play = function(){
this.handler();
};
this.handler = function(){};
};
obj.func();
Use a variable to refer original context:
...
var self = this;
myClass.handler = function(){ self.handler(); };
...
Declare variable before handler:
var obj = {
variable: true,
func: function(){
// ...
},
handler: function(){
console.log(this.variable); //true
}
};
Use Function call with this from obj in declared in a scope var to solve it.
var obj = {
func: function(){
var self = this;
var myClass = new MyClass();
myClass.handler = function() { return this.handler.call(self); };
myClass.play();
},
handler: function(){
//Here i don't have access to obj
console.log(this); //MyClass
console.log(this.variable); //undefined
},
variable:true
};
You don't have access to obj because this is bind to the instance of MyClass constructor - myClass. If in handler you want to have access to myClass through this and access to obj you have to use obj name directly so:
console.log(this); // myClass
console.log(obj.variable); // true
If you want to have this bind to obj use what Juan Mellado or gryzzly suggested.
Currently, I create objects in javascript by declaring a construction (regular function) then add methods to the prototype like so
function Test(){
}
Test.prototype.test1 = function(){
var me = this;
}
However, I would like to avoid having to declare var me = this at the top of every function. The following seems to work, but seems like it would be very inefficient:
$(document).ready(function(){
var n = 0;
(function(){
function createTest(){
var me;
function Test(){
this.n = n;
this.testArr = [1, 2, 3, 4];
n++;
}
Test.prototype.test1 = function(){
me.test2();
};
Test.prototype.test2 = function(){
alert(me.n);
$.getJSON('test.php', {}, function(reply)
//want to be able to use 'me' here
me.newField = reply;
});
};
var t = new Test();
me = t;
return t;
}
window['createTest'] = createTest;
})();
var t = createTest();
t.test1();
var t2 = createTest();
t2.test1();
t.test1();
});
This code outputs the expected, but is it actually as inefficient as it looks (the Test object being re-declared every time you call createTest())?
Anyhoo, this would seem a bit hacky... is there a completely different way to do this that is better?
EDIT: The real reason I would like to do this is so that callbacks like the one in test2 will have references to the correct this.
What you can do is bind the current this value to a function and store a copy somewhere. (For the sake of efficiency.)
if (!Function.prototype.bind) {
// Most modern browsers will have this built-in but just in case.
Function.prototype.bind = function (obj) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () { },
bound = function () {
return self.apply(this instanceof nop ? this : (obj || {}),
args.concat(slice.call(arguments)));
};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;
};
}
function Test(n) {
this.n = n;
this.callback = (function () {
alert(this.n);
}).bind(this)
}
Test.prototype.test1 = function () {
this.test2();
}
Test.prototype.test2 = function () {
doSomething(this.callback);
}
function doSomething(callback) {
callback();
}
var t = new Test(2);
t.test1();
I realize your question was not tagged with jQuery, but you are using it in your example, so my solution also utilizes jQuery.
I sometimes use the $.proxy function to avoid callback context. Look at this simple jsfiddle example. Source below.
function Test(){
this.bind();
}
Test.prototype.bind = function(){
$('input').bind('change', $.proxy(this.change, this));
// you could use $.proxy on anonymous functions also (as in your $.getJSON example)
}
Test.prototype.change = function(event){
// currentField must be set from e.target
// because this is `Test` instance
console.log(this instanceof Test); // true
console.log(event.target == $('input')[0]); // true
this.currentField = event.target; // set new field
};
function createTest(){
return new Test();
}
$(function(){ // ready callback calls test factory
var t1 = createTest();
});
Most of the time, I just declare a local variable that references this, wherever I need a reference to this in a callback:
function Foo() {
}
Foo.prototype.bar = function() {
var that=this;
setTimeout(function() {
that.something="This goes to the right object";
}, 5000);
}
Alternatively, you can use bind() like this:
Function Foo() {
this.bar = this.bar.bind(this);
// ... repeated for each function ...
}
Foo.prototype.bar = function() {
}
What this gives you is that every time you create a new Foo instance, the methods are bound to the current instance, so you can use them as callback functions for setTimeout() et al.