删除Javascript Object中间的key(6)
/* in Global code */
function getBase(){ return this; }
getBase() === this.getBase(); // false
this.getBase() === this.getBase(); // true
window.getBase() === this.getBase(); // true
window.getBase() === getBase(); // false
误解:
理解事物为何以那种方式工作的美是不可低估的. 我在网络上见过一些有关delete操作符的误解. 例如, 这个Stackoverflow上的答案(拥有令人吃惊的高rating), 自信地解释道
"当目标操作数不是一个对象属性时, delete应当是无操作的".
现在既然我们已经理解了delete操作行为的核心, 这个答案的错误也就变得显而易见了. delete并不会去区分因为变量和属性(事实上, 对于delete来说, 它们都是引用类型)并且事实上只关心DontDelete特性(和属性本身是否存在).
看到各种误解互相反驳也是非常有趣的, 在一个相同的话题中一个人首先建议只delete变量(这将不会有效果, 除非它是在eval中声明的), 而另一个人提供了一个错误的纠正说明delete是如何在全局代码中用于删除变量, 而在函数代码中却不行.
对于网络上JavaScript的解释要格外小心, 理想的方法是总去理解问题的本质. ;)
delete和宿主对象(Host Object):
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). 这是个window对象的直接属性(所以不会在第二步返回true). 所以delete唯一能够返回true的情况就是到达第四步并且真正删除那个属性. 然而, 这个属性从未被删除.
这个故事的寓意是: 永远不要相信宿主对象.
ES5 严格模式:
所以, 严格模式的ECMAScript5给我们带来了什么呢? 它介绍了很少的一些限制. 当delete操作符的表达式是一个变量的直接引用, 函数参数或者函数标示符时, 语法错误将会被抛出. 另外, 如果属性具有内部特性[[Configurable]] == false, 则一个类型错误将会被抛出.
(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
})();
另外, 删除未声明的变量(或者说未解析的引用)将也会抛出语法错误:
"use strict";
delete i_dont_exist; // SyntaxError
未声明的赋值跟严格模式下未声明的变量所表现的行为类似(除了这次是引发引用错误而不是语法错误):