【问题标题】:Why does the JavaScript "delete" operator behave differently in different browsers?为什么 JavaScript “删除”操作符在不同的浏览器中表现不同?
【发布时间】:2014-06-24 23:17:21
【问题描述】:

我打算在 JavaScript 中使用 delete 操作符来做一些事情,当我决定通过玩一些它来提醒自己它是如何工作的时候。我知道“删除”应该用于对象属性,但我想看看它在变量上的行为。但是,当我将以下sn-p代码放在不同的浏览器中时,出现了一些奇怪的结果:

var one = 1;
delete one;
console.log(one); // prints out 1 in Chrome, but not in Safari or Firefox

在 Safari 中,JavaScript 控制台打印出错误“ReferenceError: Can't find variable: one”。 Firefox 给出了类似的响应:“ReferenceError: one is not defined”。但是,Chrome 打印出变量 1 的值。谁能解释为什么 Chrome 在这方面的行为与 Safari 和 Firefox 不同?

【问题讨论】:

  • 因为delete 不应该这样使用。 one 应该保留有 1 的值,因为 delete one 不应该做任何事情。引用MDN:delete is only effective on an object's properties. It has no effect on variable or function names.
  • 你为什么要删除一个变量?你想完成什么?
  • delete 运算符仅用于删除对象的属性,例如:delete someObject.foodelete someObject[propName]

标签: javascript google-chrome


【解决方案1】:

不要相信控制台。有些代码在那里表现不同。

如果你在真实页面上运行,下面会提示 1:

var one = 1;
delete one;
alert(one);

Demo

(在 Firefox、Chrome、IE、Safari 和 Opera 上测试。)


Understanding delete 解释了为什么控制台(或萤火虫)显示不同的行为:

每个执行上下文都有一个所谓的变量对象关联 用它。与执行上下文类似,变量对象是一个 抽象实体,一种描述变量实例化的机制。现在, 有趣的部分是在 a 中声明的变量和函数 源文本实际上是作为此变量对象的属性添加的

最后,在 Eval 代码中声明的变量被创建为属性 调用上下文的变量对象

当声明的变量和函数成为变量的属性时 object — 激活对象(用于功能代码)或全局 对象(对于全局代码),这些属性使用 DontDelete 创建 属性。但是,任何显式(或隐式)的属性分配 创建属性没有 DontDelete 属性。这是本质上 为什么我们可以删除某些属性,但不能删除其他属性。

那么在 Firebug 中会发生什么?为什么变量声明在 控制台可以删除,与我们刚刚学到的相反?好, 正如我之前所说,Eval 代码在涉及到 变量声明。在 Eval 代码中声明的变量实际上是 创建为没有 DontDelete 的属性

【讨论】:

  • 嗯,警报的行为与我的控制台相同。
  • @Ylluminarious console.logalert 应该具有相同的行为(我对其进行了更改以使其更明显)。不同之处在于在控制台或真实页面中运行代码。
  • 啊,好吧,我明白你在说什么了。
  • 我喜欢你发布的文章,所以我会为此给你 +1...但我觉得你没有真正解释为什么它的答案适用于这个问题。我也会修改我的。
【解决方案2】:

Let's checkout the docs on delete from MDN.

删除操作符从对象中删除一个属性。

您使用它的方式没有多大意义,即删除局部变量。

那些文档还说:

如果属性是自己的不可配置属性,则在严格模式下抛出(在非严格模式下返回 false)。在所有其他情况下返回 true。

这意味着您在此处的使用会在严格模式下引发异常,因为您以不受支持的方式使用 delete。当你这样做时,这一点得到了证明:

var one = 1;
delete one; // returns false

正如文档所述,false 的返回值意味着 delete 操作未成功。


如果你使用得当,它的行为应该和你期望的一样:

var obj = {one: 1};
delete obj.one;    // returns true
alert(obj.one);    // alerts "undefined"

【讨论】:

  • 是的,我知道您应该在对象属性上使用 delete;我或多或少地在玩它,看看它是如何与变量一起工作的。
  • 为什么他们的行为不同很难说。可能的实现细节、控制台行为差异或不完全遵守标准。但它真的重要吗?如果你做错了不同的浏览器会以不同的方式失败,那么不要做错:)
  • 没错,我可能不会做我计划做的事情,因为当我使用不当时它会表现得很奇怪。
  • 如果您的计划以“我想不正确地使用delete 运算符”开头,那么您可能需要一个新计划。
  • 在浏览器中,delete one; 是完全合法的代码,因为 one 不存在,这是完全合法的。 delete one = 真。这很好而且“正确”。
【解决方案3】:

我认为他正在寻找的答案,基本上之前有人说过,但通过编辑添加更多解释混淆了清晰度,因此应该重述:

