计算机服务器

这个也不会,回家种田去吧你

一、问题的提出 我们先来看看下面几段代码,要注意的是,以下代码不要在浏览器的开发者工具(如FireBug、Chrome Developer tool)中运行,原因后面会说明: 为什么我们可以删除对象的属性: 复制代码 代码如下: 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操作符返回true时表示可以删除,返回false表示不能删除 要理解这一点,我们首先需要掌握像变量实例化和属性特性这样的概念--遗憾的是这些内容在一些javascript的书中很少讲到。理解它们并不难,如果你不在乎它们为什么这么运行,你可以随意的跳过这一部分。 二、代码类型 在ECMAScript中有三种类型的可执行代码:Global code、Function code和 Eval code。 复制代码 代码如下: var x=1;//Global code function test(){ var y=2;//Function Code eval;//Eval Code in Function } eval{}");//Eval Code in Global 三、执行上下文 当ECMAScript 代码执行时,它总是在一定的上下文中运行,执行上下文是一个有点抽象的实体,它有助于我们理解作用域和变量实例化如何工作的。对于三种类型的可执行代码,每个都有执行的上下文。当一个函数执行时,可以说控制进入到函数代码的执行上下文。全局代码执行时,进入到全局代码的执行上下文。 正如你所见,执行上下文逻辑上来自一个栈。首先可能是有自己作用域的全局代码,代码中可能调用一个函数,它有自己的作用域,函数可以调用另外一个函数,等等。即使函数递归地调用它自身,每一次调用都进入一个新的执行上下文。 四、Activation object/Variable object 每一个执行上下文在其内部都有一个Variable Object。与执行上下文类似,Variable 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 全局变量变成了全局对象的属性,但是,那些在函数代码中定义的局部变量又会如何呢?行为其实很相似:它成了变量对象的属性。唯一的差别在于在函数代码中,变量对象不是全局对象,而是所谓的激活对象。每次函数代码进入执行作用域时,就会创建一个激活对象。 不仅函数代码中的变量和函数成为激活对象的属性,而且函数的每一个参数和一个特定Arguments 对象也是。注意,激活对象是一种内部机制,不会被程序代码真正访问到。 复制代码 代码如下: { 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" */ }); 最后,在Eval 代码中声明的变量作为正在调用的上下文的变量对象的属性被创建。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; GLOBAL_OBJECT.foo; // 1 { /* `bar` is created as a property of calling context Variable object, which in this case is an Activation object of containing function */ eval; /* In abstract terms, ACTIVATION_OBJECT.bar; // 1 */ })(); 五、属性特性 现在变量会怎样已经很清楚,剩下唯一的需要理解的概念是属性特性。每个属性都有来自下列一组属性中的零个或多个特性--ReadOnly, DontEnum, DontDelete 和Internal,你可以认为它们是一个标记,一个属性可有可无的特性。为了今天讨论的目的,我们只关心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 特性,因此不能被删除。特定的Arguments 变量,任何函数实例的length属性也拥有DontDelete 特性。 复制代码 代码如下: { /* 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 特性,因此也不能被删除。 复制代码 代码如下: { delete foo; // false foo; // 1 delete bar; // false bar; // 'blah' }); 七、未声明的赋值 简单地就是未声明的赋值在一个全局对象上创建一个可删除的属性。 复制代码 代码如下: 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;//可理解为 window.bar=2; 根据上面的第五点是可以删除的 delete foo; // false typeof foo; // "number" delete bar; // true typeof bar; // "undefined" 请注意,DontDelete特性是在属性创建的过程中确定的,后来的赋值不会修改现有属性已经存在的特性,理解这一点很重要。 复制代码 代码如下: /* `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" 八、Eval code 在Eval中创建的变量或方法比较特别,没有DontDelete特性,也就是说可以删除。 复制代码 代码如下: eval; console.log; // 1 delete x; console.log; // undefined eval{ var x=1; console.log;/* false */;return 1;}"); console.log; // 1 delete test; console.log; // undefined 注意,这里说的在Eval中创建的变量或方法不包括方法内部的变量或方法,如上面代码中的红色部分,仍然跟之前讲的一致:不能被删除。 九、FireBug的困惑 我们看一段在FireBug中执行的代码结果: 复制代码 代码如下: var x=1; delete x; console.log;//undefined function y(){ var z=1; console.log;//false } y(); delete y; console.log;//undefined 这明明是违反上述规则的,但跟上面第八点对比后发现,这正在代码在eval中执行的效果。虽然没有证实,但我猜测FireBug(Chrome Developer tool)中控制台代码是用eval执行的。 所以,当大家在测试JS代码时,如果涉及到当前上下文环境时特别要注意。 十、delete操作符删除的对象 C++中也有delete操作符,它删除的是指针所指向的对象。例如: 复制代码 代码如下: class Object { public: Object *x; } Object o; o.x = new Object(); delete o.x; // 上一行new的Object对象将被释放 但Javascript的delete与C++不同,它不会删除o.x指向的对象,而是删除o.x属性本身。 复制代码 代码如下: var o = {}; o.x = new Object(); delete o.x; // 上一行new的Object对象依然存在 o.x; // undefined,o的名为x的属性被删除了 在实际的Javascript中,delete o.x之后,Object对象会由于失去了引用而被垃圾回收, 所以delete o.x也就“相当于”删除了o.x所指向的对象,但这个动作并不是ECMAScript标准, 也就是说,即使某个实现完全不删除Object对象,也不算是违反ECMAScript标准。 “删除属性而不是删除对象”这一点,可以通过以下的代码来确认。 复制代码 代码如下: var o = {}; var a = { x: 10 }; o.a = a; delete o.a; // o.a属性被删除 o.a; // undefined a.x; // 10, 因为{ x: 10 } 对象依然被 a 引用,所以不会被回收 另外,delete o.x 也可以写作 delete o["x"],两者效果相同。 十一、其他不能被删除的属性 除了上面说过的内置属性不能被删除外,prototype中声明的属性也不能delete: 复制代码 代码如下: function C() { this.x = 42; } C.prototype.x = 12; C.prototype.y = 13; var o = new C(); o.x; // 42, 构造函数中定义的o.x delete o.x; //true 删除的是自身定义的x o.x; // 12, prototype中定义的o.x,即使再次执行delete o.x也不会被删除 delete o.y; //true,因为 o自身没有o.y属性,y存在于prototype链中,也就是说对象自身属性和prototype属性是不同的 o.y; //13 小结 上面说了那么多,希望对大家认识JavaScript中的Delete有所帮助。由于水平有限,不保证完全正确,如果发现错误欢迎指正。 原文为: 1、 2、 本文首发

