I have a Object based on some closure, and want to implement event scheme here:
var class1 = function(val1)
{
var val = val1;
//------ want to call a method of Object of class1--------
var self = this;
setTimeout(function()
{
self.onEvent();
}, 1000);
//----------------
return {
f1: function()
{
return val;
},
onEvent: function()
{
console.log('not implemented yet. Override');
}
};
};
var obj1 = class1(5);
console.log(obj1.f1()); //5
obj1.onEvent(); //not implemented yet. Override
obj1.onEvent = function()
{
console.log('event fired');
}
got error, and I know the reason, and I need a solution:
5
not implemented yet. Override
/....../app.js:9
self.onEvent();
^
TypeError: Object #<Object> has no method 'onEvent'
It is possible if this bind with addEventListener scheme like this:
(The idea based on
Implementing events in my own object
)
var class2 = function()
{
var _this = this;
_this.events = {};
var fireEvent = function(name, args)
{
if (!_this.events.hasOwnProperty(name)) return;
if (!args || !args.length) args = [];
var evs = _this.events[name];
var l = evs.length;
for (var i = 0; i < l; i++)
{
evs[i].apply(null, args);
}
};
setTimeout(function()
{
fireEvent('testEvent', ['hello'])
}, 1000);
return {
addEventListener: function(name, handler)
{
if (_this.events.hasOwnProperty(name))
_this.events[name].push(handler);
else
_this.events[name] = [handler];
}
};
};
var obj2 = class2();
obj2.addEventListener('testEvent',
function(data)
{
console.log('event fired: ' + data);
});
event fired: hello
However, I prefer not to use addEventListener but .onEvent() scheme.
Is it possible? Perhaps it is possible using call/apply.
Thanks for your advice.
In your first block of code, you are returning an object, which is different from this or self.
You don't necessarily have to return this in your constructors but you should assign your functions on the returned object. If you create a variable for the object you want to return, you can use it in your setTimeout callback like so:
var class1 = function(val1)
{
var val = val1;
var obj = {
f1: function()
{
return val;
},
onEvent: function()
{
console.log('not implemented yet. Override');
}
};
setTimeout(function()
{
obj.onEvent();
}, 1000);
return obj;
};
For extra style points, you might want to capitalize the name of your constructors (and perhaps use new to instantiate them to make things clearer to your readers).
Related
I want to bind the parameters of an Object to another Object, so they update whenever the other one updates aswell.
Object1 =
x: 1
Object2 =
x: Object1.x
y: 0
so that Object1.x = 2; updates Object2 aswell. (And the other way round)
How do I do that efficiently? I could use .watch and update the other one on each change, but I doubt that is a smart solution. Am I missing something?
Depending on the environment this has to run, a getter might be a suitable solution:
Object.defineProperty(Object2, 'x', {
get: function() {
return Object1.x;
},
enumerable: true
});
You might also want to define a setter to sync changes back.
You need to implement the observer/observable pattern.
I could use .watch and update the other one on each change, but I
doubt that is a smart solution
How you notify the other part if something changes? Why isn't a smart solution? It's just the solution!
Check this code listing I did to show you a possible implementation of objects capable of listening changes of other objects creating a base ObservableObject prototype (also available in JSFiddle!):
var ObservableObject = function () {
this._handlers = [];
this._disablePropertyChangeNotification = false;
};
ObservableObject.prototype = {
get disablePropertyChangeNotification() {
return this._disablePropertyChangeNotification;
},
set disablePropertyChangeNotification(value) {
this._disablePropertyChangeNotification = value;
},
listenPropertyChange: function (handler) {
this._handlers.push(handler);
},
notifyPropertyChange: function (propertyName) {
if (!this.disablePropertyChangeNotification) {
this._handlers.forEach(function (handler) {
handler(propertyName);
});
}
},
};
var A = function () {};
A.prototype = new ObservableObject();
Object.defineProperty(A.prototype, "name", {
get: function () {
return this._name;
},
set: function (value) {
this._name = value;
this.notifyPropertyChange("name");
}
});
var B = function () {};
B.prototype = new ObservableObject();
Object.defineProperty(B.prototype, "name", {
get: function () {
return this._name;
},
set: function (value) {
this._name = value;
this.notifyPropertyChange("name");
}
});
var someObjectA = new A();
var someObjectB = new B();
someObjectA.listenPropertyChange(function (propertyName) {
// This will prevent an infinite loop where
// property from A is set by B and viceversa
someObjectA.disablePropertyChangeNotification = true;
someObjectB[propertyName] = someObjectA[propertyName];
someObjectA.disablePropertyChangeNotification = false;
});
someObjectB.listenPropertyChange(function (propertyName) {
// This will prevent an infinite loop where
// property from A is set by B and viceversa
someObjectB.disablePropertyChangeNotification = true;
someObjectA[propertyName] = someObjectB[propertyName];
someObjectB.disablePropertyChangeNotification = false;
});
// We set name on A instance, and we print B instance name value
someObjectA.name = "hello world";
$(document.body).append("<p>someObjectB.name: " + someObjectB.name + "</p>");
// We set name on B instance, and we print A instance name value
someObjectB.name = "hello world changed";
$(document.body).append("<p>someObjectA.name: " + someObjectA.name + "</p>");
Let's say I have a function named fna() that does a simple thing such as:
var fna = function(ar) {
console.log("argument: ", ar);
return "return value is argument too: " + ar;
}
fna() is coded by some other developer and I can't access to it. He didn't bother casting any events and when it is called, I have to be aware of it. Hopefully, his method is accessible by window.fna().
I want some additional code to be executed. Let's say, add this console.log
var fna = function(ar) {
console.log("Hola, I am some additional stuff being rewired");
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
And I want this to be executed even when called from fnb() by some other part of the code.
var fnb = function() {
return fna("Bonjour, I am fnb and I call fna");
}
Here is a way I found, using the utils.rewire() method. utils is just some utility belt, and it could be added to your favorite framework as a plugin. Unfortunately, it only works on Firefox.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(functionFullName, callback) {
var rewired = window[functionFullName];
console.log("%s() is being rewired", functionFullName)
window[functionFullName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
Use it like this.
utils.rewire("fna",function(){
console.log("Hola, I am some additional stuffs being rewired");
});
This seems to work such as shown in this jsbin, but (and here is my question:) How do I rewire obja.fna()?
var obja = {
fna = function(ar) {
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
};
I cannot make it work to rewire the some.object.method() method.
Extra bonus question: Is there a more cleaner way to do this? Out-of-the-box clean concise and magic library?
Refactor rewire into a rewireMethod function which acts on any given object:
var utils = utils || {};
utils.rewireMethod = function (obj, functionName, prefunc) {
var original = obj[functionName];
obj[functionName] = function () {
prefunc();
return original.apply(this, arguments);
};
};
Note that rewire can now be written as:
utils.rewire = function (functionName, prefunc) {
utils.rewireMethod(window, functionName, prefunc);
};
Then you just call it as:
utils.rewireMethod(obja, "fna", function () {
console.log("Hola, I am some additional stuff being rewired");
});
Note that nothing special is required if you have a method like window.ideeli.Search.init(). In that case, the object is window.ideeli.Search, and the method name is init:
utils.rewireMethod(window.ideeli.Search, "init", function () {
console.log("Oh yeah, nested objects.");
});
Add a parameter to rewire that is the object containing the function. If it's a global function, pass in window.
var utils = utils || {};
// let's rewire a function. i.e. My.super.method()
utils.rewire = function(object, functionName, callback) {
var rewired = object[functionName];
console.log("%s() is being rewired", functionName)
object[functionName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
utils.rewire(some.object, "method", function(){} );
You can simply use a closure to create a generic hook function that allows you to specify another function to be called immediately before or after the original function:
function hookFunction(fn, preFn, postFn) {
function hook() {
var retVal;
if (preFn) {
preFn.apply(this, arguments);
}
retVal = fn.apply(this, arguments);
if (postFn) {
postFn.apply(this, arguments);
}
return retVal;
}
return hook;
}
So, for any function that you want to hook, you just call hookFunction and pass it the function you want to hook and then an optional pre and post function or yours. The pre and post function are passed the same arguments that the original function was.
So, if your original function was this:
var fna = function(ar) {
console.log("argument:",ar);
return "return value is argument too:"+ar;
}
And, you want something to happen every time that function is called right before it's called, you would do this:
fna = hookFunction(fna, function() {
console.log("Hola, I am some additional stuff being rewired right before");
});
or if you wanted it to happen right after the original was called, you could do it like this:
fna = hookFunction(fna, null, function() {
console.log("Hola, I am some additional stuff being rewired right after");
});
Working demo: http://jsfiddle.net/jfriend00/DMgn6/
This can be used with methods on objects and arbitrary nesting levels of objects and methods.
var myObj = function(msg) {
this.greeting = msg;
};
myObj.prototype = {
test: function(a) {
log("myObj.test: " + this.greeting);
}
}
var x = new myObj("hello");
x.test = hookFunction(x.test, mypreFunc2, myPostFunc2);
x.test("hello");
Based on Claudiu's answer, which seems to be the most appreciated way, here is a solution using a for loop and proxying the context... But still, I find this ugly.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(method, callback) {
var obj = window;
var original = function() {};
var tree = method.split(".");
var fun = tree.pop();
console.log(tree);
// Parse through the hierarchy
for (var i = 0; i < tree.length; i++) {
obj = obj[tree[i]];
}
if(typeof(obj[fun]) === "function") {
original = obj[fun];
}
var cb = callback.bind(obj);
obj[fun] = function(ar) {
cb();
return original.apply(this, arguments);
}
}
Well, this looks strange. Consider this
function wrap(fn, wrapper) {
return function() {
var a = arguments;
return wrapper(function() { return fn.apply(this, a) })
}
}
Example:
function foo(a, b) {
console.log([a, b])
return a + b
}
bar = wrap(foo, function(original) {
console.log("hi")
var ret = original()
console.log("there")
return ret
})
console.log(bar(11,22))
Result:
hi
[11, 22]
there
33
To wrap object methods, just bind them:
obj = {
x: 111,
foo: function(a, b) {
console.log([a, b, this.x])
}
}
bar = wrap(obj.foo.bind(obj), function(fn) {
console.log("hi")
return fn()
})
In a code below:
var FilledObjectArray = function() {
this.filledObject = {};
};
FilledObjectArray.prototype = {
fill: function() {
this.filledObject["one"] = 1;
}
};
var SomeClass = function() {
this.something = new FilledObjectArray();
};
SomeClass.prototype = {
showContents: function() {
this.something.fill();
for (key in this.something) {
$("#some-div").append(this.something[key]);
}
}
};
$(document).ready(function() {
var s = new SomeClass();
$(".bla").each(function() {
$(this).click(function() {
s.showContents();
});
});
});
I'm getting this error in Firebug console:
TypeError: this.filledObject is undefined
this.filledObject["one"] = 1;
What I'm doing wrong here? From what I understand object is properly initialized and value assigning is correct. I'm testing this in Firefox 18.0.2 version and Chrome 25.
I think this should solve your problem
this.something.fill.call(this.something);
I found this issue with the existing code and have created a proposed solution, however I'm not exactly clear on your intent so it may be a little different than your needs, please modify as needed.
The Issue
The main issue stems from the FilledObjectArray object. This object's prototype is assigned a function fill and then assigned a property one of type int with a value of 1. So remember this object has two properties, one a function one an int.
So when you execute this code...
for (key in this.something) {
$("#some-div").append(this.something[key]); //Item 1
}
Two iterations of the loop occur, once for the function fill and once for the property one. The issue occurs on the iteration for the fill key, since this.something[key] is passed to the append(), which cann accept functions, causing jQuery to trigger the function. When this occurs, within the execution context of the fill function, this is assigned to #some-div, which does not have a filledObject property, causing the TypeError to be thrown. I have commented on some of this below:
var FilledObjectArray = function() {
this.filledObject = {}; //Fill is an object
};
FilledObjectArray.prototype = {
fill: function() {
this.filledObject["one"] = 1;
}
};
var SomeClass = function() {
this.something = new FilledObjectArray();
};
SomeClass.prototype = {
showContents: function() {
this.something.fill();
for (key in this.something) {
$("#some-div").append(this.something[key]); //The fill function is called here
}
}
};
$(document).ready(function() {
var s = new SomeClass();
$(".bla").each(function() {
$(this).click(function() {
s.showContents();
});
});
});
Proposed Solution
var FilledObjectArray = function() {
this.filledObject = [];
};
FilledObjectArray.prototype.fill = function(){
console.log(this);
this.filledObject[0] = 1;
};
var SomeClass = function() {
this.something = new FilledObjectArray();
};
SomeClass.prototype = {
showContents: function() {
this.something.fill();
for (var x = 0; x < this.something.filledObject.length; x++){
$("#some-div").append(this.something.filledObject[x]);
}
}
};
$(document).ready(function() {
var s = new SomeClass();
$(".bla").each(function() {
$(this).click(function() {
s.showContents();
});
});
});
i think you looking for this:
for (key in this.something) {
if(this.something.hasOwnProperty(key)){
$("#some-div").append(JSON.stringify(this.something[key]));
}
}
jsbin
you need use hasOwnProperty to iterate over the properties of an object without executing on inherit properties.
I feel a bit dumb right now but here is the solution to this:
SomeClass.prototype = {
showContents: function() {
this.something.fill();
for (key in this.something.filledObject) {
if (this.something.filledObject.hasOwnProperty(key)) {
$("#some-div").append(this.something.filledObject[key]);
}
}
}
};
I was trying to access object itself (this.something) instead of object property (this.something.filledObject)
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.
This is what I'm doing right now.
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = function() { return x; };
return x;
}
It works but only if foo is called as a function like so
foo();
But what if I want to call it as a normal variable with a value? I could modify the code to be
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = x;
return x;
}
That would allow me to only call it once as a function and after that as a regular variable. But it's still not what I want. Plus it gets complicated if it accidentally gets called as a function again, returning an error.
Is this even possible in JavaScript?
BTW, this is for a Chrome/Firefox extension, so IE compatibility does not matter.
Ended up using toString because getters don't allow me to redefine the whole attribute, a function must be associated with it. And toString has cleaner syntax.
How about using toString?
var foo = function() {
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
}
More about Object-to-Primitive Conversions in JavaScript
EDIT based on comment. Use a singleton (I think it's called):
myObject.prop = (function(){
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
})()
If only Internet Explorer didn't exist, you could use getters and setters as described by John Resig in this blog article:
John Resig: JavaScript Getters and Setters
... They allow you to bind special functions to an object that look like normal object properties, but actually execute hidden functions instead.
Using a function is your best option for now, however the new JavaScript standard (ECMAScript 5th Ed.) which is being implemented now by all major browser vendors, gives you a method to create accessor properties, where you can define a property with a get and set functions that will be internally called, without worrying to treat this properties as functions, e.g.:
var obj = {};
Object.defineProperty(obj, 'foo', {
get: function () { // getter logic
return 'foo!';
},
set: function (value) {
// setter logic
}
});
obj.foo; // "foo!", no function call
This new standard will take some time to be implemented for all browsers, (the IE9 preview version really disappointed me), and I wouldn't recommend you to use it for production, unless you have total control on the environment where your application will be used.
What I think you want is a lazily instantiated variable, which can be implemented like this.
var myProperty = null;
function getMyProperty() {
return (myProperty = myProperty || builder());
}
This is not practical on the web because IE does not support it, but you can look at
https://developer.mozilla.org/en/defineGetter for examples how to do this.
There are a couple ways to do it, here is one example:
var data = {};
data.__defineGetter__("prop",
(function () {
var value = null;
return function () {
if (null == value) {
value = getYourValueHere();
}
return value;
};
})());
and now you can use it like:
var a = data.prop;
var b = data.prop;
I would recommend a variation on ChaosPandion's answer, but with a closure.
var myProperty = (function () {
var innerProperty = null;
return function() {
return (innerProperty = innerProperty || someComplicatedComputationThatMayTakeMoreTime());
};
})();
and then use myProperty() every time you need to access the variable.
You could define a JavaScript getter. From the Apple JavaScript Coding Guidelines:
myObject.__defineGetter__( "myGetter", function() { return this.myVariable; } );
var someVariable = myObject.myGetter;
See John Resig's post, JavaScript Getters and Setters, and the Defining Getters and Setters page at the Mozilla Developer Centre for more information.
I would use explicit lazy evaluation. Here's my implementation of it based on Scheme's take:
var delay, lazy, force, promise, promiseForced, promiseRunning;
(function () {
var getValue = function () {
return this.value;
};
var RUNNING = {};
var DelayThunk = function (nullaryFunc) {
this.value = nullaryFunc;
};
DelayThunk.prototype.toString = function () {
return "[object Promise]";
};
DelayThunk.prototype.force = function () {
if (promiseRunning (this)) {
throw new Error ("Circular forcing of a promise.");
}
var nullaryFunc = this.value;
this.value = RUNNING;
this.value = nullaryFunc ();
this.force = getValue;
return this.value;
};
var LazyThunk = function (nullaryFunc) {
DelayThunk.call (this, nullaryFunc);
};
LazyThunk.prototype = new DelayThunk (null);
LazyThunk.prototype.constructor = LazyThunk;
LazyThunk.prototype.force = function () {
var result = DelayThunk.prototype.force.call (this);
while (result instanceof LazyThunk) {
result = DelayThunk.prototype.force.call (result);
}
return force (result);
};
delay = function (nullaryFunc) {
return new DelayThunk (nullaryFunc);
};
lazy = function (nullaryFunc) {
return new LazyThunk (nullaryFunc);
};
force = function (expr) {
if (promise (expr)) {
return expr.force ();
}
return expr;
};
promise = function (expr) {
return expr instanceof DelayThunk;
};
promiseForced = function (expr) {
return expr.force === getValue || !promise (expr);
};
promiseRunning = function (expr) {
return expr.value === RUNNING || !promise (expr);
};
}) ();
Example Syntax:
var x = lazy (function () { return expression; });
var y = force (x);
var z = delay (function () { return expression; });
var w = force (z);
Note values are stored once evaluated, so repeated forcing will not do extra computations.
Example usage:
function makeThunk (x, y, z) {
return lazy (function () {
// lots of work done here
});
}
var thunk = makeThunk (arg1, arg2, arg3);
if (condition) {
output (force (thunk));
output (force (thunk)); // no extra work done; no extra side effects either
}
You can use the javascript Proxy class for creating such functionality.
var object = {};
var handler = {
resolvers: {},
get ( target, property, proxy ) {
if ( ! target.hasOwnProperty( property ) && this.resolvers.hasOwnProperty( property ) ) {
// execute the getter for the property;
target[ property ] = this.resolvers[ property ]();
}
return target[ property ];
},
set ( target, property, value, receiver ) {
// if the value is function set as a resolver
if ( typeof value === 'function' ) {
this.resolvers[property] = value;
// otherwise set value to target
} else {
target.property = value;
}
},
has ( target, property, receiver ) {
//true when proxy handler has either a resolver or target has a value;
return this.resolvers.hasOwnProperty( property ) || target.hasOwnProperty( property );
}
};
var lazyObject = new Proxy( object, handler );
Now you can use it like this:
'exampleField' in lazyObject; //returns false
lazyObject.exampleField = function(){ return 'my value' }; // add a resolver function
'exampleField' in lazyObject; //returns true
lazyObject.exampleField; //executes your resolver function and returns 'my value'
This example is to demonstrate the working. You can change after your needs.
Here is a fiddle with a demonstration