"this" in a nested JavaScript function - javascript

function TheTable(calculateButtonId)
{
this.errorsBlock = document.getElementById("Some Value");
var addError = function(text) {
this.errorsBlock.innerText += text + "\n";
}
var customHandler = function(desc, page, line, chr) {
addError("[" + line + "] " + desc);
return true;
}
this.init = function() {
window.onerror = customHandler;
......................
}
this.init();
}
var table = new TheTable("BtnId");
as soon as I changed this.addError to var addError it started to thow an error:
Cannot set property 'innerText' of undefined
Could you clarify, what am I referencing by this inside of var-function?

A couple things:
Whenever you invoke a function using the new keyword -- e.g. new TheTable("BtnId") -- you create a new instance of that type. The value of this inside that constructor refers to this new instance.
You can add arbitrary properties to this new instance.
In general, when you invoke a function that has been assigned to this new object, then the value of this inside the function is that same instance.
The var keyword creates a variable scoped to its nearest enclosing function. In general, when you invoke functions of this type (i.e. functions created as global or local variables), the value of this inside them is window.
So when you do this:
this.errorsBlock = document.getElementById("Some Value");
this.addError = function(text) {
this.errorsBlock.innerText += text + "\n";
}
this.addError("whatever");
...then the value of this inside addError refers to the same object that errorsBlock was assigned to and the call succeeds. However, when you change it to this:
var addError = function(text) {
this.errorsBlock.innerText += text + "\n";
}
addError("whatever");
...then the value of this inside addError is window and window probably doesn't have any function property named errorsBlock and the call fails.

function TheTable(calculateButtonId) {
this.errorsBlock = document.getElementById("Some Value");
var self = this;
var addError = function(text) {
self.errorsBlock.innerText += text + "\n";
}
}
this in javascript is always context under which function is running.

Related

Block access to certain variables within function

How can I make a function which was originally defined elsewhere in my code be denied or limited access to certain objects when it gets assigned to an object?
Short Example
var func = function() {
console.log(window.screenX); // <-- this should work
document.createElement("div"); // <-- this should fail (ideally in
// some graceful way)
};
var makeObj = function(pFunc) {
var window = {
"screenX": window.screenX
};
var document = null;
var rObj = {};
rObj.func = pFunc;
return rObj;
};
var obj = makeObj(func);
obj.func(); // <-- want it to have no access to document and
// limited access to window
func is defined by privileged users as a sort of plugin but I want to limit what they can do such as not creating new elements but still be able to access certain variables and functions they will need (such as window.screenX)
I'm sure that there are tons of possible answers, my answer make use of the Function-constructor so that window and document are defined locally within the scope of an also locally defined function. This is very important because you do not want to overwrite both objects globally.
But lets come to this answer. Within makeObj all allowed objects in func are defined with the first and second param of a new Function-Constructor.
The third param is used to create an "anonymous" function that contains certain assignments to those allowed objects. These assignments were created locally by using serializeFakeObject.
In other words, the first and second param with new Function are going to be the params (header or signature) while the third param is going to be the body of the „anonymous“ function.
Please note that this "anonymous" function is created dynamically and looks like this afterwards:
function anonymous(window,document) {
// overwrites allowed objects
window = {"screenX":-8};
document = {};
document.createElement = function (){ throw Error("document.createElement is not allowed"); };
// this function must take assignments above
// no chance to use the globally defined objects window or document
return function (){
console.log(window);
console.log(document.createElement());
}();
}
This anonymous function is dynamically created within makeObj and after that executed by calling obj.func() at the end.
Lets put all together:
var func = function (){
console.log(window); // has just got screenX
console.log(document.createElement()); // throws an error "document.createElement is not allowed"
};
var makeObj = function(pFunc) {
var serializeFakeObject = function(name,obj){
var serializedObject = "";
// when its a property of type function then omit JSON.stringify otherwise it gets undefined
if(obj.__proto__.constructor === Function)serializedObject = obj;
else serializedObject = JSON.stringify(obj);
return name + ' = ' + serializedObject + ';';
};
var fakeWindow = serializeFakeObject("window", { "screenX": window.screenX });
var fakeDocument = serializeFakeObject("document", {});
// create property createElement of document that should fail in a graceful way
var fakeDocumentProperty = serializeFakeObject("document.createElement", function (){ throw Error("document.createElement is not allowed"); });
// if you like you can add more properties here....
var concatFakeObjects = fakeWindow+fakeDocument+fakeDocumentProperty;
var rObj = {};
// the line below creates the "anonymous" function described above
rObj.func = new Function('window', 'document', concatFakeObjects +'return '+pFunc+'();');
return rObj;
};
var obj = makeObj(func);
console.log(obj.func());

