I have a javascript function (class) that takes a function reference as one paremter.
function MyClass ( callBack ) {
if (typeof callBack !== 'function')
throw "You didn't pass me a function!"
}
For reasons I won't go in to here, I need to append something to the function by enclosing it in an anonymous function, but the only way I've been able to figure out how to do it is by adding a public function to MyClass that takes the callBack function as a parameter and returns the modified version.
function MyClass () {
this.modifyCallBack = function ( callBack ) {
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
/* new code goes here */
}
return callBack;
}
}
/* elsewhere on the page, after the class is instantiated and the callback function defined */
myCallBackFunction = MyClassInstance.modifyCallBack( myCallBackFunction );
Is it possible to make this work when passing the callBack function as a parameter to the class? Attempting to modify the function in this manner when passign it as a parameter seems to only affect the instance of it in within the class, but that doesn't seem like it's a valid assumption since functions are Objects in javascript, and are hence passed by reference.
Update: as crescentfresh pointed out (and I failed to explain well), I want to modify the callBack function in-place. I'd rather not call a second function if it's possible to do all of this when the class is instantiated.
Function objects don't provide methods to modify them. Therefore, what you want to do is impossible the way you want to do it. It's the same thing Jon Skeet likes to point out about Java: Objects are not really passed by reference, but instead a pointer to them is passed by value. That means that changing the value of an argument variable to a new one won't affect the original one at all.
There are only two ways to do what you want in call-by-value languages like Java and JavaScript: The first one would be to use the (function) object's methods to modify it. As I already stated, function objects don't have those. The other one is to pass the object of which the function object is a property as a second argument and set the appropriate property to a new function which wraps the old one.
Example:
var foo = {};
foo.func = function() {};
function wrapFunc(obj) {
var oldFunc = obj.func;
obj.func = function() {
// do some stuff
oldFunc.call(obj, _some_argument__);
};
}
wrapFunc(foo);
This works for global functions as well: they are properties of the window object.
As Javascript uses lexical scoping on variables the following is possible:
var modifiableCallback=function() { alert('A'); };
function ModifyCallbackClass(callback)
{
modifiableCallback=function() { callback(); alert('B'); };
}
function body_onload()
{
var myClass=new ModifyCallbackClass(modifiableCallback);
modifiableCallback();
}
This does what you want, however the function "modifiableCallback" must be referred to with the same name inside ModifyCallbackClass, otherwise the closure will not be applied. So this may limit the usefulness of this approach for you a little.
Using eval (performance may suffer a bit) it is also possible to make this approach more flexible:
var modfiableCallback1=function() { alert('A'); };
var modfiableCallback2=function() { alert('B'); };
var modfiableCallback3=function() { alert('C'); };
function ModifyCallbackClass(callbackName)
{
var temp=eval(callbackName);
var temp2=eval(callbackName);
temp= function() { temp2(); alert('Modified'); };
eval(callbackName + " = temp;");
}
function body_onload()
{
var myClass=new ModifyCallbackClass("modfiableCallback1");
modfiableCallback1();
myClass=new ModifyCallbackClass("modfiableCallback2");
modfiableCallback2();
myClass=new ModifyCallbackClass("modfiableCallback3");
modfiableCallback3();
}
I assume you are saving this callback somewhere... Any reason this won't work?
function MyClass ( callBack ) {
var myCallBack;
if (typeof callBack !== 'function')
throw "You didn't pass me a function!"
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
/* new code goes here */
}
myCallBack = callback;
}
You want to do something like:
function MyClass () {
this.modifyCallBack = function ( callBack ) {
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
alert("new functionality");
}
return callBack;
}
}
/* elsewhere on the page, after the class is instantiated and the callback function defined */
var myCallBackFunction = function () {alert("original");};
var MyClassInstance = new MyClass();
myCallBackFunction = MyClassInstance.modifyCallBack( myCallBackFunction );
myCallBackFunction();
Related
I am trying to create function findStrings which should share it's variables with the function init, stringSplit and stringSearch.
String.prototype.findStrings = (function () {
var action = 'split';
var params = ',';
var init = function (action = 'split', params = ',')
{ this.stringSplit(); }
var stringSplit = function (action)
{ return this.init(); }
var stringSearch = function (action)
{return this.init(); }
return init;
})();
What I need.
When I call test.findStrings('split'); it should 1) initiate variables by calling init 2) then call stringSplit so this should be string test.
When I call var result = test.findStrings( 'search', { params ... } ); 1) initiate variables, process arguments from object params 2) then call stringsSearch and this should be test.
var test = '#a > :matches(.b, #c)';
test.findStrings('split', "," );
var result = test.findStrings( 'search', { params ... } );
Error which I got is saying that this does not have stringSplit (because this is Window) and I need to this to be test.
How to access the test string within the functions and make it possible to access the variables and arguments within them?
Actually, I could remove the function searchStrings to simplify it.
Solution:
String.prototype.findStrings = (function () {
var text = this;
...
String.prototype.init = function (action = 'split', params = ',') {
text.stringSplit();
}
}
There were two errors.
I need to this to be test.
When init() is first called, this does equal test. The problem is that stringSplit and stringSearch are just variables, and have nothing to do with the current context.
Either way, the code is pretty difficult to understand. If you really want to override String, then why don't you do something like:
String.prototype.findStrings = function(action = 'split', params = ',') {
// do logic here (not entirely sure what you want to accomplish)
return (action === 'split') ? this.stringSplit() : this.stringSearch();
}
// Add other functions also to string prototype
String.prototype.stringSplit = function() { ... }
String.prototype.stringSearch = function() { ... }
Personally though, I don't like adding methods to types like String for various reasons. Why not adopt a more functional approach, and create functions that you simply pass strings to, and get an appropriate result?
For example:
function findStrings(str, params){
// do logic and return result
}
First of all, you have a circular reference in your code: the init function calls stringSplit which calls init.
But to reach to that problem you should remove the this keyword from your prototype function. this will get the value of the object that is calling your function, in your case the window object, but your functions are created in a closure so you can't access them using the window object.
My code is very simple. Ans to me it should work.
var preview = WinJS.Class.define(
function (el, options) {
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
this.preview.addEventListener('click', this.click, false);
//WinJS.Utilities.query("button", this.form)
//this.preview.addEventListener('', this.save, false);
},
{
click: function (e) {
this.form.style('display', 'block');
}
}
);
WinJS.Namespace.define('RegCtrl', { preview: preview });
But when click occurs this.form seems to be undefined of null. Why? I do not want to initialize objects in every method of the class.
New tests
I made additional test very small
var preview = WinJS.Class.define(
function (el, options) {
var test = 1;
this.test = 1;
this.test1();
},
{
test1: function () {
console.log(this.form, test);
}
}
);
WinJS.Namespace.define('RegCtrl', { preview: preview });
This test fails on line this.test1();. What I think now that this class is called RegCtrl.preview() rather than new RegCtrl.preview().
How do I shek inside the function that this called as new but not a simple function?
The other answers aren't explaining what's going on, and as such are giving incorrect advice.
JavaScript has first-class function objects - you can pass them around as values. That's exactly what you're doing when you set up this callback:
this.preview.addEventListener('click', this.click, false);
You're taking the contents of the this.click property, which happens to be a function, and handing it to the addEventListener function to do whatever it wants with it.
I was very specific about terminology there - note I specifically said function, not method. JavaScript doesn't really have a method construct, it just has methods as properties on an object.
So where does the "this" member come from? It's determined at the caller - the object you use on the left side of the '.' is the one that becomes the value of this. For example,
function exampleFunc() { console.log("this.myName = " + this.myName); }
var a = { myName: "Chris", doSomething: exampleFunc };
var b = { myName: "Bob", doSomething: exampleFunc };
Note I've assigned the exact same function to the doSomething properties. What what happens:
a.doSomething(); // Outputs "this.myName = Chris"
b.doSomething(); // Outputs "this.myName = Bob"
The exact same function object, called through two different objects, has a different this pointer.
exampleFunc is a global function, let's call it:
exampleFunc() // Outputs "this.myName = undefined"
So where'd the undefined come from? In a global function, "this" is set to window (the global scope), which didn't have the myName property defined. Which also means that you could do this instead:
myName = "Global Name"; // note, no var - we want this global
exampleFunc(); // Outputs "this.myName = Global Name"
Ok, so what's going on with the original question? Basically, you've passed the function this.click to be the callback, but you haven't passed the "this" pointer that you want it called through. Actually, addEventListener doesn't have a way to pass the this pointer. As a result, when the function is invoked this is not pointing at your object. I don't remember off the top of my head what it's pointing at - it's either window or the element that was clicked on, check the DOM documentation to verify.
To get it to call the right function with the right context (context = the correct "this"), the traditional approach is to use a closure. Capture "this" in a variable, then pass in an anonymous function that calls your actual callback with the right this pointer. The code looks like this:
var preview = WinJS.Class.define(
function (el, options) {
// Capture your current this pointer in a global variable
// Using "that" as the name comes from JavaScript: The Good Parts book
var that = this;
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
// Note what gets passed instead of this.click:
this.preview.addEventListener('click',
function (e) {
// NOTE: Calling through "that": "this" isn't pointing to the right object anymore
// Calling through "that" resets "this" inside the call to click
that.click(e);
}, false);
},
{
click: function (e) {
this.form.style('display', 'block');
}
}
);
This is a common enough pattern that ECMAScript 5 has a utility function to build these wrappers for you - function.bind. Do this:
this.preview.addEventListener('click',
this.click.bind(this),
false);
The construct this.click.bind(this) will construct a new function that, when called, will set the "this" reference to whatever you passed (in this case "this"), and then invoke the function you called it on.
Yes, there are a lot of different values for "this" floating around. Keeping track of what "this" is pointing at is an important part of mastering JavaScript programming.
I think you may want to define a global JavaScript variable as :
var myForm = document.getElementById('perview-form');
or jest define var myForm; and initialize inside function (el, options) as:
myForm = d.getElementById('perview-form');
Now you can use this variable in your function as :
myForm.style('display', 'block');
EDIT: I believe you may define this variable as first line in your WinJS.Class.define to make it instance level variable as below:
var preview = WinJS.Class.define(
var myForm;
function (el, options) {
....
....
myForm = d.getElementById('perview-form');
...
},
{
click: function (e) {
myForm.style('display', 'block');
}
});
This is a really hard thing to research if you don't know what to look for. I added one line and changed another line. That should fix your issue.
In short, the keyword this gets reset every time you enter a new function, this the value of this inside your click function is not the same this of the outer scope. Preserve this this you want. The name of that seems fairly common.
Edited based on the link provided by the OP.
This code is UNTESTED. If using this doesn't work now, then I'd try this2
Sorry I can't test this, but I don't have the framework anywhere so I'm doing
educated guesswork.
var preview = WinJS.Class.define(
function (el, options) {
that = this; // No var should be needed since it is declared already
el.winControl = this;
this.el = el;
this.textarea = d.getElementById('preview-input');
this.preview = d.getElementById('preview-text');
this.form = d.getElementById('perview-form');
this.preview.addEventListener('click', this.click, false);
//WinJS.Utilities.query("button", this.form)
//this.preview.addEventListener('', this.save, false);
},
// This is the section for instance vars
{
click: function (e) {
that.form.style('display', 'block'); // AND THIS ONE
},
that: null // Added instance variable
},
// And these are static variables
{
that2: null
}
);
I'm trying to solve this puzzle minded Javascript OOP problem.
So I have the following class :
var ClassA = function() {
this.initialize();
}
ClassA.prototype = {
methods : ['alpha','beta','gama'],
initialize : function() {
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
}
}
var a = new ClassA();
When I call every method I expect to print the name of it, right? But here is what i get :
a.alpha(); // returns gama ?!?
a.beta(); // returns gama ?!?
a.gama(); // returns gama
But when my class looks like this :
var ClassB = function() {
this.initialize();
}
ClassB.prototype = {
methods : ['alpha', 'beta', 'gama'],
initialize: function() {
for ( var i in this.methods ) {
this.addMethod(this.methods[i]);
}
},
addMethod: function(method) {
this[method] = function() {
console.log(method);
}
}
}
var b = new ClassB();
b.alpha(); // returns alpha
b.beta(); // returns beta
b.gama(); // returns gama
Why is this happening ?
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
Your problem lies here. When this loop ends, i is the last element. Each function uses the same i, so they are all the last element.
When you use addMethod you are making a closure to "capture" the correct value.
EDIT: When you call addMethod you are "copying" the value, instead of using the i value, which changes with each loop iteration.
In your first version:
initialize : function() {
for ( var i in this.methods ) {
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}
}
The methods that you create within initialize all refer to the same i variable from initialize - and after initialize runs i has the value "gama", so regardless of which of the methods you call that's the value of i that they'll log to the console. JS doesn't store the current value of i at the time the method is created.
JS creates a "closure" for each function - variables declared in your initialize function (i.e., i) continue to be in scope for the nested function(s) even after initialize has finished.
The second version calls addMethod to add each method:
addMethod: function(method) {
this[method] = function() {
console.log(method);
}
}
...and so when they run they'll refer to their own "copy" of the method parameter because then there is a separate closure for each of the methods.
Edit: See also this question: How do JavaScript closures work? (several answers there explain this more clearly than I did).
You can fix your first example by adding an anonymous closure:
initialize : function() {
for ( var i in this.methods ) {
(function (i) { // anonymous closure
this[this.methods[i]] = function() {
console.log(this.methods[i]);
}
}).call(this, i); // use .call() if you need "this" inside
}
}
Now it will work the same way as your second example. "Anonymous" means that the closure is made by function which doesn't have a name and is called instantly as it is "created".
Note sideways: use .call(this, ...) to preserve this inside the called function, or you can do var that = this, use that instead of this and call the function normally:
for ( var i in this.methods ) {
var that = this;
(function (i) { // anonymous closure
that[that.methods[i]] = function() {
console.log(that.methods[i]);
}
})(i); // Called normally so use "that" instead of "this"!
}
Well, first of all stop using for (property in object) loops on Arrays. It's all fun and games until somebody prototypes to the Array object which is both a perfectly reasonable and very useful/popular thing to do. This will result in custom methods getting added to your for x in array loops.
As for the problem, it's doing exactly what you told it to do in version 1. The problem is that by the time you get around to firing it, i is the last thing i was, 'gamma'. When you pass a reference into a function as an argument, the function holds on to the value's state as it was passed.
Are the JavaScript code snippets given below some sort of function declaration? If not can someone please give an overview of what they are?
some_func = function(value) {
// some code here
}
and
show:function(value){
// some code here
}
There are six ways/contexts in which to create functions:
1) Standard declarative notation (most familiar to people with C background)
function foo() {}
All the rest are function expressions:
2) As a method of an object literal
var obj = {
foo: function() {}
};
3) As a method of an instantiated object (created each time new is exectued)
var Obj = function() {
this.foo = function() {};
};
4) As a method of a prototype (created only once, regardless of how many times new is executed)
var Obj = function() {};
Obj.prototype.foo = function() {};
5) As an anonymous function with a reference (same effect as #1) *
var foo = function() {};
6) As an immediately executed anonymous function (completely anonymous)
(function() {})();
* When I look at this statement, I consider the result. As such, I don't really consider these as anonymous, because a reference is immediately created to the function and is therefore no longer anonymous. But it's all the same to most people.
The first one is simply creating an anonymous function and assigning it to a variable some_func. So using some_func() will call the function.
The second one should be part of an object notation
var obj = {
show:function(value){
// some code here
}
};
So, obj.show() will call the function
In both cases, you are creating an anonymous function. But in the first case, you are simply assigning it to a variable. Whereas in the second case you are assigning it as a member of an object (possibly among many others).
First is local (or global) variable with assigned anonymous function.
var some_name = function(val) {};
some_name(42);
Second is property of some object (or function with label in front of it) with assigned anonymous function.
var obj = {
show: function(val) {},
// ...
};
obj.show(42);
Functions are first-class citizens in JavaScript, so you could assign them to variables and call those functions from variable.
You can even declare function with other name than variable which that function will be assigned to. It is handy when you want to define recursive methods, for example instead of this:
var obj = {
show: function(val) {
if (val > 0) { this.show(val-1); }
print(val);
}
};
you could write:
var obj = {
show: function f(val) {
if (val > 0) { f(val-1); }
print(val);
}
};
One way of doing it:
var some_func = function(value) {
// some code here
}
Another way:
function some_funct() {
}
Yet another way:
var some_object={};
some_object["some_func"] = function() {};
or:
var some_object={};
some_object.some_func = function() {};
In other words, they are many ways to declare a function in JS.
Your second example is not correct.
The first one is a function declaration assigned to a variable (at least it should be, despite the fact that it's missing the variable type declaration first), the second one is probably related to a object declaration.
They are called anonymous functions; you can read more about them here:
http://www.ejball.com/EdAtWork/2005/03/28/JavaScriptAnonymousFunctions.aspx
The first example creates a global variable (if a local variable of that name doesn't already exist) called some_func, and assigns a function to it, so that some_func() may be invoked.
The second example is a function declaration inside an object. it assigns a function as the value of the show property of an object:
var myObj = {
propString: "abc",
propFunction: function() { alert('test'); }
};
myObj.propFunction();
The first one...
some_func = function(value) {
// some code here
}
is declaring a variable and assigned an anonymous function to it, which is equivalent to...
function some_func (value) {
// some code here
}
The second one should look like this...
obj = {
show:function(value){
// some code here
}
}
// obj.show(value)
and equivalent to...
//pseudo code
class MyClass {
function show (value) {
// some code here
}
}
obj = new MyClass(); // obj.show(value)
Cheers
Javascript code:
function doSomething(v1,v2){ //blah; }
function SomeClass(callbackFunction,callbackFuncParameters(*Array*))={
this.callback = callbackFunction;
this.method = function(){
this.callback(parameters[0],parameters[1]) // *.*
}
}
var obj = new SomeClass( doSomething, Array('v1text','v2text') );
The problem is if I change function doSomething to
function doSomething(v1,v2,v3){ //blah; }
I have to change the corresponding line (marked as //*.*) in SomeClass to
this.callback(parameters[0],parameters[1],parameters[2]);
What can be done to avoid the (*.*) line to be changed no matter how the number of 'doSomething' function's parameters is changed?
Thanks a lot!
You probably want to use the apply method
this.callback.apply(this, parameters);
The first parameter to apply indicates the value of "this" within the callback and can be set to any value.
Another way now available is to use spread syntax.
this.callback(...callbackFuncParameters)
Here it is again with the full example from the OP:
function doSomething(v1,v2) {
console.log('doing', {v1, v2});
}
function SomeClass(callbackFunction, callbackFuncParameters) {
this.callback = callbackFunction;
this.method = function(){
this.callback(...callbackFuncParameters); // spread!
}
}
var obj = new SomeClass( doSomething, Array('v1text','v2text') );
obj.method()
// output: doing {v1: "v1text", v2: "v2text"}