You can’t do this either, go home and farm
By the way, let’s talk about how to use delete
A few weeks ago, I had a chance to check out Stoyan Stefanov’s book Object-Oriented Javascript. The book has high ratings on Amazon (12 reviews, 5 stars), so I was curious Wanting to see if it was such a recommendable book, I started reading the chapter on functions. I really liked the way this book explained things, and the examples were organized in a very nice, progressive way. At first glance, it looked like even a beginner would be able to master this knowledge easily. However, almost immediately, I discovered an interesting misunderstanding that occurred throughout the chapter - deleting function functions. There were also a few other errors (such as function declarations and function expressions), but we won’t discuss them for now.
This book claims:
"A function is treated like a regular variable - it can be copied to a different variable, or even deleted." This explanation is followed by an example:
Ignoring some missing semicolons, can you see where the error is in these lines of code? Obviously, the error is that deleting the sum variable will not succeed. The delete expression should not return true, And typeof sum shouldn't return "undefined" either. All because deleting variables is not possible in JavaScript. At least, not in this way of declaration.
So, what exactly is happening in this example? Is it a bug? Or is it a special usage? Probably not. This code is actually the actual output in the Firebug console, and Stoyan must have used it As a tool for quick testing. It's almost as if Firebug obeys some other delete rules. It was Firebug that led Stoyan astray! So, what is going on here?
Before answering this question, we first need to understand how the delete operator works in JavaScript: What can be deleted and what cannot be deleted? Today, I will try to explain this question in detail. We will Take a look at Firebug's "weird" behavior and realize that it's not that weird after all. We'll take a closer look at what's behind the scenes of declaring variables, functions, assigning values to properties, and deleting them. We'll take a look at the browser's Compatibility and some of the most notorious bugs. We’ll also discuss ES5’s strict mode, and how it changes the behavior of the delete operator.
I will use JavaScript and ECMAScript interchangeably, they both mean ECMAScript (unless obviously talking about Mozilla’s JavaScript implementation)
Unsurprisingly, explanations of delete are quite scarce on the web. The MDC article is probably the best resource to understand, but, unfortunately, it is missing some interesting details on the subject. Weird Yes, one of the forgotten things is responsible for Firebug's strange behavior. And the MSDN reference is almost useless in these aspects.
Theory
So, why can we delete the properties of an object:
But objects declared like this cannot be deleted:
Or function:
Note: When an attribute cannot be deleted, the delete operator will only return false
To understand this, we first need to grasp these concepts about variable instances and property properties - concepts that, unfortunately, are rarely mentioned in JavaScript books. I will try to explain them in the next few paragraphs A brief review of these concepts. These concepts are difficult to understand! If you don't care about "why these things work the way they do", feel free to skip this chapter.
Type of code:
In ECMAScript, there are 3 different types of executable code: Global code, Function code and Eval code. These types are more or less similar in name. Self-explanatory, here’s a brief overview:
When a piece of source code is viewed as a program, it will be executed in the global environment and is considered global code. In a browser environment, the content of script elements is usually Interpreted as a program and therefore executed as global code.
Any code that is executed directly in a function is obviously considered function code. In browsers, the content of event attributes (such as
) is usually Interpreted into function code.
Finally, the code text applied to the built-in function eval is interpreted as Eval code. Soon we will find out why this type is special.
Execution context:
When ECMAScript code is executed, it usually occurs in a specific execution context. Execution context is a somewhat abstract entity concept that helps understand how scope (Scope) and variable instantiation (Variable instantiation) work. For each of the three types of executable code, there is an execution context corresponding to it. When a function is executed, we say that "program control enters the execution context of the function code"; when a piece of global code is executed, the program Control enters the execution context of global code, etc.
As you can see, execution contexts can logically form a stack. First, there might be a piece of global code with its own execution context, and then that piece of code might call a function, taking it with it. Execution context. This function can call another function, etc. Even if the function is called recursively, it will enter a new execution context each time it is called.
Activation object/Variable Object:
Each execution context has a so-called variable object associated with it. Similar to the execution context, the variable object is an abstract entity, a mechanism used to describe variable instances. The interesting thing is, Variables and functions declared in the source code are usually added to this variable object as properties.
When program control enters the execution context of global code, a global object is used as a variable object. This is why function variables declared as global become global object properties.
var foo = 1;
GLOBAL_OBJECT.foo; // 1
foo === GLOBAL_OBJECT.foo; // true
function bar(){}
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true
Okay, so global variables become properties of the global object, but what happens to local variables (those defined in function code)? They actually behave very similarly: they become variable objects (Variable object). The only difference is that when in function code, a variable object is not a global object, but a so-called activation object. The activation object will be called every time it enters the execution context of the function code. Create.
Not only the variables and functions declared in the function code will become properties of the active object; this will also be done in each function parameter (corresponding to the name of the corresponding formal parameter) and a special Arguments object (with arguments as name). Note that the active object is an internal description mechanism and cannot be accessed from program code.
Property attributes
We are almost there. Now that we have a clear idea of what happens to variables (they become properties), the only concept left to understand is the property attributes. Each Properties can have zero or more attributes, chosen from the following set: ReadOnly, DontEnum, DontDelete and Internal. You can think of them as flags - an attribute may or may not be present in the attribute. For us For today’s discussion, we are only interested in DontDelete.
When the declared variables and functions become attributes of the variable object (or the active object of the function code, or the global object of the global code), these attributes are created with the DontDelete attribute. However, any explicit Properties created by (or implicit) property assignment will not have the DontDelete attribute. This is why we can delete some properties, but not others.
Built-in objects and DontDelete
So, this is all about it (DontDelete): a special characteristic of the attribute, used to control whether this attribute can be deleted. Note that some built-in object attributes are specified to contain DontDelete, so they cannot be deleted. For example The special arguments variable (or, as we now know, a property of an active object) has DontDelete. The length property of a function instance also has a DontDelete property.
The attribute corresponding to the function parameter also has the DontDelete attribute since its creation, so we cannot delete it.
Undeclared assignment:
You may remember that an undeclared assignment creates a property on the global object, unless the property is already found elsewhere in the scope chain before the global object. And, now we know about property assignment and variables The difference between declarations - the latter will set the DontDelete property, but the former will not. We must understand why undeclared assignment creates a deletable property.
Please note: Properties are determined when the property is created, and subsequent assignments will not modify the properties of existing properties. It is important to understand this difference.
Firebug confusion:
What happened in Firebug? Why can variables declared in the console be deleted? Doesn’t this violate what we have learned before? Well, as I said before, the Eval code is facing There will be special behavior when declaring variables. Variables declared in Eval are actually created as properties without the DontDelete attribute.
Also, similarly, when called within function code:
eval('var foo = 1;');
foo; // 1
delete foo; // true
typeof foo; // "undefined"
})();
This is the basis for Firebug's abnormal behavior. All text in the console will be parsed and executed as Eval code, rather than global or function code. Obviously, all variables declared here will eventually become properties without the DontDelete attribute , so they can all be easily deleted. We need to understand the difference between this in global code and the Firebug console.
Delete variables via Eval:
This interesting eval behavior, coupled with another aspect of ECMAScript, could technically allow us to remove "non-deletable" attributes. The thing about function declarations is that they can override variables with the same name in the same execution context .
Notice how the function declaration takes precedence and overrides the variable of the same name (or, in other words, the same property in the variable object). This is because the function declaration is instantiated after the variable declaration, and is allowed Override them (variable declarations). A function declaration not only replaces the value of a property, it also replaces the attributes of that property. If we declare a function via eval, that function should replace it with its own attributes. attributes of the original (replaced) property. Also, since a variable declared via eval creates a property without the DontDelete attribute, instantiating this new function will actually remove the existing DontDelete attribute from the property, thus making A property can be deleted (and, obviously, point its value to the newly created function).
Unfortunately, this "cheat" doesn't work in any current implementation. Maybe I'm missing something here, or the behavior is just so obscure that implementers haven't noticed it .
Browser compatibility:
Understanding how things work in theory is useful, but practice is most important. Do browsers follow standards when it comes to the creation/deletion of variables/properties? The answer is: In most cases Down, yes.
I wrote a simple test set to test the browser's compatibility with the delete operator, including tests under global code, function code and Eval code. The test set checked the return value and attribute value of the delete operator Whether (as they should) actually be deleted. The return value of delete is not as important as its actual result. It doesn't really matter if delete returns true instead of false, what matters is that those who have the DontDelete attribute 's attributes are not deleted, and vice versa.
Modern browsers are generally quite compatible. Except for the eval feature I mentioned before, the following browsers passed all test sets: Opera 7.54, Firefox 1.0, Safari 3.1.2, Chrome 4.
Safari 2.x and 3.0.4 have problems deleting function parameters; these properties appear to be created without DontDelete, so they can be deleted. Safari 2.x has more problems - deleting non-references Type variables (eg: delete 1) will throw an exception; function declarations will create deleteable properties (but, strangely, variable declarations will not); variable declarations in eval will become non-deletable (but function declarations will not is deletable).
Similar to Safari, Konqueror (3.5, not 4.3) will throw an exception when deleting non-reference types (such as: delete 1), and mistakenly make function variables deletable.
Translator’s Note:
I tested the latest versions of chrome, firefox and IE, and basically kept the pass except for 23 and 24, which failed. I also tested UC and some mobile browsers, in addition to the built-in browser of Nokia E72. Except for Fail 15 and 16, most of the other built-in browsers have the same effect as desktop browsers. But it is worth mentioning that the built-in browser of Blackberry Curve 8310/8900 can pass test 23, which surprised me.
Gecko DontDelete bug:
Gecko 1.8.x browsers - Firefox 2.x, Camino 1.x, Seamonkey 1.x, etc. - exhibit a very interesting bug, explicit assignment to a property will delete its DontDelete Attribute, even if the attribute is created through a variable declaration or function declaration.
Surprisingly, Internet Explorer 5.5 - 8 passes the full set of tests, except that deleting non-reference types (eg: delete 1) throws an exception (just like old Safari). But under IE there is More serious bugs, it's not so obvious. These bugs are related to Global object.
IE bugs:
This whole chapter is about Internet Explorer bugs? Wow! That’s surprising!
In IE (at least IE 6-8), the following expression will throw an exception (when executed in global code):
this.x = 1;
delete x; // TypeError: Object doesn't support this action
This one does too, but throws a different exception, which makes things more interesting:
var x = 1;
delete this.x; // TypeError: Cannot delete 'this.x'
It looks like in IE, variable declarations in global code do not create properties on the global object. Creating a property via assignment (this.x = 1) and then deleting it via delete x will throw an error. By declaring to create the property (var x = 1) and then delete it by delete this.x throws another error.
But that’s not all. Creating a property via explicit assignment will actually cause an exception to be thrown on deletion. Not only is there an error here, but the property being created seems to have the DontDelete attribute, which of course is not Should have.
this.x = 1;
delete this.x; // TypeError: Object doesn't support this action
typeof x; // "number" (still exists, wasn't deleted as it should have been!)
delete x; // TypeError: Object doesn't support this action
typeof x; // "number" (wasn't deleted again)
Now, we would assume that under IE, undeclared assignments (which should create properties on the global object) do indeed create deletable properties.
x = 1;
delete x; // true
typeof x; // "undefined"
However, if you delete this attribute through this reference in the global code (delete this.x), a similar error will pop up.
x = 1;
delete this.x; // TypeError: Cannot delete 'this.x'
If we want to generalize this behavior, it seems that deleting a variable from global code using delete this.x never succeeds. When the property in question is created through explicit assignment (this.x = 1) delete throws an error; when the property is created by undeclared assignment (x = 1) or by declaration (var x = 1), delete throws another error.
delete x, on the other hand, should only throw an error if the property is created by explicit assignment - this.x = 1. If a property is created by declaration (var x = 1) , the deletion never occurs, and the deletion correctly returns false. If a property was created via an undeclared assignment (x = 1), the deletion works as expected.
I thought about this issue again in September, and Garrett Smith suggested that under IE,
"The global variable object is implemented as a JScript object, and the global object is implemented by the host".
Garrett used Eric Lippert's blog entry as a reference.
We can more or less confirm this theory by implementing some tests. Notice that this and window appear to point to the same object (if we can trust the === operator), but the variable object (function declaration The object where it is located) is different from the one pointed to by this.
getBase() === this.getBase(); // false
this.getBase() === this.getBase(); // true
window.getBase() === this.getBase(); // true
window.getBase() === getBase(); // false
Misunderstanding:
The beauty of understanding why things work the way they do cannot be understated. I've seen some misconceptions about the delete operator around the web. For example, this answer on Stackoverflow (with a surprisingly high rating), Confidence explained
"When the target operand is not an object property, delete should be a no-op".
Now that we understand the core of delete operation behavior, the error of this answer becomes obvious. delete does not distinguish between variables and properties (in fact, for delete, they are both reference types ) and actually only cares about the DontDelete attribute (and whether the attribute itself exists).
It's also very interesting to see the various misconceptions countered by each other, in the same thread one person first suggested just deleting the variable (which would have no effect unless it was declared in an eval), while another Provided a bug correction explaining how delete can be used to delete variables in global code, but not in function code.
Be extremely careful with the interpretation of JavaScript on the Internet, the ideal approach is to always understand the nature of the problem. ;)
delete and Host Object:
The algorithm of delete is roughly as follows:
If the operand is not a reference type, return true
If the object has no direct property with this name, returns true (as we know, the object can be an active object or a global object)
If the attribute exists but has the DontDelete attribute, return false
In other cases, delete the attribute and return true
However, the behavior of the delete operator on host objects is unpredictable. And there is actually nothing wrong with this behavior: (according to the standard), host objects are allowed to perform functions like read (internal [[Get]]), write (internal [[Put]] method) and delete (internal [[Delete]] method) several operators implement any behavior. This grace for custom [[Delete]] behavior is what changes the host object to Cause of confusion.
We've seen some IE quirks where deleting a specific object (obviously implemented as a host object) will throw an error. Some versions of Firefox will throw an error when deleting window.location. When operating You can't trust the return value of delete when the object is a host object. Let's see what happens in Firefox:
delete window.alert; // true
typeof window.alert; // "function"
Deleting window.alert returns true, even though there is absolutely no reason why this property should cause such a result. It will resolve to a reference (so it will not return true in the first step). This is a direct property of the window object (So it will not return true in the second step). So the only time delete can return true is to reach the fourth step and actually delete that attribute. However, this attribute is never deleted.
The moral of this story is: Never trust the host object.
ES5 strict mode:
So, what does strict mode ECMAScript 5 bring us? It introduces very few restrictions. Syntax errors occur when the expression of the delete operator is a direct reference to a variable, a function parameter or a function identifier will be thrown. Additionally, if the property has the internal attribute [[Configurable]] == false, a type error will be thrown.
Also, deleting undeclared variables (or unresolved references) will also throw a syntax error:
"use strict";
delete i_dont_exist; // SyntaxError
An undeclared assignment behaves similarly to an undeclared variable in strict mode (except this time it raises a reference error instead of a syntax error):
"use strict";
i_dont_exist = 1; // ReferenceError
As you can now understand, all the restrictions more or less make sense, since deleting variables, function declarations, and parameters causes so much confusion. Rather than silently ignoring deletions, strict mode takes a more aggressive and more Descriptive measures.
Summary:
This blog post ended up being quite long, so I’m not going to talk about things like deleting an array object with delete or what it means. You can refer to the MDC article for a dedicated explanation (or read the standard and Do your own experiments).
Here is a brief summary of how deletion works in JavaScript:
Variable and function declarations are properties of active objects or global objects
Attributes have some characteristics, among which DontDelete is the characteristic that determines whether the attribute can be deleted.
Variable and function declarations in global or functional code always create properties with the DontDelete attribute.
Function parameters are always properties of the active object, and have DontDelete.
Variables and functions declared in Eval code are always created without DontDelete properties.
New properties have no attributes when they are created (and of course no DontDelete).
The host object is allowed to decide how to react to the delete operation.
If you want to become more familiar with what is described here, please refer to the ECMA-262 3rd edition specification.
I hope you enjoyed this article and learned something new. Any questions, suggestions or corrections are welcome.