In this article, the author tells about the delete error in the book "JavaScript Object-Oriented Programming Guide" and details the implementation of the delete operation, limitations and performance in different browsers and plug-ins (firebug here.
The main part of the translation is as follows.
... Claims in the book
"A function is like a common variable -- it can be copied to different variables or even deleted"
The following code snippet is attached as a description:
Copy codeThe Code is as follows: >>> var sum = function (a, B) {return a + B ;};
>>> Var add = sum;
>>> Delete sum;
True
>>> Typeof sum;
"Undefined"
Can you find problems in the clips? This problem is: The operation to delete the sum variable should not be successful; the declaration of delete should not return true, and the typeof sum should not be returned as undefined. Because the variables cannot be deleted in javascript, at least they cannot be declared and deleted in this way.
So what happened in this example? Is it a print error or joke? It should not. This fragment is an actual output in the firebug console, and Stoyan (author of the book mentioned above) should be used for rapid testing. It seems that firebug has different deletion rules. It was firebug that misled Stoyan! So what is going on here?
To answer this question, we need to know how the delete operator works in Javascript: which can be deleted, which cannot be deleted, and why. I will try to explain the details below. We will observe firebug's "strange" performance and realize that it is actually not "strange" at all; we will gain an in-depth understanding of those, when we declare variables, functions, when assigning attributes and deleting them, the details are hidden behind them. Let's take a look at the implementation of the browser and some famous bugs; we will also discuss strict mode in ECMAScript version 5 and how it modifies the delete operator.
The Javascript and ECMPScript that I use alternately below generally refer to ECMAScript (unless explicitly speaking of Mozilla's JavaScript implementation ).
Unexpectedly, there are currently few interpretations of delete on the Internet (I wrote this article in January 2010 ). MDC (MDN]) resources are probably the most detailed, but unfortunately it misses some interesting details, including the strange performance of the above firebug. The MSDN documentation is almost useless.
I. Theory | Theory
Why can we delete the attributes of an object:
Copy codeCode: var x = {a: 1 };
Delete x. a; // true
X. a; // undefined
However, you cannot delete a variable:Copy codeCode: var x = 1;
Delete x; // false;
X; // 1
You cannot delete a function either:Copy codeThe Code is as follows: function x (){};
Delete x; // false;
Typeof x; // "function"
Note: delete returns false only when a property cannot be deleted.
To understand this, we need to first grasp some concepts: variable instantiation and property attributes: for details about the differences between property and attributes, refer to the Article. According to the content involved below, it is intended to be translated into internal attributes. These are rarely mentioned in javascript. In the following paragraphs, I will try to review these content briefly. It is not difficult to understand them. If you are not concerned about the reasons behind their performance, skip this chapter.
1.1. code Type | Type of code
ECMAScript has three types of executable code:
1. Global code
2. Function code
3. Eval code
The meanings of these classes are roughly the same as they are named, but let's review them quickly:
1. When a source file is viewed as a program, it is executed in the Global scope, and this is considered as a Global code. In the browser environment, the content of the SCRIPT element is usually parsed into a program and executed as a global code.
2. of course, any code directly executed in a Function is considered as a Function code. In the browser environment, the content of the event attribute (e.g. <a onclick = "... ") is usually parsed and executed as function code.
3. Finally, the code in the built-in function eval is parsed as the Eval code. We will soon see why this type is special.
1.2. Context of code Execution | Execution Context
When ECMAScript code is executed, it always occurs in a definite execution context. The execution scope is an abstract entity that helps you understand the working principles of the scope and variable instantiation. The preceding three types of executable code have their respective execution contexts. When the function code is executed, we say that the control end enters the execution context of the Function Code; when the global code is executed, we say that the control end enters the execution context of the global code, and so on.
As you can see, the execution context is logically a stack ). First, there may be a piece of global code, which has its own execution context; in this code, a function may be called, which also has its own execution context; this function may call another function, and so on. Even when a function calls itself recursively, different execution contexts are still entered in each call.
1.3. Activation object and Variable object | Activation object/Variable object
Each execution context has a Variable object associated with it ). Similar to it, a variable object is also an abstract entity, a mechanism used to describe variable instantiation. Interestingly, the variables and functions declared in a piece of source code are actually added to the Variable object as properties of the Variable object.
When the control enters the execution context of the global code, a global object is used as a variable object. This is precisely why the declared variables and functions become attributes of a Global Object:
Copy codeThe Code is as follows: var GLOBAL_OBJECT = this;
Var foo = 1;
GLOBAL_OBJECT.foo; // 1
Function bar (){};
Typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar ==bar; // true
OK, so the global variable becomes the attribute of the global Function, so what about the local variable -- the variables declared in the Function code? In fact, it is very simple: they also become attributes of variable objects. The only difference is that in function code, a variable object is not a global object, but an Activation object ). Each time you enter the execution context of the function code, an activation object is created.
Not only variables and functions declared in the Function Code become the attributes of the activated object: Each real parameter of the function (arguments, with the corresponding parameter names as the attribute names ), A special Arguments object (with arguments as the attribute name) also becomes the attribute of the activated object. Note that the activated object, as an internal mechanism, cannot be accessed by program code.
Copy codeThe Code is as follows: (function (foo ){
Var bar = 2;
Function baz (){};
/*
In the abstract process,
The special 'arguments' object becomes the property of the activated object of the function:
ACTIVATION_OBJECT.arguments = arguments;
... The same is true for the parameter 'foo:
ACTIVATION_OBJECT.foo; // 1
... The variable 'bar' is also the same:
ACTIVATION_OBJECT.bar; // 2
... The same is true for the 'baz' function:
Typeof ACTIVATION_OBJECT.baz; // "function"
*/
}) (1 );
Finally, the variables declared in Eval code become attributes of the context Variable object. The Eval code simply uses the variable object in the execution context in its call.
Copy codeThe Code is as follows: var GLOBAL_OBJECT = this;
Eval ('var foo = 1 ');
GLOBAL_OBJECT.foo; // 1;
(Function (){
Eval ('var bar = 2 ');
/*
In the abstract process
ACTIVATION_OBJECT.bar; // 2
*/
})();
1.4. Internal attribute | Property attributes
It's close to the topic. Now we have made it clear what happens to variables (they become attributes), and the concept to be understood is the internal attribute (property attributes) of the attributes ). Each attribute has zero to multiple Internal attributes, such as * ReadOnly, DontEnum, DontDelete, and Internal **. You can think of them as tags-a property may have or may not have a special internal property. In today's discussion, we are interested in DontDelete.
When declaring variables and functions, they become Variable objects-either activated objects (in Function Code) or global objects (in global code) -- attributes. These attributes are generated along with the internal attribute DontDelete. However, do not generate DontDelete for any explicit/implicit values. This is why we can delete some attributes but cannot delete others.
Copy codeThe Code is as follows: var GLOBAL_OBJECT = this;
/* 'Foo' is an attribute of the global object,
It is generated through variable Declaration, so it has the internal attribute DontDelete
This is why it cannot be deleted */
Var foo = 1;
Delete foo; // false
Typeof foo; // "number"
/* 'Bar' is an attribute of a global object,
It is generated through variable Declaration, so it has the DontDelete sub-
This is why it cannot be deleted */
Function bar (){};
Delete bar; // false
Typeof bar; // "function"
/* 'Baz' is also an attribute of the global object,
However, it is generated by assigning values to attributes, so there is no DontDelete
This is why it can be deleted */
GLOBAL_OBJECT.baz = "baz ";
Delete GLOBAL_OBJECT.baz; // true
Typeof GLOBAL_OBJECT.baz; // "undefined"
1.5 building and DontDelete | Build-ins and DontDelete
So this is the reason for all this: a special internal attribute of an attribute controls whether the attribute can be deleted. Note: Some attributes of the built-in object have internal attributes DontDelete, so they cannot be deleted. Special arguments variables (as we know, attributes of the activated object) have DontDelete; the length attribute of any function instance (return parameter length) also has DontDelete:
Copy codeThe Code is as follows: (function (){
// You cannot delete 'arguments' because DontDelete exists.
Delete arguments; // false;
Typeof arguments; // "object"
// The length of the function cannot be deleted because DontDelete exists.
Function f (){};
Delete f. length; // false;
Typeof f. length; // "number"
})();
Attributes associated with the function arguments also have DontDelete and cannot be deleted.
Copy codeThe Code is as follows: (function (foo, bar ){
Delete foo; // false
Foo; // 1
Delete bar; // false
Bar; // "bah"
}) (1, "bah ");
1.6 Undeclared variable assignment | Undeclared assignments
You may remember that un-declared variable assignments will become attributes of the global object, unless this attribute is found elsewhere in the scope chain. Now we know the difference between attribute assignment and variable declaration-the latter generates DontDelete and the former does not -- That is why undeclared variable assignment can be deleted.
Copy codeThe Code is as follows: var GLOBAL_OBJECT = this;
/* Generate the attributes of the global object through the variable declaration, with DontDelete */
Var foo = 1;
/* Generate the attributes of the global object through the undeclared variable assignment, without the DontDelete */
Bar = 2;
Delete foo; // false
Delete bar; // true
Note: The internal attribute is determined when the attribute is generated. The subsequent value assignment process does not change the internal attribute of the existing attribute. Understanding the difference is important.
/* Create 'foo' and generate DontDelete */
Function foo (){};
/* The subsequent value assignment process does not change the internal attributes of an existing attribute, and DontDelete still exists */
Foo = 1;
Delete foo; // false;
Typeof foo; // "number"
/* If a value is assigned to a non-existent attribute, an attribute without an internal attribute is created, so there is no DontDelete */
This. bar = 1;
Delete bar; // true;
Typeof bar; // "undefined"
Ii. Chaos in Firebug | Firebug confusion
So what happened in firebug? Why can the variables declared on the console be deleted, not as we discussed earlier? As I said before, Eval code has a special behavior when processing variable declarations: the variables declared in Eval code actually generate an attribute without DontDelete.
Copy codeCode: eval ('var foo = 1 ;');
Foo; // 1
Delete foo; // true
Typeof foo; // "undefined"
The same is true in function code:
Copy codeThe Code is as follows: (function (){
Eval ('var foo = 1 ;');
Foo; // 1
Delete foo; // true
Typeof foo; // "undefined"
})();
This is the cause of abnormal behavior in Firebug. All debugging texts in the console seem to be compiled and executed using Eval code, rather than being executed in global or function code. Obviously, all the variable declarations generate attributes without DontDelete, so they can be deleted. So be careful about the differences between common global code and the Firebug console code.
2.1 Delete a variable through eval | Delete variables via eval
This interesting eval action, combined with another aspect of ECMAScript, allows us to technically Delete attributes that cannot be deleted. This is about function declaration-in the same execution context, they can overwrite variables with the same name:
Copy codeThe Code is as follows: function x (){};
Var x;
Typeof x; // "function"
So why does a function declaration have priority and can overwrite variables of the same name (or, in other words, the same attribute of a Variable object? This is because the instantiation process of the function declaration can overwrite them after the variable declaration.
The function declaration can only overwrite variables with the same name that are declared but not assigned a value. If a value is assigned during the declaration (e.g. var x = 1) After the function initialization, the function declaration is overwritten by the value assignment, as shown below :)
Copy codeCode: var x = 1;
Function x (){};
Typeof x; // "number"
The function declaration not only replaces the attribute value, but also its internal attribute. If we declare a function through eval, this function will also replace the previous one with its own internal attributes. Since the attribute generated by the variable declared in eval does not have DontDelete, instantiating this function will remove the existing DontDelete internal attribute of the original attribute in "theory, this attribute can be deleted (of course, it also points to the newly generated function ).
Copy codeCode: var x = 1;
/* Cannot be deleted. 'X' has DontDelete */
Delete x; // false
Typeof x; // "number"
Eval ('function x (){}');
/* Attribute 'X' points to the function now, and there should be no DontDelete */
Typeof x; // "function"
Delete x; // It should be 'true ';
Typeof x; // it should be "undefined"
Unfortunately, this spoofing technique was not successful in all browsers I tried. I may have missed something here, or this behavior is so concealed that browsers didn't notice it.
(Translator's note: The problem here may be that the overwrite between the function declaration and the variable declaration is only a change in the value direction, while the internal attribute DontDelete is determined at the initial declaration and will not be changed, the variables and functions declared in eval can be deleted only when they are not declared in their external context. With regard to the execution sequence, as eval acts as a function, its call is always after other variables and function declarations in its external context, so the relevant internal attributes have been determined, only the point of the value is overwritten. As follows :)
Copy codeThe Code is as follows:/* The first alert returns "undefined" because the value assignment process is after the declaration process and eval execution process;
The second alert returns "false" because although the position declared by x is after eval,
However, the eval execution cannot be deleted after the variable Declaration */
Eval ('alert (x); alert (delete x )');
Var x = 1;
Iii. browser compliance | Browsers compliance
Understanding how things work is important, but the actual implementation is more important. Do browsers comply with these standards when creating and deleting variables/attributes? For most of them, yes.
I wrote a simple test unit to check the compliance of Global Code, function code, and Eval code. The test unit also checks whether the return values and attributes of the delete operation are deleted as expected. The return value of a delete operation is not as important as the actual result. It is not important to return true or false for the delete operation. It is important to check whether the attribute with/without DontDelete is deleted.
Modern browsers generally follow deletion rules. All of the following browsers passed the test: Opera 7.54 +, Firefox 1.0 +, Safari 3.1.2 +, and Chrome 4 +.
Safari 2.x and 3.0.4 have problems when deleting the function arguments. It seems that these attributes do not contain DontDelete and can be deleted. Safari 2.x has other problems-an error is thrown when no reference is deleted (for example, delete 1 ); the function declaration generates deletable attributes (the strange thing is that the variable declaration is normal); the variable declaration in eval becomes non-deletable (and the function declaration in eval is normal ).
Similar to Safari, Konqueror (3.5, not 4.3) does not reference or delete arguments in delete.
3.1. Gecko DontDelete bug
Gecko 1.8.x browser -- Firefox 2.x, Camino 1.x, Seamonkey 1.x, etc. -- an interesting bug exists: explicitly assigning a value to an attribute can remove its DontDelete, even if the attribute is generated through a variable or function declaration.
Copy codeThe Code is as follows: function foo (){};
Delete foo; // false;
Typeof foo; // "function"
This. foo = 1;
Delete foo; // true
Typeof foo; // "undefined"
Surprisingly, the IE5.5-8 also passed a vast majority of tests, except for removing non-reference throws (e.g. delete 1, like the old Safari ). However, although it cannot be found immediately, in fact, IE has more serious bugs, which are about global objects.
Iv. IE bugs
In IE (at least in IE6-8), the following expression throws an exception (in Global Code ):
Copy codeThe Code is as follows: this. x = 1;
Delete x; // TypeError: Object doesn' t support this action
The following is another one:
Copy codeCode: var x = 1;
Delete this. x; // TypeError: Cannot delete 'this. X'
// Press the Translator's note: This exception is thrown under IE8, and the exception thrown under IE6 and 7 is the same as the exception above.
It seems that the variable declaration in the global code in IE does not generate the same name attribute of the global object. Attributes created by assigning values (this. x = 1) then an exception is thrown when delete x is used to delete the object. Attributes created through the variable Declaration (var x = 1) are then deleted through the delete this. x will throw another one when deleting (the translator's press: the error message in IE6 and 7 is the same as the preceding one ).
This is not the case. In fact, attributes created by explicit assignment always throw an exception during deletion. This is not just an error, but the created attribute seems to have the internal attribute of DontDelete, which should not be based on rules:
Copy codeThe Code is as follows: this. x = 1;
Delete this. x; // TypeError: Object doesn't support this action
Delete x; // TypeError: Object doesn' t support this action
On the other hand, undeclared variable values (those attributes that also generate global objects) can be deleted normally in IE:
Copy codeThe Code is as follows: x = 1;
Delete x; // true
However, if you try to use the this keyword to delete (delete this. x), the above exception will be thrown again:
Copy codeThe Code is as follows: x = 1;
Delete this. x; // TypeError: Cannot delete 'this. X'
To sum up, we will find that 'delete this. X' in the global code will never succeed. When an explicit value assignment is used to generate an attribute (this. x = 1) throws an exception. When an attribute is generated by declaring/not declaring a variable (var x = 1 or x = 1), another exception is thrown. On the other hand, delete x throws an exception only when the property (this. x = 1) generated by the value assignment is displayed.
In September, I discussed this issue. Garret Smith believes that the Global variable object in IE is implemented as a JScript object, while the Global object is implemented by the Host object.
We can validate this theory to some extent through several tests. Note that this and window seem to reference the same object (if the '=' operator can be trusted), while the Variable object (Basis of function declaration) is different from the one referenced by this.
Copy codeThe Code is as follows: function getBase () {return this ;};
GetBase () === this. getBase (); // false
This. getBase () === this. getBase (); // true
Window. getBase () === this. getBase (); // true
Window. getBase () === getBase (); // false
V. Misunderstanding | Misconceptions
We cannot underestimate the importance of understanding how things work. I have read some misunderstandings about delete operations on the network. For example, an answer on Stackoverflow (with a high level) explains "delete is supposed to be no-op when target isn' t an object property ". Now that we understand the core of the delete operation, we know that this answer is incorrect. Delete does not distinguish between variables and attributes (in fact, these are references in the delete operation), but only cares about DontDelete (and whether the attribute already exists ).
6. 'delete' and host object | 'delete' and host object
A delete algorithm is roughly like this:
1. If the operator (operand) is not a reference, true is returned.
2. If the object does not have a ** direct property ** with the same name, true is returned (as we know, the object can be a global object or an activated object)
3. If the attribute already exists but there is DontDelete, false is returned.
4. Otherwise, remove the property and return true.
However, the delete operation on the host object may be unpredictable. In fact, there is nothing wrong with this: The Host Object (through certain rules) allows for any operation, such as the read (internal [[Get] method), Write (internal [Write] method), Delete (internal [[Delete] method), and so on. This allowed custom [[Delete] behavior causes confusion of the Host object.
We have seen some problems in IE: An exception is thrown when some objects (which are implemented as host objects) are deleted. Some versions of firefox throw an exception when trying to delete window. location (the translator presses: IE ). Similarly, you cannot trust the return value of delete in some host objects. For example, the following occurs in firefox (the translator presses the same result in chrome; an exception is thrown in IE; opera and safari allow deletion and cannot be called after deletion. It is 'normal'. Although it seems abnormal from the discussion below, they actually Delete attributes that cannot be deleted, but not in the browser above ):
Copy codeThe Code is as follows:/* 'alert 'is a direct attribute of 'window' (if we can trust 'hasownproperties ')*/
Window. hasOwnProperty ('alert '); // true
Delete window. alert; // true
Typeof window. alert; // "function"
Delete window. alert returns true, although this property does not have any conditions to produce this result (according to the above algorithm): it is resolved as a reference, so it cannot return true in the first step; it is a direct attribute of the window object, so true cannot be returned in the second step; the only thing that returns true is that when the algorithm reaches the last step, it does Delete this attribute, in fact, it is not deleted. (Press: no, it is indeed deleted in opera and safari ...).
So this story tells us never trust the Host object.
7. ES5 strict mode | ES5 strict mode
So what will the strict mode in ECMAScript 5th bring? Some restrictions are introduced. When the delete operation points to a direct reference of a variable, function parameter, or function declaration, a SyntaxError is thrown. In addition, if the attribute has an internal attribute [[retriable] = false, A TypeError is thrown:
Copy codeThe Code is as follows: (function (foo ){
"Use strict"; // enable the strict mode in the function
Var bar;
Function baz;
Delete foo; // SyntaxError. When you delete a function parameter
Delete bar; // SyntaxError. When a variable is deleted
Delete baz; // SyntaxError. When a variable created by the function declaration is deleted
/* The length of the function instance has [[retriable]: false */
Delete (function () {}). length; // TypeError
})();
Moreover, in strict mode, deleting undeclared variables (in other words, unresolved references) also throws SyntaxError; similar to it, an error (ReferenceError) is thrown when an unspecified value is assigned in the same mode)
Copy codeThe Code is as follows: "use strict ";
Delete I _dont_exist; // SyntaxError
I _dont_exist_either = 1; // ReferenceError
After reading the examples of variables, function declarations, and parameters, I believe you have understood these limitations. Strict models adopt more positive and descriptive measures, instead of simply ignoring these issues.
8. Summary | Summary
Since this article has been very long, I will not discuss other content (e.g. delete the array items and their impact ). You can read articles on MDC/MDN or read the specifications and test them by yourself.
The following is a simple summary of how delete works in Javascript:
• Both variables and function declarations are attributes of the Activation Global object.
• An attribute has an internal attribute, and one -- DontDelete is used to determine whether an attribute can be deleted.
• Variables and function declarations in global code or function code generate the DontDelete attribute.
• Function parameters are also attributes of the activated object and also have DontDelete.
• All variables and function declarations in Eval code generate attributes without DontDelete.
• The new undeclared attribute contains an empty internal attribute during generation, so there is no DontDelete.
• The host objects can respond to the deletion process in any way they wish.
Original article: Understanding delete Translation: delete in javascript Translator: justjavac