复制代码 代码如下:delete thisIsObject[key]ordelete thisIsObject.key

顺便我们来谈谈delete的用法

几个礼拜前, 我有了个机会去翻阅Stoyan Stefanov的 Object-Oriented Javascript 一书. 这本书在亚马逊上拥有很高的评价, 所以我很好奇地想看看它到底是不是那么值得推荐的一本书, 于是我开始阅读函数的那章. 我非常欣赏这本书解释事物的方式, 例子们被以一种非常漂亮, 渐进的方式被组织起来, 看起来即便是初学者也能够轻松掌握这些知识. 然而, 几乎是立刻, 我就发现了一个贯穿整个章节的有趣的误解——删除功能函数. 另外还有一些其它错误, 但是我们目前将不去讨论它们.

"函数被作为像一般变量一样对待-它可以被复制到不同的变量中, 甚至被删除". 在这个解释后面附加了这样一段示例:

复制代码 代码如下:var sum = function {return a + b;}var add = sum;delete sumtruetypeof 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在这些方面几乎是无用处的.

那么, 为什么我们能够删除对象的属性:

复制代码 代码如下:var o = { x: 1 };delete o.x; // trueo.x; // undefined

却不能删除这样声明的对象:

复制代码 代码如下:var x = 1;delete x; // falsex; // 1

复制代码 代码如下:function x(){}delete x; // falsetypeof x; // "function"

注意: 当一个属性无法被删除时,delete操作符只会返回false

要理解这个, 我们首先需要掌握这些有关变量实例和属性特性的概念——这些概念很不幸地, 很少在JavaScript书中被提及. 我将试着在接下来的几个段落中简单地复习一下这些概念. 这些概念是很难理解的!如果你不在乎"为什么这些东西会以这种方式工作"的话,尽情跳过这一章节好了.

在ECMAScript中, 有3种不同类型的可执行代码: 全局代码, 函数代码和 Eval代码. 这些类型从名称上来说或多或少是有自解释性的, 这里有一个简短的概述:

当一段源代码被看成程序时, 它将会在全局环境下被执行, 并且被认为是全局代码. 在一个浏览器环境中, 脚本元素的内容通常被解释为程序, 因此被作为全局代码来执行.

任何直接在一个函数中执行的代码显然被认为是函数代码. 在浏览器中, 事件属性的内容通常被解释成函数代码.

最后, 被应用到内置函数eval的代码文本被解释成Eval代码. 很快我们会发现为什么这种类型是特殊的.

当ECMAScript代码执行时, 它通常会发生在特定的执行上下文中.执行上下文是一个有些抽象的实体概念, 它能帮助理解范围和变量实例(Variable instantiation)是如何工作的. 对三种可执行代码的每一种, 都有一个执行上下文相对应. 当一个函数被执行的时候, 我们说"程序控制进入了函数代码的执行上下文"; 当一段全局代码被执行时, 程序控制进入了全局代码的执行上下文, 等等.

正如你所见, 执行上下文可以在逻辑上构成一个堆栈. 首先, 可能有一段全局代码和其自己的执行上下文, 然后这段代码可能会调用一个函数, 并带着它的执行上下文. 这段函数可以调用另外一个函数, 等等等等. 即使函数是递归调用的, 每次调用时被也会进入一个新的执行上下文.

