I have a context function type that is defined as below:
var Context = function () {
this.run = function () {
method1();
method2();
}
var method1 = function () {
}
}
As it is clear in the definition, method2 is not defined in the context. I need every instance of Context passes its implementation of this method.
var c = new Context();
// This does not work! because the call in run() function
// is not this.method2();
c.method2 = function () {
alert("injected method2");
};
c.run();
I need to keep method2() in run without use of this object i.e. this.method2();
Any solution?
If you can define method2 before creating Context it will work no problem:
function method2() {
alert(2);
}
var c = new Context();
c.run();
You can add method2 to the window object instead of the c object, in which case it will work.
Note that this is a clear indicator of poor design. You should probably look into doing this differently.
Callback approach:
var Context = function (callback) {
this.run = function () {
method1();
if(callback) callback();
}
var method1 = function () {
}
}
var c = new Context(function () {
alert("injected method2");
});
c.run();
If you change your run method to the following it should work as expected
this.run = function () {
method1();
this.method2();
}
UPDATE: I just realized it looks like you want to be able to do this on all instances of Context objects. In that case you would also need to define method2 on Context.prototype and not just on c
Context.prototype.method2 = function () {
console.log("injected method2dfd");
};
Related
I am trying to test if a local method (method1) is being called from another method(method2). I tried something like this but it does not work as the method1() still has the original definition. The code snippet looks like this:
var ClassA = function () {
var method1 = function () {
console.log('method1');
};
var method2 = function () {
method1();
};
return {method1: method1, method2: method2}
}
Test case:
it("should call method1 when method2 is called", function () {
var objectA = new ClassA();
spyOn(objectA, 'method1').andCallThrough;
objectA.method2();
expect(objectA, 'method1').toHaveBeenCalled();
});
Tried overriding method1 to without any success:
objectA.method1 = jasmine.createSpy('aSpy').andCallThrough();
When you call new ClassA() you invoke the ClassA function and get a new object with the related prototype. This means that when you spyOn(ClassA, "doSomething") you're not setting up a spy on the object returned by the new call, but on a possible doSomething function on the ClassA function itself.
You should be able to do something like:
it("calls method1", function() {
var originalConstructor = ClassA,
spiedObj;
spyOn(window, 'ClassA').and.callFake(function() {
spiedObj = new originalConstructor();
spyOn(spiedObj, 'method1');
return spiedObj;
});
method2();
expect(spiedObj.method1).toHaveBeenCalled();
});
Please let me know if this works if not we can discuss more.
How can I check if a private function exist inside an object?
var myObj = function(){
var myFunc = function(){};
var init = function(){
//has myFunc been defined?
}
}
I know that I can do this:
if (typeof myFunc == 'function') {
//myFunc exist
}
But this is checking the global scope.
How can I limit this to my objects scope?
Here is the most simplified case that i need:
var myComponent = function () {
var exportExcel = function () {
};
this.export = function (type) {
if('export'+type is a private function in this scope){
window["export"+type]()//but in local scope;
}
}
};
And here is my work around for now :
var myComponent = function () {
var Exports = {
Excel: function () {
}
};
this.export = function (type) {
if (Exports.hasOwnProperty(type)) {
Exports[type]();
} else {
alert('This Export type has not been implemented Yet ! or it never will ... how knows? well i don\'t ...');
}
}
};
As you probably noticed:
function myFunc () {};
function myObj () {
function init () {
if (myFunc) // passes
};
}
You could cheat a bit :-|
function myObj () {
var isdef = { myFunc: true };
function myFunc () {};
function init () {
if (isdef.myFunc) // do something
};
}
I wonder why one would do that though.
Bases on the extra information given, the most practical pattern is what you're calling the "temporary workaround": keeping your functions in a private object, keyed by type.
var myComponent = function () {
var exporters = Object.create(null, {
"Excel": function () {
// do magic export here
}
});
this.export = function (type) {
if (type in exporters) {
// defined locally
return exporters[type].call(this); // binding is optional
} else {
// no export for you!
}
}
};
This prevents two things:
Referencing the function via string composition,
Querying the global scope (or, actually, any scope in between your component and the global scope).
This may not be your design principle, you could further extend this code to allow for adding / removing exporters.
I create object var myObj = new functon () {...}.
In that object i add functions like :
var myObj = new function () {
this.func1 = function() {
func2();
}
this.func2 = function() {
...
}
}
As you can see in func1 I try to call func2 but it is always undefined. Why? Cause everything is in one object.
Change your scripts to
var myObj = function () {
var self = this;
this.func1 = function () {
self.func2();
};
this.func2 = function () {
...
};
};
On top of solutions provided by others. If you are going to call a javascript function that is defined like this
var func = function(){}
the function definition needs to come before the function call.
In the other way of defining a function this does not matter.
function func(){}
So Overall Code should be
var myObj = function(){
this.func2 = function(){
...
}
this.func1 = function(){
func2();
}
}
It's undefined because you don't have local variable func2. So correct reference should be this.func2().
However even in this case your code is not ideal construction object like this (mixing constructor and anonymous function) (although correct). In this case it's better to use object literal in the first place rather then create constructor function for just creating one single object instance:
var myObj = {
func1: function () {
this.func2();
},
func2: function () {}
};
You should call func2 like this
var myObj = new function () {
this.func1 = function () {
this.func2();
}
this.func2 = function () {
console.log('func2');
}
}
myObj.func1();
if you want call func2 with this. and without, you can do it like this
var myObj = new function () {
function func2() {
console.log('func2');
}
this.func1 = function() {
this.func2();
func2();
}
this.func2 = func2;
}
myObj.func1();
you can call like this.
Calling func2() directly, searches the function of window object.
var myObj = functon(){
var current = this;
this.func1 = function(){
current.func2();
}
this.func2 = function(){
...
}
};
I have tried to create javascript object like
function Caller() {
this.init = function() {
makeCall();
};
this.makeCall = function(){/*come code here*/}
}
var a = new Caller();
a.init();
I got error function is not defined, same thing happens when I try to call this.makeCall();
When I remove this from makeCall definition it works, but when I remove also from init it doesn't work. How to solve this ?
Use this.makeCall(). Additionally define makeCall() before its use.
function Caller() {
this.makeCall = function () { /*come code here*/ }
this.init = function () {
this.makeCall();
};
}
var a = new Caller();
a.init();
DEMO
Use this.makeCall() it is a method so you can use it.
Try this this.makeCall() function is executed successfully here.
<html>
<head>
</head>
<body>
<script>
function Caller() {
this.init = function() {
this.makeCall();
};
this.makeCall = function(){
alert('hello');
}
}
var a = new Caller();
a.init();
</script>
</body>
</html>
You need to call the function properly using this
makeCall(); => this.makeCall();
You can use IIFE to declare Caller:
var Caller=(function () {
function constructor() {
}
function makeCall() {
/*come code here*/
}
constructor.prototype.init=function () {
makeCall.apply(this, arguments);
};
return constructor;
})();
var a=new Caller();
a.init();
and be sure that Caller is loaded before it is invoked.
To answer your question and to enhance your code you should have a look at this:
function Caller(){};
This is your constructor.
You can then add methods to your constructor like this:
Caller.prototype.init = function () {
// do something
}
When you create instances of your "class" Caller
var a = new Caller();
every instance will have the methods you described in the Caller.prototype object.
And to solve your problem:
If one prototype method wants to call another prototype method, you need to do this:
// do some setup in your constructor
function Caller(phoneNumber) {
this.phoneNumber = phoneNumber;
};
Caller.prototype.init = function () {
// the code word "this" will be the instance itself
this.makeCall();
};
Caller.prototype.makeCall = function () {
// do whatever this method needs to do
alert('calling ' + this.phoneNumber);
};
var a = new Caller(0123456789);
a.init();
The main thing to understand is, that in every prototype method, the keyword this references the current instance of the constructor (in this case Caller).
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.