So far I am trying with:
var myBoolean = false; // global
function toggleBoolean(vr) {
vr = !vr;
}
alert(myBoolean); // false
toggleBoolean(myBoolean);
alert(myBoolean); // false
But obviously, It failed.
Edit: sorry, I forgot to point out that I want the function to work with many Booleans and not just one
Yes, you can toggle a global boolean from a function. You can't do it as in your attempt because JavaScript is strictly call-by-value.
function toggleBoolean() {
myBoolean = ! myBoolean;
}
Now, if you want to toggle a global by name, you could do this (though it's a little icky):
function toggleBoolean(name) {
window[ name ] = ! window[ name ];
}
Global variables (in JavaScript on a browser) are properties of the global object, which is known as "window". In other contexts there are ways of associating a name with the global context. You'd call that function, therefore, like this:
toggleBoolean( "myBoolean" );
Note that I pass a string there instead of a reference to the actual global variable.
you are passing the value to the function by value. Instead, to achieve what you want, you can do something like:
var myBoolean=false;// Global
function toggleBoolean(){
myBoolean = !myBoolean;
}
You can do
var myState = { state : false };
function toggleBoolean( s )
{
s.state = ! s.state;
}
alert( myState.state );
toggleBoolean( myState );
alert( myState.state );
primitives are passed by value by default in javascript. You can have a wrapper object and pass the object to the toggle function.
Related
The following code (codepen) doesn't work.
"use strict"; //without strict "this" works, but it refers to window object
let person = {
name : "Shimon",
logName : function(){
//console.log("test") //this works
console.log(this.name); //doesn't work
}
};
//person.logName(); //works
(false || person.logName)(); //doesn't work. Uncaught TypeError: Cannot read property 'name' of undefined
I want to understand why.
When I call (false || person.logName)(); I suppose to call person.logName(), and it indeed called.
So why I cannot use this inside this method?
The are 4 rules for the JavaScript engine to determine the this reference.
New Bound: Was the current function called using the new keyword
Explicit Binding: Was the function called using Function#call or Function#apply.
Implicit Binding: Was the function directly on its owning object?
Default: If in strict mode undefined otherwise global
In your case person.logName() falls under the 3rd category so the this reference is set to the value of person.
But in the other case, the line (false || person.logName)() is equivalent to var x = false || person.logName; x();. That is because you are using an expression to get the function and thus the context is lost.
So you either need to use person.logName() or (false || person.logName).call(person)
Here is a more complex example:
var actionMap = {
0: function() {
console.log(this === actionMap ? "actionMap" : "global");
},
1: function() {
console.log(this === actionMap ? "actionMap" : "global");
},
};
function other() {
console.log(this === actionMap ? "actionMap" : "global");
}
actionMap[0](); // works
(actionMap[0])(); // works, brackets get ignored
(actionMap[0] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1]).call(actionMap); // works, context is called explicitly
var boundOther = other.bind(actionMap);
(boundOther || actionMap[1])(); // function is bound
As you can see in this example, the context is lost even though both functions are owned by the same object. Since the function that gets called is derived by an expression and is not immediately called on its owning object.
This is simply how Javascript semantics behave. Unless you call the method from the object it is attached to this cannot be resolved in the function scope.
Your example of:
(false || person.logName)();
Could be rewritten as:
var func = person.logName;
func();
Here you can visually see that there is an intermediate step where logName() is detached from person. This has a side effect of removing the this context.
There are ways to enforce the this context on "detached" functions. One of them being Function.prototype.bind() which enables the binding of this to any function.
(false || person.logName.bind(person))();
Here a new function is created with person bound as this to achieve the desired behavior. Alternatively you could use Function.prototype.call() to avoid creating a new function.
(false || person.logName).call(person);
This works in your contrived use-case, but it may not fit into your real use-case as you will need to pass different objects as the this parameter when the functions you "detach" are not from the same object.
this works on instantiated classes/functions. In your case you have to treat the object as static like:
let person = {
name : "Shimon",
logName : function(){
console.log(person.name);
}
};
If you want this behavior you can either use classes or prototypal inherintance
class Person {
constructor(name) {
this.name = name;
}
logName() {
console.log(this.name);
}
}
or
function person(name) {
this.name = name;
}
person.prototype.logName = function() {
console.log(this.name);
}
let shimon = new Person("Shimon");
shimon.logName() // >> Shimon
I'm trying to convert one of my mid-sized JavaScript APIs from "lots of global variables and functions" into something more akin to the namespace encapsulation as used in jQuery and other well thought-out JavaScript libraries. To effect this, I'm using anonymous functions.
Here's a fragment of the code:
(function(window, undefined) {
var MyLib = (function() {
var AbortProgram = true;
function DisplayAbortProgram() { alert('AbortProgram inside=' + AbortProgram); }
return { AbortProgram: AbortProgram, DisplayAbortProgram: DisplayAbortProgram }
} ())
window.MyLib = MyLib;
})(window);
MyLib.AbortProgram = false;
alert('AbortProgram outside=' + MyLib.AbortProgram);
MyLib.DisplayAbortProgram();
When run, the outside value of AbortProgram is false but the inside value is still true. This post is to confirm the reason why this is happening? I believe it's because that return statement is returning the value of DisplayAbortProgram and not a reference to it. The underlying reason is that JavaScript returns values for primitive types and not references - objects are passed by reference.
I've read up on this and believe there is no way to return a reference to that boolean variable so I'll have to implement a function called SetAbortProgram(value).
AbortProgram is hidden by the closure, you can't change it. That's most often the goal of the pattern you use.
What you do with MyLib.AbortProgram = false; isn't changing the existing hidden variable but adding a new AbortProgram variable.
If you want to make AbortProgram modifiable, simplify your code to
var MyLib = {
AbortProgram: true,
DisplayAbortProgram: function DisplayAbortProgram() { alert('AbortProgram inside=' + this.AbortProgram); }
};
MyLib.AbortProgram = false;
MyLib.DisplayAbortProgram();
Or else add a setter to your existing code :
(function(window, undefined) {
var MyLib = (function() {
var AbortProgram = true;
function DisplayAbortProgram() { alert('AbortProgram inside=' + AbortProgram); }
function setAbortProgram(v) {AbortProgram=v}
return { AbortProgram: AbortProgram, DisplayAbortProgram: DisplayAbortProgram, setAbortProgram:setAbortProgram}
} ())
window.MyLib = MyLib;
})(window);
MyLib.setAbortProgram(false);
alert('AbortProgram outside=' + MyLib.AbortProgram);
MyLib.DisplayAbortProgram();
You are correct that it is a passed-by-value rather than reference. Create a parameters object. That will be passed by reference:
var args = { AbortProgram: true };
Pass args around instead of AbortProgram. You can access your boolean via args.AbortProgram.
The underlying reason is that JavaScript returns values for some primitive types and not references.
Not only some. All primitive types will be passed as values. Only objects are references to their properties.
I've read up on this and believe there is no way to return a reference to that boolean variable so I'll have to implement a function called SetAbortProgram(value).
Yes, if you want to change the variable. Yet, you could just use the property of your object everywhere.
function katana(){
this.isSharp = true;
}
katana();
assert( isSharp === true, "A global object now exists with that name and value." );
var shuriken = {
toss: function(){
this.isSharp = true;
}
};
shuriken.toss();
assert( shuriken.isSharp === true, "When it's an object property, the value is set within the object." );
I am actually not getting what is this code trying to tell?. Can anyone explain me what is Context in JavaScript and what exactly does context represent here in the above code?
Well, the reason this === window in the first example and this === shuriken in the second example all has to do with where those functions are created. Notice that when you define shuirken.toss outside of the object, this points to the window object. And when you call katana with new, this points to the newly created object.
The first call is equivalent to:
katana.call(window); // window is now referenced as this inside the function
In a similar fashion, you could change the call to katana() like this to change the context:
var shuriken = {}
katana.call(shuriken); // shuriken.isSharp === true
The second invocation implicitly has this equivalent function call:
shuriken.toss.call(shuriken);
Since functions are first class objects, it should be possible to assign to the members of them.
Am I right thinking that arguments.callee does this?
Are there any other ways to set these fields?
How is it possible to set field in first case?
function something1() {
arguments.callee.field = 12;
}
alert(something1.field); // will show undefined
something1();
alert(something1.filed); // will show 12
something2 = function() {
arguments.callee.field = 12;
};
alert(something2.field); // will show undefined
something2();
alert(something2.field); // will show 12
UPDATE 1
I mean how to access members from within function when it runs.
You don't need to use arguments.callee to refer to a function that has a name; you can just use the name. This is naturally the case when you declare the function using the
function name(...) { ... }
syntax; but even in a function expression, you're allowed to supply a temporary name:
(function temp_name(...) { ... })(arg);
So, if you want to set the properties from inside the function, you can write:
function something1() {
something1.field = 12;
}
alert(something1.field); // will show undefined
something1();
alert(something1.field); // will show 12
something2 = function something2() { // note the second "something2"
something2.field = 12;
};
alert(something2.field); // will show undefined
something2();
alert(something2.field); // will show 12
Here's how I would do it:
function something1() {
this.field = 12;
}
var test = new something1();
alert(test.field); // alerts "12"
test.field = 42;
alert(test.field); // alerts "42"
If you're going to treat it like a class, you need to create a new instance of it before you can access its fields.
JSFiddle
Am I right thinking that arguments.callee does this?
Yes it did, but now they deprecated it.
Are there any other ways to set these fields?
The official way to replace callee is to use an explicit function name, as in funcName.propertyName=.... This is however not always convenient, for example, with dynamically generated functions. To quote John Resig, we're going to miss arguments.callee, it was quite useful for specific tasks.
Simple
function something1() {};
something1.Property = "Foo";
You can directly assign the properties to any function just like a normal object. If to say in OOP language create static properties and methods.
Edit
The same inside the function
function something1() {
something1.AnotherProp = "Bar";
};
something1.Property = "Foo";
I'm just curious to know how jQuery is able to hijack the 'this' keyword in Javascript. From the book I'm reading: "Javascript the Definitive Guide" it states that "this" is a keyword and you cannot alter it like you can with an identifier.
Now, say you are in your own object constructor and you make a call to some jQuery code, how is it able to hijack this from you?
function MyObject(){
// At this point "this" is referring to this object
$("div").each(function(){
// Now this refers to the currently matched div
});
}
My only guess would be that since you are providing a callback to the jQuery each() function, you are now working with a closure that has the jQuery scope chain, and not your own object's scope chain. Is this on the right track?
thanks
You can change the context of a function (i.e. the this value) by calling it with .call() or .apply() and passing your intended context as the first argument.
E.g.
function fn() {
return this.foo;
}
fn.call({foo:123}); // => 123
Note: passing null to either call or apply makes the context the global object, or, in most cases, window.
It's probably worth noting the difference between .apply() and .call(). The former allows you to pass a bunch of arguments to the function it's being applied to as an array, while the latter lets you just add them as regular arguments after the context argument:
someFunction.apply( thisObject, [1,2,3] );
someFunction.call( thisObject, 1, 2, 3 );
From the jQuery source:
for ( var value = object[0];
i < length &&
callback.call( value, i, value ) // <=== LOOK!
!== false;
value = object[++i] ) {}
It doesn't hijack anything - it just makes sure that "this" is pointing at what it wants it to. Look up the standard "call" function that's available for any Javascript function object.
See the documentation for Function.apply. The first parameter is the "context". It can be any object. If null, it will be scoped globally.
The function type in JavaScript has a method called apply() which allows you to specify to what object this is bound at. Its signature is:
apply(thisObj, arguments);
Once called, it calls the function binding this to thisObj and passing the arguments arguments.
It is usually used as such:
function product(name, value)
{
this.name = name;
if (value > 1000)
this.value = 999;
else
this.value = value;
}
function prod_dept(name, value, dept)
{
this.dept = dept;
product.apply(this, arguments);
}
prod_dept.prototype = new product();
// since 5 is less than 1000 value is set
var cheese = new prod_dept("feta", 5, "food");
// since 5000 is above 1000, value will be 999
var car = new prod_dept("honda", 5000, "auto");
try this:
var MyObject = { "Test": "Hello world!" };
(function ()
{
alert(this.Test); // Gives "Hello world!"
}).call(MyObject);
(function ()
{
alert(this.Test); // Gives "Hello world!"
}).apply(MyObject);
(function ()
{
alert(this.Test); // Gives "undefined"
})()
function.apply() and function.call() allow you to change what this points to in Javascript. Here's a couple of handy articles that explain it in greater detail - Scope In Javascript and Binding Scope in Javascript