活动对象 / 变量对象:

每一个执行上下文都有一个跟其所关联的所谓变量对象. 类似于执行上下文, 变量对象是一个抽象实体, 一种用来描述变量实例的机制. 有趣之处在于, 在源代码中声明的变量和函数通常会被当做属性增加到这个变量对象上.

当程序控制进入全局代码的执行上下文时, 一个全局对象被用来作为一个变量对象. 这正是为什么声明为全局的函数变量会变成全局对象属性的原因.

复制代码 代码如下:/* remember that `this` refers to global object when in global scope */var GLOBAL_OBJECT = this;

var foo = 1;GLOBAL_OBJECT.foo; // 1foo === GLOBAL_OBJECT.foo; // true

function bar(){}typeof GLOBAL_OBJECT.bar; // "function"GLOBAL_OBJECT.bar === bar; // true

好, 所以全局变量会变成全局对象的属性, 但是局部变量会发生什么呢? 其实它们的行为也非常类似: 它们会变成变量对象的属性. 唯一的不同在于, 当在函数代码中时, 一个变量对象并不是全局对象, 而是所谓的活动对象. 活动对象在会每次进入函数代码的执行上下文时被创建.

并不是只有在函数代码中声明的变量和函数会变成活动对象的属性; 这也会在每个函数参数和一个特殊的Arguments对象上发生. 注意, 活动对象是一个内部描述机制, 在程序代码中并不能被访问.

复制代码 代码如下:{ 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" */});最后, 在Eval代码中声明的变量会成为调用者上下文的变量对象的属性. 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;GLOBAL_OBJECT.foo; // 1{ /* `bar` is created as a property of calling context Variable object, which in this case is an Activation object of containing function */ eval; /* In abstract terms, ACTIVATION_OBJECT.bar; // 1 */})();

我们几乎是已经在这了. 既然我们已经很清楚在变量上发生了什么, 唯一剩下的需要理解的概念就是属性的特性了. 每一个属性可以拥有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; // falsetypeof 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; // falsetypeof 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; // truetypeof GLOBAL_OBJECT.baz; // "undefined"

内置对象和DontDelete

所以, 这就是有关它的所有: 属性的一个特殊特性, 用来控制这个属性是否能够被删除. 注意, 有些内置对象的属性是指定含有DontDelete的, 所以无法被删除. 如特殊的arguments变量(或者, 正如我们现在所知道的, 一个活动对象的属性)拥有DontDelete. 函数实例的length属性也具有DontDelete属性.

复制代码 代码如下:{ /* 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特性的, 所以我们也无法删除它.

复制代码 代码如下:{ delete foo; // false foo; // 1 delete bar; // false bar; // '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; // falsetypeof foo; // "number"delete bar; // truetypeof 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; // falsetypeof 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; // truetypeof bar; // "undefined"

Firebug的困惑:

在Firebug中发生了什么? 为什么在console中声明的变量可以被删除, 这不是违背了我们之前所学到的知识么? 嗯, 就像我之前所说的那样, Eval代码在面对变量声明时会有特殊的表现. 在Eval中声明的变量实际上是作为不带DontDelete特性的属性被创建的.

复制代码 代码如下:eval;foo; // 1delete foo; // truetypeof foo; // "undefined"

同样, 类似的, 当在函数代码中调用时:

复制代码 代码如下:{

eval; foo; // 1 delete foo; // true typeof foo; // "undefined"

这就是Firebug反常行为的依据. 在console中的所有文本都会被当做Eval代码来解析和执行, 而不是全局或函数代码. 显然, 这里声明的所有变量最后都会成为不带DontDelete特性的属性, 所以它们都能被轻松删除. 我们需要了解这个在全局代码和Firebug控制台之间的差异.

通过Eval来删除变量:

其他新闻
  • C# 正则表达式使用示例-匹配 C#正则表达式使用示例-替换 Regular Expression Net程序员必然要掌握正则的核心内容: 匹配 、 提取 、 替换 、 常用元字符 。 using System.Text.RegularExpressions;...
    2019-12-30
  • 很多人认为问题没解决是由于缺少计算机软件,似乎开发一个打车 App就可以解决打车难的问题;也有很多人认为计算机软件都是多余的,顶得上跟客户唱一晚歌、打一场球么? 第十二...
    2019-12-30
  • 像网页的导航栏的文字可以利用滑动门技术做。滑动门就是根据字的多少自动收缩,不必根据字的多少再去调宽度。像导航栏这种的都需要链接,则以一个a标签作为父亲,他的孩子可以...
    2019-12-27
友情链接

公司名称巴黎人电玩
版权所有:Copyright © 2015-2019 http://www.zhongqiangjy.com. 巴黎人电玩有限公司 版权所有

友情链接

Copyright © 2015-2019 http://www.zhongqiangjy.com. 巴黎人电玩有限公司 版权所有
公司地址http://www.zhongqiangjy.com