I found this answer: How to modify "this" in javascript
But I did not like it. Surely there must be a way to somehow modify this?
Here is an example:
(function()
{
var a = function(v)
{
v += 10;
a = function()
{
console.log(this);
this += 5; // How to "unlock" 'this'?
console.log(this);
}.bind(v)
//a();
}
a(0);
a();
})();
Fiddle: https://jsfiddle.net/ahvonenj/mz19aku6/
If you comment out the this += 5 part, the console says that this is Number {[[PrimitiveValue]]: 10}. So I do not see a reason why a Number (PrimitiveValue) type could not be increased by 5 and printed out. What am I missing here?
Is there a way to "break out" of the engine thinking this as this and somehow forcefully convert this to what it says it is - Number in this case?
Maybe by somehow making a function that accepts a to-be-converted this as an argument and returns a new function which has the supplied this rebound with bind?
Note: I am not looking for doing anything practical with a possible solution. I am just curious from a technical standpoint or something.
Edit: So a user named Oriol told me that this is not actually a Number object with a value of 10, but Instead a Number literal. This apparently means that this += 5 is almost the same as writing 10 += 5 in this case. I tried checking out out like so:
(function()
{
var a = function(v)
{
v += 10;
a = function()
{
console.log(this);
var p = this + 5; // How to "unlock" 'this'?
console.log(p);
}.bind(v)
//a();
}
a(0);
a();
})();
So var p = this + 5 is now basically the same as writing var p = 10 + 5, which is what we expected.
You can think of this as an implicit, immutable, value accompanying a function call.
There are a number of ways it can be set, the most common being that a call of the form o.f() sets this to o within f. Of course, this can also be set with call and apply and bind, and in other ways as well.
this is not like other function parameters. If were a normal parameter, its value could in theory be changed. Instead, it represents a value, a sort of lexical placeholder for the value of this. In other words, if I call
f.call(5)
and then write
function f() { console.log(this); }
You can think of the this as being lexically replaced by the value 5. So if I tried to write
function() { this += 5; }
you can think of this as being the equivalent of
function() { 5 += 5; }
which is obvious nonsense. I cannot replace the value 5 with something else. By the same token, if I said
o = {
f: function() { this = o2; }
};
and then call o.f(), the function is essentially rewritten as
function() { { f: ... } = o2; }
which is equal nonsense. I cannot re-assign the value of an object.
Since this is a value, not a parameter, it cannot be assigned to or changed. It is not a valid left-hand-side for an assignment, any more than 42 is. We cannot change the value of the value named 42. We cannot change the value of the value named this.
The reason for this is that is the specification. In theory, one can imagine a version of JavaScript where this was mutable. There is no definitive reason why this should not be possible. After all, no matter where it comes from, within the function it is simply a value. It would be confusing, and could result in unexpected behavior, but there is no reason why it could not work.
The primary reason this is not supported is simply that that is not how the language is defined. If your question is "why is the language defined that way", then that is something for language historians to answer. However, one can imagine good reasons for this, involving intuitive code behavior.
Consider the following code:
o = {
f: function() {
this.doSomething();
this = o2;
this.doSomething();
};
It is going to be extremely hard for normal human beings to understand such code. The same two this.doSomething() calls do two different things due to the intervening change to this. This alone is a more than adequate reason to prohibit changing the value of this. All the more so since there is no logical reason for needing to do this, since I can replace the last two lines with a simple o2.doSomething().
Just to be clear, of course when we talk about "changing this", even if we could do that, which we can't, we couldn't possibly mean somehow changing the outside value this refers to. We all know that in the following case:
function a(x) { x = 2; }
var y = 1;
a(y);
The change to x within a() is purely local, and can't possibly affect y. The same thing would apply equally to this of course.
One more note on objects wrapping primitives. We know we can write
o5 = Object(5);
to get an object wrapping the number 5. Now I can define a method on this object, as in
o5.f = function() { console.log(this); };
and calling o5.f() will log
Number {[[PrimitiveValue]]: 5}
So since this object "is" a number, certainly in this case I should be able to say
o5.f = function() { this++; };
But no. this is not the number 5; it's an object wrapping 5. Those are two very different things. The function above is essentially equivalent to saying
o5.f = function() { Object(5)++; }
which yields
Uncaught ReferenceError: Invalid left-hand side expression in postfix operation
Now I can do arithmetic on the object, such as Object(5) + 1, because JS will coerce the object to its underlying primitive before adding one. But I cannot re-assign to this object any more than any other. And I cannot modify the primitive number underlying the object. Once the wrapper object is created, the primitive value underlying it is locked in forever.
So the answer to your question is "no, there is no way to change this". Which is not to say there are not other ways to do other things to accomplish whatever it is you want to accomplish.
Actually the problem here is that when you binding the function to a number then the this is getting changed to number and now when you are doing the assignment operation this += 5; the left hand side is not a variable where we can store the new value as its a primitive number instance. so to overcome that what you can do is you can store the this value in another variable say me and then do the operation as following:
(function() {
var a = function(v) {
v += 10;
a = function() {
var me = this;
console.log(this); //logs Number {[[PrimitiveValue]]: 10}
me += 5;
console.log(me); //logs 15
}.bind(v)
//a();
}
a(0);
a();
})();
I don't know any language that allows you to alter this.
you might want to think of this as an address:
my house "sits" on Sesame street 10. of course, I can invite designers who can change who my Sesame-street-10-house looks like, but can I suddenly make Sesame street 10 turn into Sesame street 15? of course not. I can't just rip an entire piece of land and pass it 5 houses ahead.
going back to programing context, this (eventually) is interpreted to the memory block your object sits in. you can change that object which sits on the specified memory block, but you can't just change the block address.
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
Simple types (except objects and arrays) is immutable. You can't modify it and save in source variable.
function foo() {
this.val+=10;
}
a=function(obj) {
a=foo.bind(obj);
}
But you can wrap it into object. See: http://jsfiddle.net/5dhm8fsn/
Related
I have a quick question regarding some code I do not understand:
var anonymousGreet = function(){
console.log('Hi');
}
function log(a){
console.log(a);
}
log(anonymousGreet);
In the code above when I call the function log and pass in the function expression anonymousGreet as a parameter to log. Does that mean the variable "a" in log is pointing to the variable anonymous greet which is then pointing to the function object. Or does a point directly to the function object pointed to by anonymousGreet? I am a little confused if a first points to the variable anonymous greet or does it directly point to the function object pointed to by anonymousGreet. Sorry if this is confusing but any help would be appreciated! Thanks you!
If you come from a C++ background then a simple rationalization is
In Javascript everything is passed by value, references are never used
All passed values are however pointers to objects
For example when you write:
a = b + c;
you should imagine (in C++) something along the lines of
Object *a, *b, *c;
a = new Number(b->numericValue() + c->numericValue());
(note that however Javascript differently from C++ provides a garbage collector so no explicit delete is ever needed).
This is of course just a simple description of what is the observable behavior (and it was may be the implementation of very first Javascript engines). Today what really happens behind the scenes is much more sophisticated and includes run-time generation of machine code (JIT).
This is the reason for which for example:
function foo(x) {
x[0] = 1;
}
var a = [0];
foo(a); // Will change the content of a[0]
but
function bar(x) {
x = 9;
}
var b = 0;
bar(b); // Will *NOT* change the content of b
Yes, if you check a===anonymousGreet, the result is true. All primitive values are passed by value, but objects are passed by reference in Javascript.
var anonymousGreet = function(){
console.log('Hi');
}
function log(a){
console.log(a === anonymousGreet); //true
console.log(a);
}
log(anonymousGreet);
As a matter of terminology, and to avoid confusion, I would avoid the use of the word "pointer". It's too easy to get confused when using the word "pointer". Sometimes "A pointing to B" can mean that A is a kind of alias for B. Or that B can be modified through A. Or A can point to B which points to C; what does that mean?
IMO it is clearer to use the terminology that in JavaScript a variable holds a value. That's it. It holds a value. It does not point to anything, it does not refer to anything, it holds a value.
However, two variables may hold the same value, such as an empty array []:
var a = [];
var b = a;
Both a and b hold the same value, a particular empty array. We can see that they hold the same value by checking a === b. Since they hold the same value, that value can be manipulated through either one; a.push(1) and b.push(1) do exactly the same thing, because they are both manipulating the value held in common by each. Re-assigning to a merely changes the value that a holds; it does not and cannot affect b, and vice versa. b is not a "pointer" to a, or an alias to a; it is a variable which happens to hold the same value as a.
In other words, a = b does not make a "point" to b. Nor does it copy the value of b and assign it to a. It gives a the value of b, so that a and b now hold the identical value.
The function parameter passing mechanism can be thought of as a particular type of assignment. It makes the formal parameter in the function definition assume, or hold, the value of a parameter specified in the call. When I define a function
function foo(x) { console.log(x); }
and then call it
var y = 42;
foo(y);
the variable/parameter x is assigned the value of the parameter passed in the function call. In other words, x comes to hold the value of y. x does not "point" to y, and it is not a copy of y. It holds the same value as y, which in this case is 42.
Let's take your specific example:
var anonymousGreet = function(){
console.log('Hi');
}
function log(a){
console.log(a);
}
log(anonymousGreet);
In this case, anonymousGreet is a variable which, by virtue of the assignment, holds as its value a particular anonymous function. When you call log(anonymousGreet), the parameter a to the log function is set to hold the value of the parameter in the call, so it now holds the value of anonymousGreet. It does not "point" to the variable anonymousGreet, and any changes to it cannot affect the value of anonymousGreet, nor of course the function which that value represents.
Does that mean the variable a in log is pointing to the variable anonymousGreet which is then pointing to the function object?
No, it means that the variable/parameter a in log holds the value held by anonymousGreet, which is the anonymous function.
Or does a point directly to the function object pointed to by anonymousGreet?
Yes, but not "point to the thing pointed to by", but rather, "holds the value of anonymousGreet, which holds as its value the anonymous function.
I just started programming and I've been playing around with it. I watched a programming course on lynda.com to start out, but it didn't cover functions very well, I know what they are, but I don't know the different formats of functions or how to call them. I know how to call simple things like this:
var foo=function {
//code...
}
but I want to use more complicated things like this (I'm starting to to things with HTML):
$(document).keypress(function(e)) {
if(e.which == 13) {
alert('You pressed enter!');
}
});
or just whatever other styles there are.
There are no different formats of functions. All of them are defined with function keyword followed by arguments and body. If you have a function foo then you call it via foo(); (you can pass arguments as well, foo(1,2,3);). There are more complex ways of calling functions, for example via foo.call or foo.apply but I don't think it is necessary to talk about that here.
Note that functions in JavaScript are first class citizens. That means that they can be treated as objects. In particual you can pass them to another function. Have a look at this example:
var foo = function(fn) {
return fn()+1;
};
What happens here? foo takes a function as an argument (we know that because fn() is called in body), calls it and adds 1 to result. So if I call foo like this:
foo(function() {
return 1;
});
what would the result be? That's a simple example. Consider something more complex:
var foo = function(list, fn) {
var res = [];
for (var i = 0; i < list.length; i++) {
var mapped = fn(list[i]);
res.push(mapped);
}
return res;
};
This is a simple version of the .map method on lists. So it takes a list and a function as arguments, then applies the function to each element of the list and returns a new list:
> foo([1, 2, 3], function(el) { return -el; });
[-1, -2, -3]
It is the same as calling
> [1, 2, 3].map(function(el) { return -el; });
[-1, -2, -3]
Different ways of declaring functions
function A() {}; // function declaration
var B = function() {}; // function expression
var C = ( function() {} ); // function expression with grouping operators
var D = function foo() {}; // named function expression
var E = ( function() { // immediately-invoke function expression (IIFE) that returns a function
return function() {}
})();
var F = new Function(); // Function constructor
var G = new function() {}; // special case: object constructor
Your code is actually a good example of the different types of functions. First is $(), which represents the JQuery library--a useful add-on to JavaScript. You typically give it a string called a CSS selector, which is a way to pick out a part (or parts) of a document. In this case, you're picking out the whole document, but you could also say something like $("#logo"), which will return a document with id="logo" or $("img") will return all elements in the document.
An object in JS can be just about anything, and there are different types of objects. Each type of object has special functions available to it. These functions are called methods.
In the above example, the document object returned by $(document) has the .keypress() method available to it, which listens for a particular keypress. Keypress needs to know what to do when a key is pressed, and you do so by giving it a function as a parameter. That function will execute every time a key is pressed.
One thing that's useful to remember about JavaScript is that functions are thought of as "first-class citizens", meaning they are as good as straight-up values as far as JavaScript is concerned. So if I have a function like:
var myString = function(word)
{
return word;
}
If I do:
var myWord = "hello";
It's the same as:
var otherWord = myString("hello");
If I compare them:
var compareWords = function(word1, word2)
{
return word1 == word2;
}
var compareMyWords = compareWords(myWord, otherWord);
It's the same as saying
var compareMyWords = true;
This is important to know because it means functions can take other functions as arguments without a problem. We see that happening in the keypress method. It might be a bit hard to notice, but you can see it if you declare the function beforehand:
var keypressFunction = function(e)) {
if(e.which == 13) {
alert('You pressed enter!');
}
The following is the same as your example:
$(document).keypress(keypressFunction);
The difference is that your example uses an anonymous function. It hasn't been declared with "var" or "function", so I couldn't reuse it if I wanted to. That's not really a bad thing. Anonymous functions are very useful when you get into more advanced stuff.
Your anonymous function is allowed one parameter, an object representing the key that was actually pressed. In this case it only wants to do something if the key represented by 13 is pressed (Enter).
Finally there's the alert function, which pops up a message. Note that functions don't always have to return a value. Usually when they don't, they do something to their environment instead (otherwise what's the point). In your example only one function actually returns something--the $() function, although nothing gets done with it, but you could keep going and replace the semicolon at the end with a period and use the ready() method or whatever methods are available to the object returned by $(document). In fact, as long as you know what type of object is being returned, you could do something like:
$("#message").addClass("alert").fadeIn().delay().fadeOut();
Because each of these methods return the same object, we can do this "method chaining". jQuery purposely tries to make most of its methods return the DOM objects it acted on so that you can do this. Other libraries, like d3.js, make use of method chaining for some very cool visual transformations.
I don't think starting off using jQuery is as horrible an idea as others might advise. It's still JavaScript. It could be argued that JQuery forces you to learn advanced concepts (callbacks, CSS selectors, etc) that you could otherwise hobble by for years without knowing. I could also argue that JavaScript itself is a bad language to start with when learning to program.
Instead I'll say dive in and have fun! The ultimate purpose is to build awesome things & not get too crippled by having to do it the right way. Find a good balance between learning and building, and don't get too discouraged by the few jealous stackoverflow users who come here to snuff the joy they once had in building awesome things.
I order to know how to call different types of functions in Javascript it is important to know all JavaScript Function types. Below is a link that take you to:
All JavaScript Function Types?
I personally think you are over complicating it.
Yes, the different ways of writing a function tend to have different names (function, anonymous function, colosure, lambda,....) but in the end its all a function:
They combine several statements into one.
For example:
// Crating a function (most commomn way)
function a() {
alert('a');
};
// Creating a anonymous (nameless) function and assigning it to b
var b = function() {
alert('b');
};
var c = a;
var d = b;
var e = {};
// Creating a function that accepts a function and executes it.
// This is in essence what $(document).keypress() does
e.f = function(f){
f();
};
// Alerts: a
a();
// Alerts: b
b();
// Alerts: a
c();
// Alerts: b
d();
// Sample how you can call $(document).keypress()
// Alerts: c
e.f( function() {
alert('c');
});
// Alerts: a
e.f(a);
// Alerts: b
e.f(b);
function a(){ return x++; }
function b(){ var x=0; return a(); /*call a using b's scope*/ };
console.log(b(),b(),b());
Desired output: 1, 2, 3
Question: is there a way to call a inside b, forcing it to use b's scope?
Not quite, but you can wrap a closure around it:
(function() {
var x = 0;
window.a = function() {return x++;};
window.b = function() {return a();};
console.log(b(),b(),b());
})();
Your problem can be decomposed into two parts:
Dynamic scoping - http://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping
Variable preservation across calls
The first part can be easily tackled using eval. Read the following StackOverflow question for more details: Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
The second part requires that you store the intermediate value of the variable x somewhere and restore it after every function call. The best place to store it in this case would be on the function b itself.
This is what your code would look like:
function a() {
return ++x; // I believe you want prefix increment, not postfix
}
function b() {
var x = b.x;
eval(String(a)); // dynamic scoping
b.x = a();
return x;
}
b.x = 0;
console.log(b(), b(), b());
You can see the demo here: http://jsfiddle.net/ucGxD/1/
No. Scope comes solely from how the function is defined.
You would have to pass data using arguments (and a closure) to get something from the scope of b into a.
Your exact example isn't going to work (because x is reinitialized every time you call b), but consider:
x = 100;
function a() { return x++; }
function b() {
var x = 0;
console.log(a(), a(), a()); // 100 101 102
eval(a.toString())
console.log(a(), a(), a()); // 0 1 2
}
Needless to say, don't try that at work.
No, there isn't. And that's definitely a feature.
Update
Since some answers and comments around here suggested using function.toString and eval to emulate dynamic scoping, let me summarize why I think that that is not a proper solution (besides being a bad idea anyways):
The result of invoking toString on functions is not specified by the language standard, nor intended for this kind of use. It happens to work on most current browsers for many (user-defined) functions, but there is no guarantee for that, and I strongly advise against writing production code that relies on it.
Functions are far more than the piece of text that stringification can give you. In particular, functions are objects that have identity and carry state, neither of which stringification can preserve. More precisely:
Functions can have properties that the function body accesses (in particular, the prototype property). They will be lost across stringification.
Functions have a closure environment encapsulating the variables from surrounding scope they use. Again, this will be lost, resulting in dangling or misbound variable references (even if that perhaps was the purpose of the exercise for some of them).
Functions have other potentially relevant properties, like their identity, a modified __proto__, or configuration states like being non-extensible. None of that is preserved.
Not all functions are simple JavaScript source functions. They may e.g. be native, or from ES6 on, they may be function proxies. In neither case will stringification be useful.
A function with unbound variable references is not legal in strict mode (and thus will be illegal in many places in ES6).
So while stringification may work in some simple cases, that is by accident, not by design. It is a brittle hack. Steer clear of it.
Yes. Context can be transferred by call or apply. You can set the context to scope by calling bind to itself. See this example:
function a() {
return ++this.x;
}
function b() {
this.x = this.x || 0;
return a.call(this); // a works with x in b's context
}
b = b.bind(b); // now the b's context is the b's scope :-)
console.log(b(),b(),b()); // 1 2 3
console.log(x); // error: x is not defined - global scope is clear
As Quentin points out see the difference between context and scope here.
Background: I want to rewrite a library (which I did not write) to avoid having the Closure Compiler generate warnings with the advanced option. Per this question JavaScript “this” keyword and Closure Compiler warnings the answer was to rewrite the code using a closure. The intent is to avoid using the keyword this (which generates the compiler warnings).
As the library has a number of functions, I though it would be best for the new closure to return an object literal. I want to understand how this works and any possible ramifications. I therefore wrote the following (meaningless) example as a learning expercise (also here: jsFiddle):
var CurrencyObject = function(Amount) {
var money = Amount;
return {
"toCents": function() {
money *= 100;
return money;
},
"toDollars": function() {
money /= 100;
return money;
},
"currentValue": money // currentValue is always value of Amount
};
}; // end currencyObject
var c1 = CurrencyObject(1.99); // what's the difference if the syntax includes `new`?
alert('cents = ' + c1.toCents() + '\ncurrent money = ' + c1.currentValue + '\ndollars = ' + c1.toDollars() + '\ncurrent money = ' + c1.currentValue);
var c2 = CurrencyObject(2.99);
alert('cents = ' + c2.toCents() + '\ncurrent money = ' + c2.currentValue + '\ndollars = ' + c2.toDollars() + '\ncurrent money = ' + c2.currentValue);
alert('cents = ' + c1.toCents() + '\ncurrent money = ' + c1.currentValue + '\ndollars = ' + c1.makeDollars() + '\ncurrent money = ' + c1.currentValue);
Q1: Why isn't currentValue updated after the call to toCents? (I'm going to guess that it is because currentValue is a literal(?) that it is initialized when CurrencyObject is first executed. If that's the case, what's the syntax for also returning the property currentValue?)
Q2: This syntax (with new) var c1 = new CurrencyObject(1.99); does not change the code's behavior in a way that I can detect, yet I assume there is a difference. What is it?
Q3: When c2 is instantiated, are copies of the functions being created or will c1 and c2 share the same (function) code? (If copies of the functions are being created, what changes do I make to avoid that?)
TIA
BTW: In the event someone is wondering, the symbols in the object literal are quoted to avoid having the Closure-Compiler rename them.
Q1: You've created two variables that contains separate copies of the Amount. One is in the money variable captured in the closure. The other copy is in the currentValue property of your returned object. Those are separate variables that have no connection to one another, other than the initial value of currentValue is initialized with the value of the money variable.
To work-around this issue, you have to settle on only have one place that you store your currentValue and refer to it there. You could only use the money local variable in the closure, but that would require changing the currentValue property to be a getter function (that retrieves the money value) rather than a direct data property.
Or, you could get rid of the money closure variable and only use the currentValue property. That would require you to use this to get that property in the toCents and toDollars methods. In my opinion, the cleaner way to do this would the latter (using this to reference the object's own properties). I have no idea why closure wouldn't want you to do that.
Q2: When you explicitly return an object from a constructor, then it no longer matters whether the constructor is called directly as a function or with the new operator. As you have observed, both create the same result.
Q3: Since your functions are anonymous, each new instantiation will create a new anonymous function object. How efficient or inefficient that is will depend upon the javascript implementation. You could make there only be one function by declaring your functions to be local named functions and referring to that name. Then, you could guarantee that no new function was created.
UPDATE:
I branched your fiddle, and added a couple of methods that all instances will in fact share (ie: no new function objects will be created for each instance): the methods that convert the amount into Euro's or Pounds, for example. I've also omitted the money variable, because it's simply not necessary. To avoid having to use this as much as possible, I've also assigned the returned object to a variable within the closure scope, so that all methods can reference their parent object via that variable name, rather than having to rely on this.Shared methods, however, will still have to use this occasionally, because they were declared in a "higher" scope, and don't have access to the returnObject variable, simply because it doesn't exist in their scope. Lifting the returnObject variable declaration is no solution in case you were wondering, because then you'll soon find out that you can't create more than 1 instance of the currency object.Lastly, I've changed the name of the "constructor" to start with a lower case. Technically, your function is not a constructor any longer, and convention is that it therefore can't start with an upper-case letter... Let me know if anything I explained here, or any of my suggested changes are still unclear to you.
The currentValue isn't updated because you changed the money variable's value by doing: money *= 100;. This statement multiplies the money value and assigns it to the same variable. Since this is a primitive, currentValue has its own copy of this value, they're not linked in any way.Suggestions: use return money/100; to keep the value of money constant. As it now stands calling the toCents method twice is the same as multiplying the original amount by 10,000. To update/set the currentValue on each call to whatever you want it to be, add: this.currentValue = money*100;, which is a little dangerous, or giving your closure access to its own literal by using a named reference (which is safer, but a bit more verbose):
var someClosure = function (amount)
{
var money = amount;//redundant: you can use amount, it's preserved in this scope, too
var me = {currentValue:amount,
toCents: function()
{
me.currentValue = money*100;//<-- use me to refer to the object itself
return amount/100;
}
};
return me;
}
There is no reason to use the new keyword: the object that is created by this "constructor" is an object literal, it has only 1 prototype (Object.prototype, and no explicit constructor name). Adding new will make this point to a new object in the function itself, but since you're not using it, and your function returns another object, the object is never returend.
Strictly speaking: some JS engines will create new function objects for each new instance. Some modern objects optimize this, and will in fact only create 1 function object. To be on the safe side, you can wrap another closure around things, to make sure there is only 1 function, regardless of what engine runs your code:
var someObjectGenerator = (function()
{
var oneOffFunction = function()
{
console.log('this function will only be created once');
}
return function(amount)
{
return { foo:oneOffFunction,
toCents: function()
{
return amoutn/100;
}
};
};
};
Of course, functions that are created in a scope above the one that has the money or amount variable won't have access to that variable, so in that case you'll have to create new functions... but JS objects are very cheap, so don't worry about it that much. Again, most engines deal with this rather well.
The with statement in JavaScript first checks if the requested property of the object exists before it decides if it should set the property of the given object or the property of the global object.
Example:
var x = {a:5};
with(x){
a = 6; //x.a is now 6
b = 7; //window.b is now 7, x.b still does not exist
}
I want to change the behavior of with, so that when it checks for the presence of a property on the object I'm working with it will always treat it as though it exists, preventing assignments within the block from accidentally modifying the global object.
Could this be accomplished by overloading the function that checks whether the property of the object exists or not? For example something like this:
Object.prototype.__hasOwnProperty__ = function(p){
return true;
}
var x = {a:5};
with(x){
a = 6; //x.a is now 6
b = 7; //x.b should now be 7
}
My code runs on node.js & V8, so it doesn't matter if the solutions only work with this.
Hope someone has an ide how I realize this.
Thanks for your help!
You're trying to fundamentally change how the with statement works in JavaScript. This isn't possible, because you have no guarantee that the interpreter is using hasOwnProperty (or any other construct you have access to) to check for the presence of a property on the object you're working with.
You're joining a nice tradition of wishing that with worked differently, but it doesn't. It works how it does and it's best avoided.
Two things:
Don't modify the Object.prototype. It's considered bad practice and will cause unexpected result everywhere, plus other JS frameworks won't even run if it's been modified.
Please don't use with. It is being deprecated from javascript because it can't be determined how it should best function when there is local variable with the same name as a property.
If you need to iterate over the properties of an object, just do this:
for (var i in myObject) {
if (myObject.hasOwnProperty(i)) {
// processing logic here
}
}
You mentioned code that would help you achieve overriding that setting logic. https://developer.mozilla.org/en/JavaScript/Guide/Working_with_Objects has information on using defineSetter to override the setting portion, but it is highly recommended, on not even possible from JavaScript 1.8.1 and after. I would recommend another approach.
To keep variables out of the global scope, you can use an anonymous function; since JavaScript has functional scoping, all variables defined in a function are local in scope. It requires you to declare variables properly, though, but I would highly suggest that under any circumstance.
(function () {
var a = 1;
alert("a is... " + a);
// do more stuff here
}());
alert(typeof a); // should be undefined
Example: http://jsfiddle.net/AWDzV/
This doesn't specifically deal with your with statement, but it is probably the best way to accomplish what you're looking for. Then, all you have to do to emulate the behavior of the with statement is to use a variable:
(function () {
var a = 1,
some_very_long_object_name = {a: 1, b: 2, c: 3},
obj = some_very_long_object_name;
obj.a = 2;
obj.b = 3;
obj.c = 4;
}());
alert(typeof a); // should be undefined
Not the best code in the world, but it does demonstrate that using a short variable name to replace a long object name works perfectly. All-in-all, I feel this is probably the best solution.
Of course, any variable not declared properly will "leak" into the global scope:
(function () {
a = 1; // notice the lack of "var" keyword
}());
alert(a); // should be 1
Example: http://jsfiddle.net/AWDzV/1/
But you can use JSLint to help catch that particular error. Running it on the above code yields the following error message:
Error: Implied global: a 2,4, alert 4
I've found a way to totally isolate a skript. It's not good code, but it works.
X = 5;
function ExecuteIsolated(code){
var vars = [];
(function(){
for(var v in window){
vars.push(v);
}
eval("var "+vars.join(",")+";");
//Totally isolated code here:
eval(code);
//End of totally isolated code
})();
var i = 0;
for(var v in window){
if(vars[i++] != v){
delete window[v];
i--;
}
}
}
ExecuteIsolated("X = 8");
console.log(X);
Maybe its useful for someone else ;)