This context inside class - javascript

Consider following class and calling foo method: (jsFiddle: http://jsfiddle.net/dpvbd9LL/2/)
function MyClass() {
this.Prop = "Hello";
}
MyClass.prototype.foo = function () {
var self = this;
$("<p>" + self.Prop + "</p>").appendTo("#result");
}
function sampleMethod(callback) {
// do magic stuff
callback();
}
function executeMethod() {
var myClass = new MyClass();
sampleMethod(function() {
myClass.foo();
});
sampleMethod(myClass.foo);
}
(function() {
executeMethod();
})();
The output:
Hello
undefined
As you can see, there is a big difference between calls:
sampleMethod(function() {
myClass.foo();
});
sampleMethod(myClass.foo);
Why that happens? How to write a class to prevent such a situation?

This is caused by the fact that sampleMethod(myClass.foo); pass function of myClass but not in class context, if you test the current object this, you will see that is window, the global one.
If you declare sampleMethod in the object context, you could do something like that.

You can solve this by passing the parent scope to the anonymous function executioner, like this:
function sampleMethod(callback, scope) {
if(scope) scope[callback]();
else callback();
}
function executeMethod() {
var myClass = new MyClass();
sampleMethod(function() {
myClass.foo();
});
sampleMethod("foo", myClass);
}
The problem is that you are trying to pass a function of a -for the sampleMethod- unexisting scope. So if you pass both you can call them as you need to.

Related

Calling a JavaScript function from a nested objects function fails

On the simple example below and on JSFiddle here - https://jsfiddle.net/jasondavis/dnLzytju/ you can see the issue I have.
I can see why it could happen but I am not sure how to fix it while keeping the same JS structure.
The issue is when I define a JavaScript objects prototype functions and I have a 2nd level nested object which has a function and in that function I call a function on the parent/root level it fails.
This function from the code below this.nestedObject.nested_object_function() tries to call the function this.normal_function() however it fails and says:
Uncaught TypeError: this.normal_function is not a function
at Object.nested_object_function (VM2493:79)
I assume the reason is that this is referencing this.nestedObject instead of the parent object.
If that is the case, then how can I call that function like I am trying to do from the nested object function and call a parent function?
I have also tried calling JsLibTest.normal_function() as a test from the this.nestedObject.nested_object_function() function but I get the same error.
var JsLibTest = (function (document) {
"use strict";
var JsLibTest = function (){
// run init() function on initiation of a new JsLibTest object
this.init();
};
/**
* JsLibTest prototype functions
*/
JsLibTest.prototype = {
init: function() {
// as expected this function runs fine
this.normal_function();
// nested level objects functions run fune from parent level object function
this.nestedObject.nested_object_function();
},
normal_function: function() {
console.log('this.normal_function() ran');
},
nestedObject: {
// calling a function on the parent object fails here when called from this nested object function
nested_object_function: function() {
this.normal_function();
console.log('this.nestedObject.nested_object_function() ran');
},
}
};
return JsLibTest;
})(document);
// run it
$(document).ready(function(){
var Sidebar2 = new JsLibTest();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Your assessment is correct. this will be set to the nested object instead of the parent object and that's why it says the function is undefined.
What you need is a way of referencing the parent. Objects don't normally carry any information needed to reference an object which references them. This makes sense when you consider the fact that many objects can reference the same object internally.
You can either store a reference to the parent object and reference that in the nested function:
var nested = {
g() {
this.parent.f();
}
};
var parent = {
f() {
console.log('called');
}
};
nested.parent = parent;
nested.g();
or you can use Function.prototype.call (or something similar) to set the correct context.
var obj = {
f() {
console.log('called');
},
g() {
this.nested.nested_f.call(this);
},
nested: {
nested_f() {
this.f();
}
}
};
obj.g();
Putting the last solution in to the context of your problem:
var JsLibTest = (function(document) {
"use strict";
var JsLibTest = function() {
this.init();
};
JsLibTest.prototype = {
init: function() {
this.normal_function();
// NOTICE: Using .call here to set the context
this.nestedObject.nested_object_function.call(this);
},
normal_function: function() {
console.log('this.normal_function() ran');
},
nestedObject: {
nested_object_function: function() {
this.normal_function();
console.log('this.nestedObject.nested_object_function() ran');
}
}
};
return JsLibTest;
})(document);
// run it
$(document).ready(function() {
var Sidebar2 = new JsLibTest();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You are correct that scope doesn't have access to the parent. Easy solution would be that you pass parent to the nested object like:
this.nestedObject.nested_object_function(this);
then in your nested function call parent as:
nested_object_function: function(self) {
self.normal_function();
alert('this.nestedObject.nested_object_function() ran');
}
since you pass this (parent) as self you can then call it from nested one.
At first, the Object must be unique for Each, having a prototype:
this.nestedObject=Object.create(this.nestedObject);
var JsLibTest = function (){
// run init() function on initiation of a new JsLibTest object
this.init();
//bind to childs:
this.nestedObject.parent=this;
};
Now you can use this.parent inside of your inner function...
this.parent.normal_function();
If you want this to be the parent, bind:
var JsLibTest = function (){
// run init() function on initiation of a new JsLibTest object
this.init();
//bind to childs:
for(i in this.nestedObject){
var el=this.nestedObject[i];
if(typeof el==="function"){
this.nestedObject[i]=el.bind(this);
}
}
};
To make it easier, may use sth like that ( a helper function):
getfunc:function(...a){
a.reduce((obj,key)=>obj[key],this).bind(this);
}
Use like this:
JsLibTestInstance("nestedObject","nestedobject_function")();
Yea, you are right that the this value in your JSLibTest.prototype.nestedObject function is pointing to nestedObject and not JSLibTest.
If you want to maintain the same call signature, you can declare nestedObject as an IIFE:
nestedObject: (function() {
var that = this;
return {
nested_object_function: function() {
console.log(that);
// this.normal_function();
alert('this.nestedObject.nested_object_function() ran');
}
}
}())
https://jsfiddle.net/dnLzytju/1/
Note: You probably do not want to declare your prototype that way is it effectively deletes all the native prototype methods of the object.
To author your code in a similar way, consider using Object.assign to help you out.
var foo = Object.assign({}, Function.prototype, {
bar() {
console.log("Hello!")
}
});
foo.bar();

How to keep 'this' in a class from within a javascript closure

I have read this answer and IIFE but I can't seem to find the correct solution to my problem.
I have a simple class here:
define(['jquery'], function($) {
// Need 'self' because someCallback() is being called with .call() and 'this' changes
var self;
function Foo(number) {
self = this;
this.someNumber = number;
}
Foo.prototype = {
someCallback: function () {
//Use self because 'this' changes to a DOM element
var num = self.someNumber;
//Do something with the num
return num * 2;
}
};
return Foo;
});
and someCallBack() is being called by a jQuery plugin using .call(). Because of this, the context changed, hence the use of the self variable.
However, this is wrong because:
define(['foo'], function(Foo) {
describe('context question', function () {
var foo1 = new Foo(1);
var foo2 = new Foo(2);
it('"this" should work', function () {
var call1 = foo1.someCallback.call(this); // 4
var call2 = foo2.someCallback.call(this); // 4
expect(call2).toBe(4); // Only works because it is 'new' last
expect(call1).toBe(2); // Fails because 'self' is taken from foo2
});
});
});
How exactly should I wrap the self variable to make this code work?
You could probably just use the revealing module pattern and declare it as a "global" variable (local to the module):
define(['jquery'], function($) {
var someNumber;
function Foo(number) {
someNumber = number;
}
Foo.prototype = {
someCallback: function () {
return someNumber * 2;
}
};
return Foo;
});
Two ways of calling an object method which stores its own this value include
Define the method as a nested function which references its this value in a closure which stores this value in a variable. The function defined could be anonymous or declared with a name but must be evaluated each time a class instance is created, so as to create a new Function object capturing different values of self in function scope.
Take a statically defined function object and bind its this value using bind. Bind creates a new wrapper function object each time it is called.
The first method looks like (without Jquery or Jasmine):
function Foo(number)
{ var self = this;
this.num = number;
this.someCallback = function() // method with new Foo object stored as self in function scope
{ // something with num:
return self.num * 2;
}
}
and the second method could look like
function Foo(number)
{ this.num = number
this.someCallback = this.someCallback.bind(this); // bind prototypical method as local method.
}
Foo.prototype = {
someCallback: function () {
// this value is bound by constructor;
//Do something with the num
return this.num * 2;
}
};

JavaScript Function Hoisting in my Object

I have created an object and am attaching a bunch of functions to the object. I am concerned about how the ordering of the functions effects when I can call my functions. In my example below, I must define my functions first before I can use them. My problem with this is that I cannot call init() immediately until I have defined it. Init() will contain a bunch of other functions that it will need to call, which will have to be placed above init(). So in the end, init() will have to be the very last function defined in my object. I believe this is related to Hoisting.
My question is if there is a way for me to call a function before defining it? Is there some sort of way to create a 'placeholder' function like in C?
https://jsfiddle.net/13hdbysh/1/
(function() {
foo = window.foo || {};
//this will not error
foo.helloWorld = function() {
console.log('helloWorld()');
};
foo.helloWorld();
//this will error
foo.init();
foo.init = function() {
console.log('init()');
};
})();
What you're asking deals with how objects store member data. This can be seen in a weird light because of prototypal inheritance. Javascript by default will parse naked functions before they execute.
Example:
(function() {
init();
function init()
{
console.log("Init");
}
)};
This gets muddied when storing behavior as a member to an object. Because prototypal inheritances dynamic functionality you need to declare your members before accessing them. This is Javascript's main difference from traditional OOP languages.
You mentioned, "is there a way to create a 'placeholder' function like in C." You can, but not in the same way. You can assign it to a naked function and assign that to your object. Look in my example, the hello function.
Alternatively you can store the behavior on the prototype of your object and override it when necessary.
Example:
function hello()
{
console.log("Hello my name is "+this.name);
}
(function() {
var something = function(name) {
this.name = name;
};
something.prototype.initTwo = function() {
console.log("My Name is: "+this.name);
};
var thingOne = new something("Thing One");
thingOne.init = "SomeThing";
var thingTwo = new something("Thing Two");
thingTwo.init = function() {
console.log(this.name);
};
thingTwo.initTwo = function() {
console.log("SomethingTwo is Named: "+this.name);
};
thingTwo.hello = hello;
console.log(thingOne.init);
thingTwo.init();
thingOne.initTwo();
thingTwo.initTwo();
thingTwo.hello();
}) ();
Demo: Fiddle
Documentation on objects in javascript.
Try using similar IIFE pattern
(function() {
foo = window.foo || {};
//this will not error
foo.helloWorld = function() {
console.log('helloWorld()');
};
foo.helloWorld();
//this will error
// foo.init();
foo.init = (function _foo() {
console.log('init()');
this.init = _foo;
return this.init
}).call(foo);
foo.init()
})();
jsfiddle https://jsfiddle.net/13hdbysh/2/
I am not sure why would you wanna call it before it is defined but here is how to do it:
foo = window.foo || { init: function() { } };
How about declaring it as a local variable first.
(function() {
foo = window.foo || {};
//this will not error
foo.helloWorld = function() {
console.log('helloWorld()');
};
foo.helloWorld();
var initFunction = function() {
console.log('init()');
};
//this will no longer error
initFunction();
foo.init = initFunction;
})();
Init() will contain a bunch of other functions that it will need to call, which will have to be placed above init().
You are operating under a misapprehension.
A function must be defined before you call it, not before you define another function which will call it later.
Just define all your functions and then start calling them.
(function() {
foo = window.foo || {};
foo.helloWorld = function() {
console.log('helloWorld()');
};
foo.init = function() {
console.log('init()');
};
foo.init();
foo.helloWorld();
})();
As far as hoisting is concerned, function declarations (you only have function expressions) are hoisted, but they create locally scoped variables, not object properties. You would have to assign them to object properties before you could call them as such, and that assignment wouldn't be hoisted.
It's throwing an error because you're calling the method init() before it's declared.
This way will works
foo.init = function() {
console.log('init()');
};
foo.init();
Since foo is an object, you can put those functions into an object so that will be assigned to foo once window.foo is null
(function() {
foo = window.foo || {
helloWorld: function() {
console.log('helloWorld()');
},
init: function() {
console.log('init()');
}
};
//this will not error
foo.helloWorld();
foo.init()
})();

How can I access "this" from within a JavaScript module?

I'm trying to understand how to best use the JavaScript module pattern. My problem is that it seems there's no way to refer to the module from within itself, because this is set to the Window object.
I've got this simple test code:
var Test = function() {
var that = this;
return {
something: function() {
console.info(that);
}
}
}
var test1 = Test();
test1.something();
var test2 = Test();
test2.something();
Both test1 and test2 print a reference to the Window object instead of the module itself.
Any idea how to change it so that I have a valid this inside the module?
If you did
var test1 = new Test()
You could then do
test1.something();
An alternative module structure would be to do something like this:
var myModule = function () {
var obj = {};
obj.something = function () {
return console.log(obj);
};
obj.something2 = function () {
return console.log(obj === this); // true
};
return obj;
};
var test = myModule();
test.something();
test.something2();
Hope this helps
I think you're confusing the JavaScript module pattern with JavaScript constructor functions.
JavaScript constructor functions
If you write a function and call it with the new keyword in front of it, then that function is called as a constructor function.
It will automatically return a new object, that you can refer to within the constructor function using the this keyword.
var Test = function() {
var that = this;
this.something = function () {
console.info(that);
console.info(this);
};
}
var test1 = new Test();
test1.something();
You can return your own object instead, but you wouldn't normally do that in a constructor, you'd just use this instead:
var Test = function() {
var that = this;
return {
something: function () {
console.info(that);
console.info(this);
}
};
}
var test1 = new Test();
test1.something();
If you don't call it with the new keyword in front of it, then it's called like a regular function, meaning any references to this inside of it refer to the object of which the function is a property (which, in the absence of anything else, will be the global object, which in web browsers is window).
var geoff = {
Test: function () {
var that = this;
return {
something: function () {
console.info(that);
}
};
}
};
var test2 = geoff.Test();
var test3 = Test();
Note: with constructor functions, you'd normally define methods on their prototype object, so that the methods don't get unnecessarily redefined for each object you create using the constructor function:
var Test = function() {
this.else = "Something Else"
}
Test.prototype.something = function () {
console.info(this);
}
Test.prototype.somethingElse = function () {
console.info(this.else);
}
var test4 = new Test();
test1.somethingElse() // Logs "Something Else"
(Note that if you return your own object from the constructor function as we mentioned above, then you won't be able to access methods on the prototype object any more.)
Also note that each time you call a constructor function, it returns a new object. You can pass parameters into a constructor function (just like you can with any other function) and use them to customise the object returned:
var Test = function(else) {
this.else = else;
}
Test.prototype.somethingElse = function () {
console.info(this.else);
}
var test1 = new Test("Something else");
var test2 = new Test("Something else again");
test1.somethingElse(); // Logs "Something else"
test2.somethingElse(); // Logs "Something else again"
The problem you have is because this refers to an object, but Test() isn't an object; it's just a function. The object that owns Test() is the Window object (because Test is in the global scope), so therefore that's what you get back when you reference this from within Test().
You may want to try something like this:
var testObj = {
Test : function() {
var that = this;
return {
something: function() {
console.info(that);
}
}
}
}
Now you can call testObj.Test(); and you'll get a reference back to the testObj object.
Hope that clarifies things a bit.

Define an anonymous function as part of an object definition?

Suppose I have:
var myfunc = function() {
// do stuff
}
myfunc.foo = function() {
//do other stuff
};
Now myfunc has a property foo that is a function, great. Is there a way to create myfunc from the get-go in this state? That is, I want foo to be defined when myfunc is created. The syntax, I would imagine, is something like:
var myfunc = {
:function() {
// do stuff
},
foo: function() {
// do other stuff
}
}
Except that's wrong.
You can place an anonymous function inside an object, however the only plausible way of doing this is to call the anonymous function when the object is initialised, otherwise the function will never be able to be called - it's anonymous!
Here's a JSFiddle: http://jsfiddle.net/g105b/99K5F/
var myfunc = function() {
this.foo = function() {
console.log("myfunc.foo called!");
};
(function() {
console.log("Anonymous function called.");
})();
};
// Initialising "myfunc" will call the anonymous function.
var instance = new myfunc();
// Now the foo method can be called.
instance.foo();
A little confused as to what functionality you are looking to gain here...
If you want some code to execute when the myfunc is defined, you could use the module pattern:
var myfunc = (function() {
var self = {};
// any initialization code can go here
alert("myfunc init code");
self.somePublicMethod = function () {
}
return self;
}());
This can also be called an immediate function, a function that is defined and executed at the same time.
From within the closure, and code that is not defined as part of another function will be executed when the object is defined, so when you do something like this:
myfunc.somePublicMethod()
the alert would have already been fired.
(This answer written before the first half of the question was significantly revised)
Now myfunc has a property foo that is a function
No, it doesn't.
You called it with myfunc() so this is a reference to the global window object, thus you are creating window.foo.
Possibly what you are looking for is:
function myfunc () {
// do stuff when myfunc is called
}
myfunc.foo = function () {
// do stuff when myfunc.foo is called
};
or perhaps:
function myfunc () {
// do stuff when myfunc is instantiated
this.foo = function () {
// Add a foo property to this when myfunc is instantiated
// ... which is only worth while if you are doing pretty
// ... odd stuff with the variables that are passed in
}
}
var instance = new myfunc();
or maybe:
function myfunc () {
// do stuff when myfunc is instantiated
}
myfunc.prototype.foo = function () {
// Have a foo function on every instance of myfunc
}
var instance = new myfunc();
… but you've abstracted the problem you are trying to solve away in your question, so it is hard to tell what you are actually trying to achieve.
You can use jQuery:
var myFunc = jQuery.extend(
function() { ... },
{
bar: "wioll haven be",
foo: function() { alert(myFunc.bar); }
}
);
myFunc();
myFunc.foo();
This is mostly code acrobatics, this is probably the closest you'll get:
var myfunc;
(myfunc = function(){}).foo = function(){};
There is no practical difference in declaring methods later though, since javascript is single-threaded.

Categories