Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

delete: can someone explain this behavior

Compare this code1:

  somevar = 5;
  delete window.somevar;
  alert(typeof somevar) //=> undefined, so deleted

to this code:

  var somevar = 5;
  delete window.somevar;
  alert(typeof somevar) //=> number, so NOT deleted

See it in action here

Now in the first block, somevar is deleted, in the second block it's not. The only difference is using the var keyword in the second block. Both blocks run in the global scope.

Can this be explained?

1 the code can't be tested in a chrome-console or firebug, and not in jsfiddle either. In those environments all code is evalled, and in evalled code delete works on anything that is the result of eval (see more about that). In IE < 9 delete window[anything] is not allowed anyway.

like image 814
KooiInc Avatar asked Dec 21 '22 14:12

KooiInc


1 Answers

What you're seeing is an aspect of the fact that the global object (window, on browsers) is a conflation of two different things which are distinct everywhere except the global execution context.

In the first block, someVar is a normal property of the window object. Properties can be removed via delete.

In the second block, someVar is a property of the binding object of the variable context of the global execution context — which is also window. You cannot delete properties the binding object receives in its role as the binding object (even though you can delete properties it receives in other ways). That is, you cannot delete variables declared with var (and a few other things that are added the same way).

(Sorry, not my terminology; it comes from the spec, which features some very fun language indeed.)

It's only the global execution context where we have this conflation of concepts. The variable binding object for other execution contexts (function calls, for instance) is still a very real thing (and crucial to proper functioning of closures), but there's no programmatic way to directly access it. In the global execution context, though, it's the global object, which of course we can access.

It helps to understand this if we look at functions first, and then look at the global execution context. when you call a function, these things happen:

  1. Set this to point to the object designated by the call (the value of this is usually implicitly set, but there are ways to set it explicitly).
  2. Create an execution context for this call.
  3. Create a variable context for that execution context.
  4. Create a binding object for that variable context.
  5. Add the function's name, if it has one, to the binding object as a property referring to the function.
  6. Add the arguments property to the binding object, referring to the pseudo-array of arguments to the function.
  7. Add any named arguments declared in the function definition as properties of the binding object, referring to their entries in the arguments.
  8. Add the names of of any variables declared via var statements (anywhere in the function body) as properties of the binding object, initially with the value undefined.
  9. If there are named functions declared within the function, add their names as properties of the binding object, referring to those functions.
  10. Put the binding object at the top of the scope chain (more below).

...and then step-by-step execution of the code in the body of the function begins. Any var statements with initializers (e.g., var a = 5; rather than just var a; are treated as assignment statements (a = 5;) when the execution point reaches them.

Throughout the above, whenever a property is added "to the binding object", it's added with a flag indicating that it cannot be deleted. This is why vars (and the names of declared functions, etc.) can't be deleted.

Any unqualified reference is looked up via the scope chain. So when you refer to a in your code, the first place the interpreter looks is the binding object at the top of the scope chain. If it has a property called a, that's what gets used; if not, we look at the next link down the scope chain and use that property if we find it; and so on until we run out of links on the scope chain. The global object is the bottommost link of that chain (which is why global variables work).

So what's different about the global context? Well, very little, actually. Here's the sequence (roughly):

  1. Create an execution context for this call.
  2. Create a variable context for that execution context.
  3. Create a binding object for that variable context.
  4. Set this to point to the binding object; that makes it the global object.
  5. Set some default properties on that object as defined by the environment (in browsers, for instance, the property window is added to the object, referring to itself).

...and then we basically pick up with step 8 in the function stuff:

  • Add the names of of any variables declared via var statements (anywhere in the global scope) as properties of the binding/global object, initially with the value undefined.
  • If there are named functions declared within the global scope, add their names as properties of the binding/global object, referring to those functions.
  • Put the binding/global object at the top of the scope chain (more below).

...and start step-by-step execution of the code (again with var initializers becoming assignments).

like image 58
T.J. Crowder Avatar answered Jan 08 '23 20:01

T.J. Crowder