Scope Regarding "use strict" and $this - javascript

I have the following code
$(document).ready(function($) {
"use strict";
generalClass = function() {
this.ip = false; // page page
this.ie = false; // IE
this.pp = false; // portfolio page
this.om = false; // organize menu
this.wc = false; // shop
this.sh = false; // side header
// So we can get the correct $this if needed
$this = this;
this.__init();
}
generalClass.prototype = {
__init: function( $check ) {
// Run functions
$this.__format();
$this.__resize();
}
}
});
But I am getting that $this is undefined. I have tried writing it as var $this but still it is undefined for $this.__format();. I realise that I can use this instead of $this, but there are instances later in the code where I can't. How do I make it so that $this is defined in the __init() function? So that it refers to generalClass.
Thanks!

First of all, if you want to use strict-mode, then you have to put var (or let, or const) before your generalClass and $this.
Like that:
(function () {
"use strict";
var generalClass = function() {
this.ip = false; // page page
this.ie = false; // IE
this.pp = false; // portfolio page
this.om = false; // organize menu
this.wc = false; // shop
this.sh = false; // side header
// So we can get the correct $this if needed
var $this = this;
this.__init();
};
generalClass.prototype = {
__init: function( $check ) {
console.log('$this', $this);
// Run functions
$this.__format();
$this.__resize();
}
}
var instance = new generalClass();
}());
(I changed $(document).ready() to IIFE so I could run it in console. Plus, I created instance of your class.)
What happens now? $this inside __init() is not defined. You would have to define $this inside __init() but here is the problem: what should be assigned to it?
In your example of __init() you could actually call this instead of $this but as you already pointed it’s not always possible.
But let me illustrate it with more abstract example:
(function () {
var GeneralClass = function () {
this.foo = [1, 2, 3];
this.bar = 4;
this.baz = [5];
};
GeneralClass.prototype.someFunction = function () {
console.log('foo', this.foo); // [1, 2, 3]
console.log('bar', this.bar);
var self = this; // storing reference to *this* (oobject for later)
this.baz.forEach(function (item) {
console.log('baz?', this.baz); // undefined, because *this* means here this.baz, and there is no this.baz.baz
console.log('baz!', self.baz); // [5]
});
console.log('foo * bar');
this.foo.forEach(function (item, index) {
console.log('item', index, 'value', item * self.bar);
});
console.log('Both accesible here', this.bar, self.bar);
};
var generalClassInstance = new GeneralClass();
generalClassInstance.someFunction();
}());
Here I assign this to self (personally, I’d use $this for $(this) but it’s only a convention so do as you please, as long as you are consistent). Now functions called inside my function can use self which works as a reference to outer this. And if I called another function in my sub-function, it would still point to GeneralClass’s this.
I hope this is what you were primarily interested in.

Related

How to call javascript class function from closure

I'm working on a jQuery plugin that does not have a selector. When initializing it, I instanciate an object that has functions. In these functions, I need to use closures. In these closures, I would like to call my initial object functions.
To make it more clear, here is a simplified version of the code.
HTML
<script src="/path/to/jquery/2.1.1/jquery.min.js"></script>
<script src="/path/to/my/script/myeditor.js"></script>
<div class="editable">Div1</div>
<div class="editable">Div2</div>
<script>
$.myeditor({
option1: 'a',
option2: 'b'
});
</script>
myeditor.js
function ($) {
var MyEditor = function (options)
{
this.$options = $.extend(true, {}, $.myeditor.defaults, options);
this.init();
};
$.myeditor = function (options = {})
{
return new MyEditor(options);
};
$.flyeditor.defaults = {
option1: '1',
option2: '2'
};
MyEditor.prototype.notify = function(message = '')
{
console.log(message);
};
MyEditor.prototype.init = function()
{
// Do stuff
$('.editables').each(function() {
$this = $(this);
// Here comes the error
notify($this.html());
});
};
}(jQuery);
The problem is that notify(this.html()); raises an error ReferenceError: notify is not defined
How can I reach this notify method?
You can assign this to a separate local variable in a closure. You need to do that because this will no longer point to your MyEditor object inside the each, it will point to each of the .editables
Also, you probably meant to call this.notify(), since the function is attached to the prototype of MyEditor
MyEditor.prototype.init = function()
{
// Do stuff
var that = this; // <-- now we can reach this inside function.
$('.editables').each(function() {
$this = $(this);
// Here comes the error
// You can't use notify, that function is not defined
// You can't use this.notify because this points to something else (the node)
// inside the function in each
that.notify($this.html());
});
};
MyEditor.prototype.init = function()
{
// Do stuff
var self = this;
$('.editables').each(function() {
$this = $(this);
// Here comes the error
self.notify($this.html());
});
};

