简体   繁体   中英

Why does the JavaScript “delete” operator behave differently in different browsers?

I was planning on using the delete operator in JavaScript for something, when I decided to remind myself how it worked by playing with it some. I understand that "delete" is supposed to be used on object properties, but I wanted to see how it behaves on variables. However, some weird results occurred when I put the following snippet of code in different browsers:

var one = 1;
delete one;
console.log(one); // prints out 1 in Chrome, but not in Safari or Firefox

In Safari, the JavaScript console printed out the error "ReferenceError: Can't find variable: one". Firefox gave a similar response: "ReferenceError: one is not defined". However, Chrome printed out the value of the variable, 1. Can anyone explain why Chrome behaves differently than Safari and Firefox in this regard?

Don't trust the console . Some codes behave different there.

The following will alert 1 if you run it on a real page:

var one = 1;
delete one;
alert(one);

Demo

(Tested on Firefox, Chrome, IE, Safari and Opera.)


Understanding delete explains why the console (or firebug) shows a different behavior:

Every execution context has a so-called Variable Object associated with it. Similarly to execution context, Variable object is an abstract entity, a mechanism to describe variable instantiation. Now, the interesing part is that variables and functions declared in a source text are actually added as properties of this Variable object .

Finally, variables declared within Eval code are created as properties of calling context's Variable object .

When declared variables and functions become properties of a Variable object — either Activation object (for Function code), or Global object (for Global code), these properties are created with DontDelete attribute . However, any explicit (or implicit) property assignment creates property without DontDelete attribute . And this is essentialy why we can delete some properties, but not others.

So what happens in Firebug? Why is it that variables declared in console can be deleted, contrary to what we have just learned? Well, as I said before, Eval code has a special behavior when it comes to variable declaration. Variables declared within Eval code are actually created as properties without DontDelete .

Let's checkout the docs on delete from MDN.

The delete operator removes a property from an object.

You are using it a way that doesn't make much sense, that is to remove a local variable.

Those docs also say:

Throws in strict mode if the property is an own non-configurable property (returns false in non-strict). Returns true in all other cases.

Which means your usage here would throw an exception in strict mode, since you are using delete in an unsupported way. This is proven when you do:

var one = 1;
delete one; // returns false

And as the docs mention, a return value of false means the delete operation did not succeed.


If you use it properly, it should behave like you expect:

var obj = {one: 1};
delete obj.one;    // returns true
alert(obj.one);    // alerts "undefined"

Revised Answer:

Based on feedback from @Oriol concerning property attributes. I have found that the real issue here is concerning Property Attributes (See ECMA-262 edition 5.1 section 8.6.1 ) and the Variable Environment 's execution context (See ECMA-262 edition 5.1 section 10.3 )

Can anyone explain why Chrome behaves differently than Safari and Firefox in this regard?

var one = 1;
delete one;
console.log(one); // Returns 1.. but why?

Two things are happening here:

  1. The var declaration binds the declared object into an "Execution context" that is distinctly different from the Global ( window ) one.
  2. JavaScript evaluates the [[Configurable]] property to determine if its "OK" to delete

Concerning #1

The var declaration in the code establishes a VariableEnvironment whereby the value is bound to the object in a distinctly different execution context (scope) than the global one. So naturally, when var is not used the VariableEnvironment is interpreted in a global execution binding process, making statements like one = 1; or delete one; possible.

var one = 1; // Execution context #1 has a unique VariableEnvironment
delete one; // Execution context #2 has a global VariableEnvironment
console.log(one); // Return the value from 'var one'

This complies with the language spec:

10.4 Establishing an Execution Context

Evaluation of global code or code using the eval function (15.1.2.1) establishes and enters a new execution context...

10.4.2 Entering Eval Code

The following steps are performed when control enters the execution context for eval code:

If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then, Initialise the execution context as if it was a global execution context using the eval code...

Concerning #2

Chrome and JSFiddle are both doing the right thing here. The the reason has to do with the [[Configurable]] property attribute, which is assigned to both native and user-created properties behind the scenes. When a user-created property is established, this attribute is set to true . This allows the developer to execute assign and delete commands on the property.

var test = {};
test.me = "OK" // [[Configurable]] is true so No Problem!
delete test.me // Good here too!

To prevent certain situations where object properties should not ever be deleted or modified [[Configurable]] is set to false by default. Which respects the language spec:

If the value of an attribute is not explicitly specified by this specification for a named property, the default value defined in Table 7 is used...

[[Configurable]] false

var test2 = [1,2,3];
console.log(test2.length); // length property is '3'
console.log(delete test2.length); // NOPE [[Configurable]] is false

Same is true in function arguments in a function scope:

(function foo(one) {
 console.log(delete one);
})(); // NOPE (false)

What can we draw from both findings?

From this we can understand that Firefox and Safari do not does not play by the rules. When var one=1; is declared in either of these browser's consoles, properties in this scope are incorrectly deemed [[Configurable]] by default and thus deletes var one and not the implied window.one .

In Firefox/Safari:

var one = 1; // var 'one'?
delete one; // NUKE var 'one'!
console.log(one); // ReferenceError: 'one' is not defined :'(

"OK Wait! So why then is delete one true by itself?

It plays out as determined by the language spec ( 10.4.2 ):

var one = 1; // VariableEnvironment not global or [[Configurable]]
delete one; // FALSE

...

delete one; // TRUE VariableEnvironment global and [[Configurable]]

...

var one = 1; // VariableEnvironment not global or [[Configurable]]
delete this.one; // TRUE VariableEnvironment is global and [[Configurable]]

I think the answer he's looking for, and someone essentially said earlier, but muddied up the clarity by adding more explanation through an edit should be restated thus:

When you use a method in a way that was not original dictated in the standard, then the result is undefined and therefore varies from browser to browser ( because each essentially has a different js engine ) in the way that it's handled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM