删除JavascriptObject中间的key
这个也不会,回家种⽥去吧你
复制代码代码如下:
delete thisIsObject[key]
or
delete thisIsObject.key
顺便我们来谈谈delete的⽤法
⼏个礼拜前, 我有了个机会去翻阅Stoyan Stefanov的 Object-Oriented Javascript ⼀书. 这本书在亚马逊上拥有很⾼的评价(12篇评论, 5颗星), 所以我很好奇地想看看它到底是不是那么值得推荐的⼀本书, 于是我开始阅读函数的那章. 我⾮常欣赏这本书解释事物的⽅式, 例⼦们被以⼀种⾮常漂亮, 渐进的⽅式被组织起来, 看起来即便是初学者也能够轻松掌握这些知识. 然⽽, ⼏乎是⽴刻, 我就发现了⼀个贯穿整个章节的有趣的误解——删除功能函数. 另外还有⼀些其它错误(例如函数声明与函数表达式的区别),但是我们⽬前将不去讨论它们.
这本书声称:
"函数被作为像⼀般变量⼀样对待-它可以被复制到不同的变量中, 甚⾄被删除". 在这个解释后⾯附加了这样⼀段⽰例:
复制代码代码如下:
var sum = function(a, b) {return a + b;}
var add = sum;
delete sum
true
typeof sum;
"undefined"
忽略掉⼀些漏掉的分号, 你能看出这⼏句代码的错误在哪么? 显然, 错误在于删除sum这个变量的操作是不会成功的. delete表达式不应该返回true, 并且 typeof sum也不应该返回"undefined". 这⼀切都因为
在JavaScript中删除变量是不可能的. ⾄少, 在这种声明⽅式下是不可能的.
所以, 在这个例⼦中到底发⽣了什么? 它是⼀个错误么? 抑或是⼀个特殊⽤法? ⼤概不是这样的. 这⼀段代码事实上是Firebug控制台中的真实输出, Stoyan⼀定是使⽤了它作为快速测试的⼯具. 这⼏乎就好像是Firebug遵守了其它⼀些delete的规则⼀样. 是Firebug导致了Stoyan误⼊歧途! 所以, 这⼉到底发⽣了什么?
在回答这个问题之前, 我们⾸先需要理解delete运算符到底在JavaScript中是如何⼯作的: 到底什么能够被删除, 什么不能够被删除? 今天, 我将尝试着详细解释这个问题. 我们将看看Firebug的"奇怪"⾏为并且意识到它其实并不是那么奇怪. 我们将深⼊了解在声明变量, 函数, 给属性赋值和删除它们的这些场景背后到底隐藏了什么. 我们将看看浏览器的兼容性和⼀些最臭名昭著的bug. 我们还将讨论ES5的严格模式, 和它如何改变delete操作符的⾏为.
我将交换着使⽤JavaScript和ECMAScript, 它们都意味着ECMAScript(除⾮明显地谈论Mozilla的JavaScript实现)
不出所料, 在⽹络上, 对delete的解释是相当稀缺的. MDC article⼤概是最好理解的资源了, 但是, 不幸的是, 它缺失了这个主题的⼀些有趣的细节. 奇怪的是, 其中⼀个被遗忘的东西就是Firebug的奇怪表现的原因. ⽽MSDN reference在这些⽅⾯⼏乎是⽆⽤处的.
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; // truejs arguments
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 声明的变量会创建不带Dont
Delete特性的属性, 实例化这个新函数将会实际上从属性中删除已存在的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"
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论