Simple JavaScript OOP Class

I'm using John Resig's simple OOP Class that is adapted to use "use strict" and taken from SO post.
In all examples I see the usage of Class.extend like so:
var MyObj = Class.extend({
init:function(){},
prop: "Property"
});
But I found a large disadvantage for me of using it in such way - I cannot have "private" variables, so I cannot store reference to this like var $this = this;.
I found the solution for my case, and now I using the Class.extend in following way:
var MyObj = Class.extend(new function(){
var $this = this;
this.init = function(){};
this.prop = "Property";
});
Everything works in my case, but I want to know if there some things that can cause me problems in a long run?
Does this way my application will consume much more memory in browser?
What alternative ways I have to implement my needs?
Note: I need to store $this, because I use heavily events and callbacks, so I want to refer "original" this easy to have access to all methods and properties on object.
EDIT: As requested, this is my code example:
(function () {
"use strict";
window.QuickPlay = Class.extend(new function () {
var $this = this;
this.init = function (initData) {
$this.elementsToHide.push(initData.el);
$(function () {
playProcessStart();
Sys.Application.add_load(function () {
$find("ctl00_ContentPlaceHolderMain_ctrlPlayPopup1").add_closed(function () { $this.setElementsVisibility(""); });
});
$this.setElementsVisibility("hidden");
});
};
this.elementsToHide = [];
this.setElementsVisibility = function (visibility) {
$.each($this.elementsToHide, function (i) {
$("#" + this).css("visibility", visibility);
});
};
});
} ());
You can use module pattern and maintain all the OOP. These kind of pattern gives your code more security and better organization.
//these are namespaces in javascript
window.project = window.project || {}; //this kind declarations prevents recreate the object
project.group = project.group || {};
//in the line below we can use $ instead jQuery, and use window and document instead ask for the browser every time.
(function (window, document, $) {
"use strict";
project.group.NameOfYourModule = function () {
var privateAttribute = true,
students = 32, //It's is a best practice declare everything in an unique var.
privateMethod = function () {
alert('Now I know OOP using jQuery');
};
return {
init: function () {
//this is a public method and we can initiate some private method;
privateMethod();
//we call a public method using this
this.publicMethod();
},
publicMethod: function () {
//this is a public method
}
};
};
$(function () {
var myclass = new project.group.NameOfYourModule(); //instantiate you class
myclass.init(); //initiate some public method
});
}(window, document, jQuery));
Working example at JsFiddle
How to work with Inheritance and Module Pattern here
I cannot have "private" variables
Of course you can. Either in the (currently unnecessary) (function () { … } ()); wrapper, or in your constructor (the init thing).
new function () {
Avoid that pattern! If you really need your code to work as it does now, use
(function () {
"use strict";
// Here's the place where you could put a private, static variable
// for example `var elementsToHide = [];`
var $this = {
init: function (initData) {
$this.elementsToHide.push(initData.el);
$(function () {
playProcessStart();
Sys.Application.add_load(function () {
$find("ctl00_ContentPlaceHolderMain_ctrlPlayPopup1").add_closed(function () {
$this.setElementsVisibility("");
});
});
$this.setElementsVisibility("hidden");
});
},
elementsToHide: [],
setElementsVisibility: function (visibility) {
$.each($this.elementsToHide, function (i) {
$("#" + this).css("visibility", visibility);
});
}
};
window.QuickPlay = Class.extend($this);
}());
I want to know if there are some things that can cause me problems
Yes. Multiple instances will hardly work, as they all do reference the same elementsToHide array. And you're not using any instance methods at (only a constructor and static elements on your class), so the class pattern seems quite unnecessary. Use a module instead. If you need single instances (and classes), the code should look like this:
"use strict";
window.QuickPlay = Class.extend({
init: function (initData) {
var $this = this;
this.elementsToHide = [];
$(function () {
playProcessStart();
$this.elementsToHide.push(document.getElementById(initData.el));
Sys.Application.add_load(function () {
$find("ctl00_ContentPlaceHolderMain_ctrlPlayPopup1").add_closed(function () {
$this.setElementsVisibility("");
});
});
$this.setElementsVisibility("hidden");
});
},
setElementsVisibility: function (visibility) {
$(this.elementsToHide).css("visibility", visibility);
}
});

avoid needing to declare 'var me = this' for javascript prototype functions

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.

Private functions in JavaScript

In a jQuery-based web application I have various script where multiple files might be included and I'm only using one of them at a time (I know not including all of them would be better, but I'm just responsible for the JS so that's not my decision). So I'm wrapping each file in an initModule() function which registers various events and does some initialization etc.
Now I'm curious if there are any differences between the following two ways of defining functions not cluttering the global namespace:
function initStuff(someArg) {
var someVar = 123;
var anotherVar = 456;
var somePrivateFunc = function() {
/* ... */
}
var anotherPrivateFunc = function() {
/* ... */
}
/* do some stuff here */
}
and
function initStuff(someArg) {
var someVar = 123;
var anotherVar = 456;
function somePrivateFunc() {
/* ... */
}
function anotherPrivateFunc() {
/* ... */
}
/* do some stuff here */
}
The major difference between these two approaches resides in the fact WHEN the function becomes available. In the first case the function becomes available after the declaration but in the second case it's available throughout the scope (it's called hoisting).
function init(){
typeof privateFunc == "undefined";
var privateFunc = function(){}
typeof privateFunc == "function";
}
function init(){
typeof privateFunc == "function";
function privateFunc(){}
typeof privateFunc == "function";
}
other than that - they're basically the same.
this is a model that helped me to manage modules in javascript:
base.js:
var mod = {};
mod.functions = (function(){
var self = this;
self.helper1 = function() {
} ;
self.helper2 = function() {
} ;
return self;
}).call({});
module_one.js
mod.module_one = (function(){
var
//These variables keep the environment if you need to call another function
self = this, //public (return)
priv = {}; //private function
priv.funA = function(){
}
self.somePrivateFunc = function(){
priv.funA();
};
self.anotherPrivateFunc = function(){
};
// ini module
self.ini = function(){
self.somePrivateFunc();
self.anotherPrivateFunc();
};
// ini/end DOM
$(function() {
});
return self; // this is only if you need to call the module from the outside
// exmple: mod.module_one.somePrivateFunc(), or mod.module_one.ini()
}).call({});