当您以标准中未规定的方式使用方法时,结果是未定义的,因此因浏览器而异(因为每个浏览器本质上都有不同的 js 引擎)处理方式。

【讨论】:

  • 我不认为这是尊重标准的问题,而是控制台如何查看其代码相对于 DOM 的问题。浏览器控制台中 DOM 的根继承“行为”没有标准。因此,默认情况下,某些控制台不会将 window 视为父级。
【解决方案4】:

修改后的答案:

基于@Oriol 关于属性属性的反馈。我发现这里真正的问题是关于 Property Attributes(参见 ECMA-262 edition 5.1 section 8.6.1)和 Variable Environment 的执行上下文(参见 ECMA-262 edition 5.1 section 10.3

谁能解释为什么 Chrome 的行为与 Safari 不同? 火狐在这方面呢?

var one = 1;
delete one;
console.log(one); // Returns 1.. but why?

这里发生了两件事:

  1. var 声明将声明的对象绑定到与全局 (window) 明显不同的“执行上下文”。
  2. JavaScript 评估 [[Configurable]] 属性以确定其是否“可以”删除

关于#1

代码中的var 声明建立了一个VariableEnvironment,其中值绑定到与全局上下文截然不同的执行上下文(范围)中的对象。所以很自然,当不使用var 时,VariableEnvironment 会在全局执行绑定过程中进行解释,从而使one = 1;delete one; 之类的语句成为可能。

var one = 1; // Execution context #1 has a unique VariableEnvironment
delete one; // Execution context #2 has a global VariableEnvironment
console.log(one); // Return the value from 'var one'

这符合语言规范:

10.4 建立执行上下文

使用 eval 函数评估全局代码或代码 (15.1.2.1) 建立并进入一个新的执行上下文...

10.4.2 输入评估码

控制进入执行时执行以下步骤 评估代码的上下文:

如果没有调用上下文或者 eval 代码没有被调用 然后通过对 eval 函数的直接调用 (15.1.2.1.1) 进行评估, 初始化执行上下文,就好像它是全局执行一样 使用 eval 代码的上下文...

关于#2

Chrome 和JSFiddle 都在做正确的事情。原因与[[Configurable]] 属性有关,该属性在后台分配给本机和用户创建的属性。建立用户创建的属性时,此属性设置为true。这允许开发人员对属性执行分配和删除命令。

var test = {};
test.me = "OK" // [[Configurable]] is true so No Problem!
delete test.me // Good here too!

为了防止某些情况下不应删除或修改对象属性,[[Configurable]] 默认设置为false。尊重语言规范:

如果一个属性的值没有明确指定 命名属性的规范,表中定义的默认值 7用了……

[[可配置]] 错误

var test2 = [1,2,3];
console.log(test2.length); // length property is '3'
console.log(delete test2.length); // NOPE [[Configurable]] is false

函数范围内的函数参数也是如此:

(function foo(one) {
 console.log(delete one);
})(); // NOPE (false)

我们可以从这两个发现中得出什么结论?

由此我们可以了解到,Firefox 和 Safari 并不是不按规矩办事的。当var one=1; 在这些浏览器的控制台中声明时,此范围内的属性默认被错误地视为[[Configurable]],因此删除var one 而不是隐含的window.one

在 Firefox/Safari 中:

var one = 1; // var 'one'?
delete one; // NUKE var 'one'!
console.log(one); // ReferenceError: 'one' is not defined :'(

“好吧等等!那为什么delete one本身就是真的?

它的表现由语言规范 (10.4.2) 决定:

var one = 1; // VariableEnvironment not global or [[Configurable]]
delete one; // FALSE

...

delete one; // TRUE VariableEnvironment global and [[Configurable]]

...

var one = 1; // VariableEnvironment not global or [[Configurable]]
delete this.one; // TRUE VariableEnvironment is global and [[Configurable]]

【讨论】:

  • "我个人认为 jsfiddle 在这种情况下实际上是错误的":不,jsfiddle 中显示的行为遵循规范,所以它是好的。 “我的猜测是 jsfiddle 默认情况下不会将变量附加到父窗口”:当然,因为代码在 onload 事件侦听器中运行,而不是在全局上下文中 - 你可以在jsfiddle 选项,但由于vardelete 无论如何都不会工作。 “只是为了让事情变得更奇怪one = 1; alert(window.one)”:这与delete无关,并且是仅在非严格模式下允许的不良做法。
  • @Oriol 您可以将执行代码的加载状态更改为 jsfiddle 中的任何可用选项。他们都做同样的事情。 - 我展示的最后一个例子是为了说明one 被窗口吞噬了。它是。没有人声称它是好的代码(或任何与此相关的代码)。
猜你喜欢
  • 1970-01-01
  • 2018-01-09
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多