- 浏览: 275751 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
yh008:
不错的工具类。 版面需要调整下,有些内容看不到了,只能复制粘贴 ...
Spring 优秀工具类盘点,第 1 部分: 文件资源操作二 -
手心的水滴:
function Foo() { (function( ...
js 两个小括号 ()() 的用法 -
showthesunli:
博主说了这么多,我来说说我的想法,不晓得对不对总结一下,如下代 ...
js 两个小括号 ()() 的用法 -
aqbzwxd:
就是这种盲目无原理依据的研究测试弄晕了我, 有必要这么做吗? ...
js 两个小括号 ()() 的用法 -
wst0350:
多谢
oracle 触发器
最近重新温习JS,对delete操作符一直处于一知半解的状态,偶然发现一篇文章,对此作了非常细致深入的解释,看完有茅塞顿开的感觉,不敢独享,大致翻译如下。
原文地址:http://perfectionkills.com/understanding-delete/
P.S. 作者是PrototypeJS的开发组成员之一
========分割线========
在开始之前,先让我们看一段代码
>>> var sum = function(a, b) {return a + b;} >>> var add = sum; >>> delete sum true >>> typeof sum; "undefined"
这段代码是Firebug控制台里的实际结果,初看这段代码,你觉得有什么问题?但我要说的是,删除sum应该是失败的,同时typeof sum的结果不应该是undefined,因为在Javascript里以这种方式声明的变量是无法被删除的。
那么问题出在哪里?为了回答这个问题,我们需要理解delete操作符在各种情况下的实现细节,然后再回过头来看Firebug的这个看似“诡异”的输出。
P.S 没有特殊声明的情况下,下文中所提到的Javascript都指的是ECMAScript规范。
1. 理论
delete操作符通常用来删除对象的属性:
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中,有三种可执行代码类型:全局代码、函数代码、eval代码。
1. 当一段代码被当做程序段运行的时候,它是在全局作用域下执行的,也就是全局代码。在浏览器环境下,通常<SCRIPT>元素就是一段全局代码。
2. 所有在function中声明的代码即是函数代码,最常见的是HTML元素的响应事件(<p onclick="...">)。
3. 传入内建的eval函数中的代码段称为eval代码,稍后我们会看到这种类型的特别性。
执行上下文(Execution Context)
在ECMAScript代码执行的时候,总是会有一个执行的上下文。这是一个比较抽象的概念,但可以帮助我们理解作用域以及变量初始化的相关过程。对于以上三种代码段类型,都有一个相应的执行上下文,比如函数代码有函数上下文,全局代码有全局上下文,等等。
逻辑上执行上下文相互间可以形成堆栈,在全局代码执行的最开始会有一个全局上下文,当调用一个函数的时候会进入相应函数的上下文,之后又可以再继续调用其他的函数亦或是递归调用自己,这时执行上下文的嵌套类似于函数调用栈。
Activation object / Variable object
每个执行上下文都和一个Variable object(变量对象)相关联 ,这也是一个抽象的概念,便于我们理解变量实例化机制:在源代码中声明的变量和方法实际上都是作为属性被加入到与当前上下文相关联的这个对象当中 。
当执行全局代码的时候,Variable object就是一个全局对象,也就是说所有全局变量和函数都是作为这个变量的属性存在。
/* 全局环境下,this所指向的就是这个全局对象 */ 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
那么对于在函数中声明的变量呢?情况是类似的,函数中声明的变量也是被当做相应上下文对象的属性,唯一的区别是在函数代码段中,这个对象被称为Activation object(活动对象)。每次进入一个函数调用都会新建一个新的活动对象。
在函数段中,并不是只有显式声明的变量和函数会成为活动对象的属性,对于每个函数中隐式存在的arguments对象(函数的参数列表)也是一样的。注意活动对象其实是一种内部机制,程序代码是无法访问到的。
(function(foo){ var bar = 2; function baz(){} /* 可以吧活动对象作为一个抽象的存在,在每进入一个函数的时候,默认的arguments对象以及传入的参数都会自动被设为活动对象的属性: ACTIVATION_OBJECT.arguments; // arguments变量 传入参数foo: ACTIVATION_OBJECT.foo; // 1 函数内声明的变量bar: ACTIVATION_OBJECT.bar; // 2 以及函数内定义的baz函数: typeof ACTIVATION_OBJECT.baz; // "function" */ })(1);
最后,在evel代码段中定义的变量都是被加入到当前执行eval的上下文环境对象中,也就是说进入eval代码时并不会新建新的变量对象,而是沿用当前的环境。
var GLOBAL_OBJECT = this; /* foo被加入到当前变量对象中,也就是全局对象。 */ eval('var foo = 1;'); GLOBAL_OBJECT.foo; // 1 (function(){ /* bar被加入到当前这个函数的活动对象中。 */ eval('var bar = 1;'); /* 可以抽象地表示为: ACTIVATION_OBJECT.bar; // 1 */ })();
变量属性的标记
我们已经知道声明变量时发生了什么(他们都变成了当前上下文对象的属性),接下来我们就要看一下属性究竟是怎么样一回事。每一个变量属性都可以有以下任意多个属性: ReadOnly, DontEnum, DontDelete, Internal。你可以把这些当做标记,标明了变量属性可以持有的某种特性。这里我们最感兴趣的就是DontDelete标记。
在声明变量或者函数时,他们都变成了当前上下文对象的属性--对于函数代码来说是活动对象,对于全局代码来说则是变量对象,而值得注意的是这些属性在创建时都带有DontDelete标记,但是显式或者隐式的赋值语句所产生的属性并不会带有这个标记!这就是为什么有一些属性我们可以删除,但另一些却不可以:
var GLOBAL_OBJECT = this; /* foo是被正常声明的,所以带有DontDelete标记,从而不能被删除! */ var foo = 1; delete foo; // false typeof foo; // "number" /* bar是作为函数被声明,一样带有DontDelete,不能被删除。 */ function bar(){} delete bar; // false typeof bar; // "function" /* baz是直接通过一个赋值而没有声明,不会持有DontDelete标记,才可以被删除! */ GLOBAL_OBJECT.baz = 'blah'; delete GLOBAL_OBJECT.baz; // true typeof GLOBAL_OBJECT.baz; // "undefined"
内建对象与DontDelete
DontDelete就是一个特殊的标记,用来表明某一个属性能否被删除。需要注意的是一些内建的对象是自动持有这个标记的,从而不能被删除,比如函数内的arguments,以及函数的length属性。
(function(){ /*arguments对象默认持有DontDelete标记,不能被删除。 */ delete arguments; // false typeof arguments; // "object" /* 函数的length属性也一样 */ function f(){} delete f.length; // false typeof f.length; // "number" })();
函数的传入参数也是一样的:
(function(foo, bar){ delete foo; // false foo; // 1 delete bar; // false bar; // 'blah' })(1, 'blah');
非声明性赋值
你可能知道,非声明性的赋值语句会产生全局变量,进而变成全局变量对象的属性。所以根据上面的解释,非声明性的赋值所产生的对象是可以被删除的:
var GLOBAL_OBJECT = this; /* 通过声明的全局变量会持有DontDelete,无法被删除。 */ var foo = 1; /* 没有经过声明的变量赋值不会带DontDelete,可以被删除。 */ bar = 2; delete foo; // false typeof foo; // "number" delete bar; // true typeof bar; // "undefined"
需要注意的是属性标记诸如DontDelete是在这个属性被创建的时候 产生的,之后对该属性的任何赋值都不会改变此属性的标记!
/* foo被声明时会带有DontDelete标记 */ function foo(){} /* 之后对foo的赋值无法改变他所带的标记! */ foo = 1; delete foo; // false typeof foo; // "number" /* 当给一个还不存在的属性赋值的时候会创建一个不带任何标记的属性(包括DontDelete),进而可以被删除! */ this.bar = 1; delete bar; // true typeof bar; // "undefined"
2. Firebug的困扰
现在再让我们回到最开始的问题,为什么在Firebug控制台里声明的变量可以被删除呢?这就要牵涉到eval代码段的特殊行为,也就是在eval中声明的变量创建时都不会带有DontDelete标记!
eval('var foo = 1;'); foo; // 1 delete foo; // true typeof foo; // "undefined"
在函数内部也是一样的:
(function(){ eval('var foo = 1;'); foo; // 1 delete foo; // true typeof foo; // "undefined" })();
这就是导致Firebug"诡异"行为的罪魁祸首: 在Firebug控制台中的代码最终将通过eval执行,而不是作为全局代码或函数代码。显然地,这样声明出来的变量都不会带DontDelete标记,所以才能被删除!(译者:也不能太信任Firebug啊。)
3. Browsers Compliance
//译者:这一节讲了主流浏览器对一些delete的特殊情况的不同处理,篇幅所限暂不赘述,有兴趣的可以参看原文。
4. IE bugs
是的,你没有看错,整个这一节都是在讲IE的bug!
在IE6-8中,下面的代码会抛出错误(全局代码):
this.x = 1; delete x; // TypeError: Object doesn't support this action var x = 1; delete this.x; // TypeError: Cannot delete 'this.x'
看起来似乎在IE里变量声明并不会在全局变量对象里产生相应的属性。还有更有趣的,对于显式赋值的属性总是会在删除时出错,并不是真正抛出错误,而是这些属性似乎都带有DontDelete标记,和我们的设想相反。
this.x = 1; delete this.x; // TypeError: Object doesn't support this action typeof x; // "number" (没有被删除!) delete x; // TypeError: Object doesn't support this action typeof x; // "number" (还是没有被删除!)
但下面的代码表明,非声明性的赋值产生的属性确实是可以删除的:
x = 1; delete x; // true typeof x; // "undefined"
不过当你试图通过全局变量对象this来访问x的时候,错误又来了:
x = 1; delete this.x; // TypeError: Cannot delete 'this.x'
总而言之,通过全局this变量去删除属性(delete this.x)总会出错,而直接删除该属性(delete x)时:如果x是通过全局this赋值产生会(this.x=1)导致错误;如果x通过显式声明创建(var x=1)则delete会像我们预料的那样无法删除并返回false;如果x通过非声明式赋值创建(x=1)则delete可以正常删除。
对于以上的问题,Garrett Smith 的一个解释是"IE的全局变量对象是通过JScript实现,而一般的全局变量是由host实现的。"(ref: Eric Lippert’s blog entry )
我们可以自己验证一下这个解释,注意this和window看上去是指向同一个对象,但是函数所返回的当前环境的变量对象却和this不同。
/* 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
5. 错误的理解
//译者: 大意为网上对Javascript一些行为有各种不同的解释,有的甚至可能完全矛盾,不要轻易相信别人的解释,试着自己去寻找问题的核心:)
6. delete与宿主对象(host objects)
delete的大致算法如下:
1. 如果操作对象不是一个引用,返回true
2. 如果当前上下文对象没有此名字的一个直接属性,返回true(上下文对象可以是全局对象或者函数内的活动对象)
3. 如果存在这样一个属性但是有DontDelete标记,返回false
4. 其他情况则删除该属性并返回true
然而有一个例外,即对于宿主对象而言,delete操作的结果是不可预料的。这并不奇怪,因为宿主对象根据不同浏览器的实现允许有不同的行为,这其中包括了delete。所以当处理宿主对象时,其结果是不可信的,比如在FF下:
/* "alert" 是window对象的一个属性 */ window.hasOwnProperty('alert'); // true delete window.alert; // true typeof window.alert; // "function",表明实际上并没有真正删除
总而言之,任何时候都不要相信宿主对象。
7. ES5 strict mode
为了能更早地发现一些应该被发现的问题,ECMAScript 5th edition 提出了strict mode的概念。下面是一个例子:
(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
对未声明的变量赋值:
"use strict"; i_dont_exist = 1; // ReferenceError
可以看出,strict mode采用了更主动并且描述性的方法,而不是简单的忽略无效的删除操作。
8. 总结
- 变量和函数的声明实际上都会成为全局对象或者当前函数活动对象的属性。
- 属性都有一个DontDelete标记,用于表明该属性是否能被delete。
- 变量和函数的声明创建的属性都会带有DontDelete标记。
- 函数内建的arguments对象作为该函数活动对象的默认属性,创建时总会带有DontDelete标记。
- 在eval代码块中声明的变量和方法都不带有DontDelete标记。
- 对还不存在的变量或属性的直接赋值产生的对象不会带有任何标记,包括DontDelete。
- 对于宿主对象而言,delete操作的结果有可能是不可预料的。
评论
delete x;
x就已经删除了
和你说法矛盾啊
没有呀。你再alert(x)看看。。注意别在firebug中测试。。因为firebug中的代码是eval的。根据上面的说法,eval中的是不带don't delete标记的。不过作者还少提到了一句:eval中的函数,函数中第一的变量,是带DontDelete标记的。
delete x;
x就已经删除了
和你说法矛盾啊
发表评论
-
cookie操作
2010-06-03 09:12 1067设置cookie每个cookie都是一个名/值对,可以把下面这 ... -
js 两个小括号 ()() 的用法
2010-06-02 18:08 15033JavaScript 中,考查如下代码: (functi ... -
try catch finally
2010-05-31 08:38 1486try catch finally是javascript ... -
showModalDialog()、showModelessDialog()方法使用详解
2010-05-30 23:24 1524Javascript有许多内建的方法来产生对话框,如:wind ... -
window.event对象详细介绍
2010-05-30 23:11 27281、event代表事件的状态,例如触发event对象的元素、鼠 ... -
cookie跨域访问的问题
2010-05-14 14:41 4994cookie跨域访问的问题(多个项目间) 今天有一同 ... -
escape() encodeURI() encodeURIComponent() 区别
2010-04-26 18:03 1006escape() 方法: 采用ISO ... -
javaScript 中函数对象 call 方法的使用
2010-04-20 20:44 1504javaScript 中的call() 是一个奇妙的方法,但也 ... -
js 数组 pop,push,unshift,splice,shift
2010-04-17 15:45 5952Js:数组的各种方法范例pop,push,unshift,sp ... -
window.showModalDialog 的使用
2010-03-20 11:04 985父窗口 var a = window.showModal ... -
js中escape,encodeURI,encodeURIComponent三个函数的区别
2010-02-06 19:09 870js对文字进行编码涉及3个函数:escape,encodeUR ... -
关于js中window.location.href,location.href,parent.location.href,top.location.href的用
2009-11-25 14:44 18002"window.location.href" ... -
window.location.href和window.location.replace的区别
2009-06-17 16:26 2514总是在资料上看到 window.location.href和w ... -
javascript 正则
2009-04-14 22:14 839javascript中与正则表达式有关的匹配字符串的函数主要有 ... -
javascript 小技巧
2009-04-01 10:27 824<!-- 另存为图片 --> ... -
javascript 小技巧
2009-03-27 20:08 1055//最小化,最大化,关闭 <object id=min ... -
javascript 小技巧
2009-03-27 19:32 766text文本框 内容保存 <HTML> < ... -
javascript 小技巧
2009-03-27 19:30 815事件源对象 event.srcElement.tagName ... -
javascript 小技巧
2009-03-27 19:26 1038//setTimeout增加参数 <script> ... -
页面打印Js 代码
2009-03-27 18:49 2902<script language=javascript& ...
相关推荐
JavaScript中的`delete`操作符主要用于从对象中删除属性,或者在某些特定情况下,从数组中删除元素。然而,它与C++中的`delete`操作符不同,C++的`delete`用于释放内存,而JavaScript的`delete`只是移除属性引用,...
JavaScript中的delete操作符是一种用于删除对象属性的运算符,其作用是移除对象的指定属性。然而,在实际应用中,delete操作符的行为可能会让人感到困惑,因为它在处理对象、数组以及变量时有着不同的特性。 首先,...
主要介绍了Javascript中的delete操作符详细介绍,着重介绍了哪些情况可以使用delete操作符,以有delete操作符的返回值等问题,需要的朋友可以参考下
### JS中的内部属性与delete操作符介绍 JavaScript(JS)是一种广泛使用的高级编程语言,它具备丰富的内部属性,以及特定的关键字和操作符来控制这些属性的行为。其中,`delete` 操作符是一个重要的组成部分,它...
在JavaScript中,`delete`操作符用于删除对象的属性或者变量。然而,并非所有属性或变量都可以使用`delete`操作符删除。以下是关于JavaScript中`delete`操作符不能删除的对象的详细分析: 首先,理解`delete`的工作...
1.delete操作符 delete 操作符用于删除对象的某个属性;如果没有指向这个属性的引用,那它最终会被释放 语法:delete expression delete 操作符会从某个对象上移除指定属性。成功删除的时候回返回 true,否则返回 ...
本文主要讲解了如何在JavaScript中删除对象的某个属性,并通过实际案例详细解释了delete操作符的使用方法。在JavaScript中,delete操作符主要用于删除对象的属性。如果操作成功,delete会返回true,否则返回false。...
JavaScript中的delete操作符是一个用于删除对象属性或数组元素的运算符。虽然它看起来简单易用,但在实际应用中却隐藏着不少技巧和注意事项,下面将根据提供的内容详细解析delete操作符的使用场景和特点。 首先,...
总结来说,JavaScript中的delete操作符用于删除对象的属性,但不能用于删除变量或函数。delete操作符的使用受到变量对象和激活对象的特性影响,开发者需要对这些概念有深入的理解才能准确判断何时delete操作符能够...
JavaScript中的`delete`运算符是一种一元操作符,它的主要作用是删除对象的属性或数组的元素。然而,`delete`并非总是能成功删除,并且它不会返回删除的值,而是返回一个布尔值来表示删除操作是否成功。下面将详细...
在JavaScript中,`delete`操作符是用来删除对象的属性,但它的工作方式可能并不像其他编程语言中的删除那样直观。理解`delete`的工作机制是至关重要的,尤其是当你在处理对象及其原型链时。让我们深入探讨一下`...
JavaScript 有赋值、比较、算术、位、逻辑、字符串和特殊操作符。本章描述了这些操作符,包含了关于操作符优先级的信息。 赋值操作符 比较操作符 算术操作符 % (求余数) ++ (自加) -- (自减) - (一元否定) 位操作符 ...
在JavaScript编程语言中,表达式和操作符是构建程序的基础元素。本章主要讲解了JavaScript中的表达式和各种运算符的使用。 3.1 表达式 表达式是由数值、变量以及运算符组成的组合,它们可以计算出一个值。在...
在JavaScript中,对象属性的存取可以非常灵活,而delete操作符就是用来移除对象属性的手段之一。关于delete关键字,它涉及到几个方面的知识点:属性存在性的检测,以及使用delete操作符删除属性。 首先,属性存在性...