In jQuery, how do you resolve the scope of "this", when you are in the scope of each()?

I've created an Object, and have a method setup() in the object.
this.debug = function (){...}
this.setup = function(){
var fieldsets = form.children("fieldset");
fieldsets.each(function(){
this.debug($(this).attr("class")));
});
}
I'm trying to call this.debug which is in the scope of the Object but not in the scope of each, since THIS is a different this...
How do I access this.debug?
Say var that = this after this.debug, then do that.debug.
This is basically Skilldrik's answer, but showing you where it works best
this.setup = function(){
// save it in a scoped var... some people use "self" for this purpose, i prefer
// naming it whatever the outer object actually is...
var containingObject = this;
var fieldsets = form.children("fieldset");
fieldsets.each(function(){
// use that scoped var later!
containingObject.debug($(this).attr("class")));
});
}
In jQuery 1.4 you can do:
this.debug = function (){...}
this.setup = function(){
var fieldsets = form.children("fieldset");
fieldsets.each(jQuery.proxy(function(){
this.debug($(this).attr("class")));
},this);
}
The jQuery.proxy(function, object) function will take 2 arguments:
The function will be the function used in the loop.
The object argument will be the this object inside the function.
In this way, you can transfer the this from the outer scope inside the each function.
I tried this in my Greasemonkey-Console:
this.debug = function() {
console.log("foo");
};
this.setup = function() {
var fieldsets = form.children("fieldset");
fieldsets.each(function(){
debug($(this).attr("class"));
});
};
Which will search the scope for any debug .. which that is hopefully the function above. This will fail, if you assign a variable with the very same name :)
I normally do it this way: (note:example below is from memory but looks sound):
function CustomObject()
{
var _instance = this;
this.debug = function(){...};
this.setup =
function()
{
var fieldsets = form.children("fieldset");
fieldsets.each(
function()
{
_instance.debug($(this).attr("class"));
});
};
}
this.debug = function (){...}
this.setup = function(){
var that = this;
var fieldsets = form.children("fieldset");
fieldsets.each(function(){
that.debug($(this).attr("class")));
});
}

Categories

Resources