Why is it not possible to call functions directly from an object without function expression?

function Car(make, model) {
this.make = make
this.model = model
console.log(" Iam inside the object" )
this.whatsmymodel = function () {
console.log(" Iam " , this.model)
}
function whatsmymake() {
console.log(" Iam " , this.make)
}
}
function whatsthis() {
console.log (" This is a function")
}
Car.prototype.whoami = function () {
console.log(" Iam " , this.make + " " + this.model)
}
var tesla = new Car("Tesla", "ModelS")
tesla.whoami()
tesla.whatsmymodel()
tesla.whatsmymake() // Error!!
whatsthis()
How come I get error for tesla.whatsmymake()
TypeError: tesla.whatsmymake is not a function
I understand it is possible in the new ES6 class but wouldnt it be easier to define a function within a function constructor ?
It is letting me define but doesnt allow me to call - why is that ?
You have defined a function called whatsmymake inside the lexical scope of the Car constructor, but you have not done anything to create a property call whatsmymake on the this returned by the Car constructor. If every function defined inside the lexical scope of a constructor were attached to the constructed object, this would be very inconvenient, and would disallow the creation of helper methods inside the constructor without also attaching those helper methods to the constructed object. For example:
function Car() {
this.rightWheelAngle = 0;
this.leftWheelAngle = 0;
this.turnLeft = function() { turnBothWheels(1); }
this.turnRight = function() { turnBothWheels(-1); }
function turnBothWheels(inc) {
this.rightWheelAngle += inc;
this.leftWheelAngle += inc;
}
}
Suppose we do not want to expose turnBothWheels as a method on the returned object. JavaScript does not require you to do so, but you can still use the turnBothWheels function within functions that have lexical-scope visibility of that function (such as the turnRight and turnLeft methods, above).
As for why function declarations don't allow a property-access name-path like function foo.bar.baz() { ... }, they just don't -- function names must be plain identifiers. That's how the grammar is defined. If you want to have a function in a property named baz on the object stored in the property bar on the object foo, then simply do an assignment to store it there: foo.bar.baz = function() { ... }

Javascript bind to object

