看书时,发明firefox 35.0下的掌握台或许firebug里,delete表现跟理论上不太一样,chrome表现则很正常,岂非firefox有题目?
搜了篇文章,看完才晓得题目出在那里,分享一下。Understanding delete
Theory
那末, 为何我们能够删除对象的属性:
var o = { x: 1 };
delete o.x; // true
o.x; // undefined
却不能删除如许声明的对象:
var x = 1;
delete x; // false
x; // 1
或许函数呢:
function x(){}
delete x; // false
typeof x; // "function"
注重: 当一个属性没法被删除时,delete操作符只会返回false
要明白这个, 我们起首须要掌握这些有关变量实例和属性特征的观点——这些观点很不幸地, 很少在JavaScript书中被说起. 我将试着在接下来的几个段落中简朴地温习一下这些观点. 这些观点是很难明白的!假如你不在乎”为何这些东西会以这类体式格局事情”的话,恣意跳过这一章节好了.
代码的范例:
在ECMAScript中, 有3种差别范例的可实行代码: 全局代码(Global code), 函数代码(Function code)和 Eval代码(Eval code). 这些范例从称号上来讲或多或少是有自诠释性的, 这里有一个简短的概述:
当一段源代码被算作顺序(Program)时, 它将会在全局环境下被实行, 而且被认为是全局代码(Global code). 在一个浏览器环境中, 剧本元素的内容一般被诠释为顺序, 因而被作为全局代码来实行.
任何直接在一个函数中实行的代码明显被认为是函数代码(Function code). 在浏览器中, 事宜属性的内容(如 <p onclick="....">)一般被诠释成函数代码.
末了, 被应用到内置函数eval的代码文本被诠释成Eval代码(Eval code). 很快我们会发明为何这类范例是特别的.
实行高低文(Execution context):
当ECMAScript代码实行时, 它一般会发作在特定的实行高低文中.实行高低文是一个有些笼统的实体观点, 它能协助明白局限(Scope)和变量实例(Variable instantiation)是怎样事情的. 对三种可实行代码的每一种, 都有一个实行高低文相对应. 当一个函数被实行的时刻, 我们说”顺序掌握进入了函数代码的实行高低文”; 当一段全局代码被实行时, 顺序掌握进入了全局代码的实行高低文, 等等.
正如你所见, 实行高低文能够在逻辑上组成一个客栈. 起首, 能够有一段全局代码和其本身的实行高低文, 然后这段代码能够会挪用一个函数, 并带着它(函数)的实行高低文. 这段函数能够挪用别的一个函数, 等等等等. 纵然函数是递归挪用的, 每次挪用时被也会进入一个新的实行高低文.
运动对象(Activation object) / 变量对象(Variable Object):
每一个实行高低文都有一个跟其所关联的所谓变量对象(Variable Object). 相似于实行高低文, 变量对象是一个笼统实体, 一种用来形貌变量实例的机制. 风趣的处所在于, 在源代码中声明的变量和函数一般会被当作属性(properties)增加到这个变量对象上.
当顺序掌握进入全局代码的实行高低文时, 一个全局对象(Global object)被用来作为一个变量对象. 这正是为何声明为全局的函数变量会变成全局对象属性的缘由.
/* remember that `this` refers to global object when in global scope */
var GLOBAL_OBJECT = this;
var foo = 1;
GLOBAL_OBJECT.foo; // 1
foo === GLOBAL_OBJECT.foo; // true
function bar(){}
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true
好, 所以全局变量会变成全局对象的属性, 然则局部变量(那些在函数代码中定义的变量)会发作什么呢? 实在它们的行动也异常相似: 它们会变成变量对象(Variable object)的属性. 唯一的差别在于, 当在函数代码中时, 一个变量对象并非全局对象, 而是所谓的运动对象(Activation object). 运动对象在会每次进入函数代码的实行高低文时被竖立.
并非只要在函数代码中声明的变量和函数会变成运动对象的属性; 这也会在每一个函数参数(对应响应的形式参数的称号)和一个特别的Arguments对象(以arguments为称号)上发作. 注重, 运动对象是一个内部形貌机制, 在顺序代码中并不能被接见.
(function(foo){
var bar = 2;
function baz(){}
/*
In abstract terms,
Special `arguments` object becomes a property of containing function's Activation object:
ACTIVATION_OBJECT.arguments; // Arguments object
...as well as argument `foo`:
ACTIVATION_OBJECT.foo; // 1
...as well as variable `bar`:
ACTIVATION_OBJECT.bar; // 2
...as well as function declared locally:
typeof ACTIVATION_OBJECT.baz; // "function"
*/
})(1);
末了, 在Eval代码中声明的变量会成为挪用者高低文(calling context)的变量对象的属性. Eval代码只是简朴地运用挪用它的代码的实行高低文的变量对象.
var GLOBAL_OBJECT = this;
/* `foo` is created as a property of calling context Variable object,
which in this case is a Global object */
eval('var foo = 1;');
GLOBAL_OBJECT.foo; // 1
(function(){
/* `bar` is created as a property of calling context Variable object,
which in this case is an Activation object of containing function */
eval('var bar = 1;');
/*
In abstract terms,
ACTIVATION_OBJECT.bar; // 1
*/
})();
属性的特征(property attributes)
我们几乎是已在这了. 既然我们已很清晰在变量上发作了什么(它们变成了属性), 唯一剩下的须要明白的观点就是属性的特征(property attributes)了. 每一个属性能够具有0个或多个特征, 它们从以下鸠合中拔取: ReadOnly, DontEnum, DontDelete和 Internal. 你能够把它们认为是flags —— 一种特征能够在属性中存在, 也能够不存在. 关于我们本日的议论来讲, 我们只对DontDelete感兴趣.
当被声明的变量和函数成为变量对象(或许函数代码的运动对象, 或全局代码的全局对象)的属性时, 这些属性在竖立时就带上了DontDelete的特征. 但是, 任何显式(或隐式)的属性赋值所竖立的属性将不会被带上DontDelete特征. 这就是为何我们能够删除一些属性, 但删除不了别的的.
var GLOBAL_OBJECT = this;
/* `foo` is a property of a Global object.
It is created via variable declaration and so has DontDelete attribute.
This is why it can not be deleted. */
var foo = 1;
delete foo; // false
typeof foo; // "number"
/* `bar` is a property of a Global object.
It is created via function declaration and so has DontDelete attribute.
This is why it can not be deleted either. */
function bar(){}
delete bar; // false
typeof bar; // "function"
/* `baz` is also a property of a Global object.
However, it is created via property assignment and so has no DontDelete attribute.
This is why it can be deleted. */
GLOBAL_OBJECT.baz = 'blah';
delete GLOBAL_OBJECT.baz; // true
typeof GLOBAL_OBJECT.baz; // "undefined"
内置对象和DontDelete
所以, 这就是有关它(DontDelete)的一切: 属性的一个特别特征, 用来掌握这个属性是不是能够被删除. 注重, 有些内置对象的属性是指定含有DontDelete的, 所以没法被删除. 如特别的arguments变量(或许, 正如我们如今所晓得的, 一个运动对象的属性)具有DontDelete. 函数实例的length属性也具有DontDelete属性.
(function(){
/* can't delete `arguments`, since it has DontDelete */
delete arguments; // false
typeof arguments; // "object"
/* can't delete function's `length`; it also has DontDelete */
function f(){}
delete f.length; // false
typeof f.length; // "number"
})();
函数参数所对应的属性也是从竖立最先就具有DontDelete特征的, 所以我们也没法删除它.
(function(foo, bar){
delete foo; // false
foo; // 1
delete bar; // false
bar; // 'blah'
})(1, 'blah');
未声明的赋值:
你能够还记住, 未声明的赋值会在全局对象上竖立一个属性, 除非这个属性已在这个作用域链中全局对象之前的别的处所被找到. 而且, 如今我们晓得属性赋值和变量声明的差别的处所——后者会设置DontDelete属性, 但前者不会. 我们必需清晰, 为何未声明的赋值会竖立一个可删除的属性.
var GLOBAL_OBJECT = this;
/* create global property via variable declaration; property has DontDelete */
var foo = 1;
/* create global property via undeclared assignment; property has no DontDelete */
bar = 2;
delete foo; // false
typeof foo; // "number"
delete bar; // true
typeof bar; // "undefined"
请注重: 特征是在属性被竖立时被决议的, 以后的赋值不会修正已存在属性的特征. 明白这一点区分异常重要.
/* `foo` is created as a property with DontDelete */
function foo(){}
/* Later assignments do not modify attributes. DontDelete is still there! */
foo = 1;
delete foo; // false
typeof foo; // "number"
/* But assigning to a property that doesn't exist,
creates that property with empty attributes (and so without DontDelete) */
this.bar = 1;
delete bar; // true
typeof bar; // "undefined"
Firebug的疑心:
在Firebug中发作了什么? 为何在console中声明的变量能够被删除, 这不是违犯了我们之前所学到的学问么? 嗯, 就像我之前所说的那样, Eval代码在面临变量声明时会有特别的表现. 在Eval中声明的变量实际上是作为不带DontDelete特征的属性被竖立的.
eval('var foo = 1;');
foo; // 1
delete foo; // true
typeof foo; // "undefined"
一样, 相似的, 当在函数代码中挪用时:
(function(){
eval('var foo = 1;');
foo; // 1
delete foo; // true
typeof foo; // "undefined"
})();
这就是Firebug失常行动的根据. 在console中的一切文本都邑被当作Eval代码来剖析和实行, 而不是全局或函数代码. 明显, 这里声明的一切变量末了都邑成为不带DontDelete特征的属性, 所以它们都能被轻松删除. 我们须要相识这个在全局代码和Firebug掌握台之间的差别.
经由过程Eval来删除变量:
这个风趣的eval行动, 再加上ECMAScript的另一个方面, 能够在技术上许可我们删除”non-deletable”的属性. 有关函数声明的一点是, 它们能够掩盖雷同实行高低文中同名的变量.
function x(){ }
var x;
typeof x; // "function"
注重函数声明是怎样取得优先权而且掩盖同名变量(或许, 换句话说, 在变量对象中的雷同属性)的. 这是因为函数声明是在变量声明以后被实例化的, 而且被许可掩盖它们(变量声明). 函数声明不仅会替代掉一个属性的值, 它还会替代掉谁人属性的特征. 假如我们经由过程eval来声明一个函数, 谁人函数就应该会用它本身的特征来替代掉原有的(被替代的)属性的特征. 而且, 因为经由过程eval声明的变量会竖立不带DontDelete特征的属性, 实例化这个新函数将会实际上从属性中删除已存在的DontDelete特征, 从而使得一个属性能够被删除(而且, 明显会将其值指向新竖立的函数).
var x = 1;
/* Can't delete, `x` has DontDelete */
delete x; // false
typeof x; // "number"
eval('function x(){}');
/* `x` property now references function, and should have no DontDelete */
typeof x; // "function"
delete x; // should be `true`
typeof x; // should be "undefined"
不幸的是, 这类”诳骗”在现在的任何完成中都不起作用. 或许我在这漏掉了什么, 或许是这类行动只是太艰涩了以至于完成者都没有注重到它.