Javascript教程:delete删除对象(7)
误区
理解事物为什么那么工作是一种难以言说的美,我在网上已经看到了与delete
运算符误解相关的误区。例如,在关于栈溢出的回答(评分出其不意的效果高)中,它自信的解释道:“delete is supposed to be no-op when target isn’t an object property ”。现在,我们已经理解了delete
行为的核心,很清楚这个答案是不准确的。delete
不区分变量和属性(事实上,对于删除,这些都是引用),真正的只关心的是DontDelete特性(和属性存在)。
非常有意思的看到这个误解如何相互影响,在同样一个线程中,有人首先提出要直接删除变量(除非它是在eval
中声明,否则不会生效),接着另外一个人提出一种错误的纠正方法--在全局中可能删除变量,但在函数内不行。
在网站上解释Javascript 最好小心,最好总是抓住问题的核心。
‘delete’和宿主对象
delete
的算法大概是这样:
- 如果操作不是一个引用,返回
true
; - 如果一个对象没有直接的属性,返回
true
;(我们知道,对象可以是激活对象,可以是全局象); - 如果一个属性存在并有DontDelete特性,返回
false
; - 否则,删除属性并返回
true
;
但是,宿主对象的delete
运算符的行为难以预测。实际上并没有错:除了少数几个,宿主对象是允许执行任何类型的运算行为的(按规范),如read(内部的[get]方法)、write(内部的[put]方法)或delete
(内部的[delete]方法)。这个定制的[[Delete]]行为使得宿主对象如此混乱。
在IE中我们已经看到一些古怪的行为,如果删除某些对象(明显作为宿主对象来执行)将抛出错误。Firefox的一些版本在尝试删除window.location
时将抛出错误。当涉及到宿主对象时,你不能信任delete
返回的任何值。看看在Firefox会有什么发生:
/* "alert" is a direct property of `window` (if we were to believe `hasOwnProperty`) */
window.hasOwnProperty(
'alert'
);
// true
delete
window.alert;
// true
typeof
window.alert;
// "function"
删除window.alert
返回true
,虽然这个属性什么也没有,它应该导致这个结果。它保留了一个引用(因此在第一步中不应该返回true
),它是窗口对象的直接属性(因此第二步中不能返回true)。唯一的办法让delete
返回true
是在第四步之后真正删除属性。但是,属性是永远不会被删除的。
这个故事的寓意在于永远不要相信宿主对象
ES5严格模式
那么,ECMAScript 5th edition 的严格模式可以拿到台面上来了。一些限制正被引入,当delete
运算符是一个变量、函数参数或函数标识符的直接引用时将抛出SyntaxError。另外,如果属性内部有[[Configurable]] == false,将抛出TypeError。
(
function
(foo){
"use strict"
;
// enable strict mode within this function
var
bar;
function
baz(){}
delete
foo;
// SyntaxError (when deleting argument)
delete
bar;
// SyntaxError (when deleting variable)
delete
baz;
// SyntaxError (when deleting variable created with function declaration)
/* `length` of function instances has { [[Configurable]] : false } */
delete
(
function
(){}).length;
// TypeError
})();
另外,删除未声明的变量(换句话说,没有找到的引用)也抛出SyntaxError。
"use strict"
;
delete
i_dont_exist;
// SyntaxError
正如你所理解的那样,考虑到删除变量、函数声明和参数会导致如此多得混淆,所有这些限制就有点意义。与不声不响的忽略删除行为相反,严格模式应该采取更积极的、更具有描述性的措施。