function Developer(skill) {
this.skill = skill;
this.says = function() {
alert(this.skill + ' rocks!');
}
}
var john = new Developer('Ruby');
var func = john.says;
func();
I tried this example when I do this I get the following message undefined rocks! instead of Ruby rocks!.
can u explain why is that.
Function Execution Context and the this Keyword
JavaScript functions have an execution context at invocation time such that the this keyword is bound to the object they are invoked from. If you call john.says() the execution context of the function would have a this keyword that points to john. If you instead assign a global variable func to the method says found on the object john you have changed the execution context to the global object. When you invoke the func function, this dereferences to window (or undefined*) and since window.skill is undefined, says will coerce that value into a string to concatenate it with the string ' rocks!'.
How to guarantee execution context using bind
You can instead bind a copy of the function to an object (effectively locking it's context reference):
var func = john.says.bind(john);
How to guarantee execution context using a closure
Alternately you can close over the relevant bits by using a closure in your constructor:
function Developer(skill){
var _this = this; // we keep a reference here
this.skill = skill;
this.says = function(){
alert(_this.skill + ' rocks!');
// when invoked _this refers to the context at construction
}
return this;
}
How to guarantee a value using a closure
You could just reference the skill value directly from the method and so not need the context at all:
function Developer(skill){
// because skill is defined in this context, says will refer to this context
// to get the value of the skill variable.
this.says = function(){
alert(skill + ' rocks!');
}
}
How to guarantee an execution context at invocation time using call and apply
The final options are to to invoke the method with the context you want at invocation time:
func.call(john /*, optional arguments... */);
func.apply(john /*, optional arguments as an array */);
How to use prototypes to allow the dynamic execution context to set the right this
If we want to reuse a method between object instances or types but have the right execution context when invoked we can use the prototype property.
function Developer(skill){
this.skill = skill;
this.says();
}
Developer.prototype.says = function(){
alert(this.skill + ' rocks!');
}
var john = new Developer("Ruby"); // alert("Ruby rocks!")
var tony = new Developer("JavaScript"); // alert("JavaScript rocks!")
More reading:
Specification for execution contexts: http://ecma-international.org/ecma-262/5.1/#sec-10.3
MDN Docs about this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FOperators%2Fthis
* "use strict" activates a special strict mode representing the future of JavaScript. This special strict executing environment will not resolve to the global object when a context has not been set, instead resolving this to the appropriately scoped value undefined.
Let's illustrate what is happening, by actually returning the object:
function Developer(skill) {
this.skill = skill;
this.says = function() {
alert(this.skill + ' rocks!');
}
return this; //this is basically { skill: skill, says: function() {} }
}
var john = new Developer('Ruby');
var func = john.says; // all it knows is the function() part, has no knowledge of skill
func(); //undefined rocks!
Ok, so why are we getting undefined? Well, once we rip out the function, it no longer has context - it doesn't know what this is. If we were to do:
func.apply(john);
Then we are passing in John as the this parameter. A workaround, it to pass the value in to the function when we create it:
function Developer(skill) {
this.skill = skill;
this.says = function(skill) { //<--calling this function
return function() { //<--returning this function
alert(skill + ' rocks!');
};
}(this.skill); //<--passing in the value here
return this;
}
When you call a function, this will refer to the object on which the function was called.
When you say
var func = john.says;
you are just getting the function object (it doesn't remember the object on which it is defined).
When you invoke the function, like this
func();
there is no current object, on which func is invoked. So, by default, JavaScript assigns the global object(window object in browser) as this. Since, skill is not defined in the global object, undefined is returned.
Note: In Strict mode, this will be undefined, if there is no current object.
That is why we have to explicitly bind the object to the function, like this
func.bind(john)();
Function.prototype.bind will return a new function, which is bound to john. So, when you invoke this this will refer john.
Because func() only has the function and not any other related info.(this.skill)
The context is lost. this referred to the john when the says() is called.
But now, when you call func() this refers to the window. Therefore this.skills will return undefined unless you have a global variable of the same name.
the fastest way to keep the above pattern is to bind this direct to the method this.says = function () { ... }.bind(this);:
function Developer(skill) {
this.skill = skill;
this.says = function () {
alert(this.skill + ' rocks!');
}.bind(this);
}
var john = new Developer('Ruby');
var func = john.says;
func(); // Ruby rocks!
try this
function Developer(skill) {
this.skill = skill;
this.says = function() {
alert(this.skill + ' rocks!');
}
}
var john = new Developer('Ruby');
john.says();
working demo
http://jsfiddle.net/ZENtL/

Access members within function/setting them from outside

I would really like to do this:
var Html = function() {
alert('internal: ' + this.val);
};
Html.val = "x";
alert('external: ' + Html.val);
Html();
but it doesn't work. why? how can I get the function code to see values set after its creation?
- update -
because my original post was not so clear in its intent, and because I've posted a solution that's closer to the intention than that of other posters here, allow me to say that what I wanted was a mechanism for accessing the internal members of a function with the explicit purpose of being able to create a generic function that can be seeded to modify its behaviour later and independently
The this context of a JavaScript function depends on how you call the function. You can pass the context explicitly with call:
var Html = function() {
alert('internal: ' + this.val);
};
Html.call({ val:'x' });
If you meant to use the function as a constructor you can instantiate with new:
function Html(val) {
this.val = val;
}
var html = new Html('x'); // Must be used with `new`
alert('internal: '+ html.val);
You can add methods to the prototype if you want to extend your object:
Html.prototype.getVal = function() {
return 'internal: '+ this.val;
};
var html = new Html('x');
alert(html.getVal());
In Javascript, the "this" is the object associated to a method, not the method itself. The simplest solution would be to change the Html function to a method:
var html = {
val: "x",
draw: function(){
console.log('internal: ' + this.val);
}
}
html.draw();
html.val = "y"
html.draw();
If you want to keep the html function as a direct function you can either craete a wrapper around a method.
var Htmldraw = function(){
return html.draw();
}
html.val = "asd"
Or you can change the function to use lexically scoped variables instead of "this"
var val;
var Html = function() {
console.log('internal: ' + val);
};
val = "x";
alert('external: ' + val);
Html();
thank you both for taking the time to post elaborate answers. you both got thank you points.
I'm going to answer my own question for the sake of some poor sod who may struggle with this in the future... what I wanted to do is best accomplished as shown below (please see my edits in the original posting to clarify what my intention was):
function fn() {
this.val = "x";
this.show = function() {
alert("internal = " + this.val);
};
return this;
};
var x = fn();
alert("initial = " + x.val);
x.val = "y";
alert("reset = " + x.val);
x.show();
from which you can see that a variable internal to my function is accessible from the outside. This allows me to manufacture a function and subsequently load it with a bunch of data for its use, without having to pass parameters during its creation (which is useful because when the function is actually called (as opposed to created), it will receive a different set of parameters)

send parametres as values using JSON syntax

i want to send an url with parametres, those parametres are values taken by a form with javascript and i want to use JSON to do it, but when i debug i see this error : Uncaught ReferenceError: name is not defined..
function recup()
{
var selectElmt = document.getElementById("name");
var selectcat = document.getElementById("msg");
var name = selectElmt.options[selectElmt.selectedIndex].value;
var msg = selectcat.options[selectcat.selectedIndex].value;
}
function go() { // button send who call the function go
var p_url="http://mysite.com/class?name=" + name + "&message=" + msg +
$.getJSON(p_url, {
}).done(function( data ) {
$.each(data, function (key, field) {
alert(field);
});
});
return false;
}
it's a syntax error when calling the value name and msg but i don"t know how to fix it or in the go function
You two errors, closing curly brace and plus character, the code shoud be:
var msg = "hello"; // i just simplified the value
var name = "test";
function go() { // button send who call the function go
var p_url="http://mysite.com/class?name=" + name + "&message=" + msg;
$.getJSON(p_url, {
}).done(function( data ) {
$.each(data, function (key, field) {
alert(field);
});
});
return false;
}
UPDATE: You need to make name and msg global:
var name, msg;
function recup() {
var selectElmt = document.getElementById("name");
var selectcat = document.getElementById("msg");
name = selectElmt.options[selectElmt.selectedIndex].value;
msg = selectcat.options[selectcat.selectedIndex].value;
}
function go() { // button send who call the function go
var p_url="http://mysite.com/class?name=" + name + "&message=" + msg;
$.getJSON(p_url, {
}).done(function( data ) {
$.each(data, function (key, field) {
alert(field);
});
});
return false;
}
and recup need to be executed before go
the two variables are in another function
Well, that explains it. A variable that is local to a function cannot be accessed by another function.
You have to define the variables in a scope that is shared by both functions. This could be the global scope, but you should avoid creating global variables (you cannot have a global variable with name name anyways, because it exists already).
If you want to assign a value to a variable in a higher scope, use name = ...; instead of var name = ...;.
Example:
(function() {
// create a new scope so that we don't pollute the global scope
// this variable can be accessed by both functions
var answer;
function foo() {
// don't use `var` here, otherwise you create a local variable which
// shadows the variable with the same name in a higher scope
answer = 42;
}
function bar() {
alert(answer);
}
foo();
bar();
}());

Categories