Javascript中的delete介绍


Posted in Javascript onSeptember 02, 2012

一、问题的提出

我们先来看看下面几段代码,要注意的是,以下代码不要在浏览器的开发者工具(如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(放在Eval中执行的代码)。

var x=1;//Global code 
function test(){ 
var y=2;//Function Code 
eval("var z=3");//Eval Code in Function 
} 
eval("function evalTest(){}");//Eval Code in Global

三、执行上下文

当ECMAScript 代码执行时,它总是在一定的上下文中运行,执行上下文是一个有点抽象的实体,它有助于我们理解作用域和变量实例化如何工作的。对于三种类型的可执行代码,每个都有执行的上下文。当一个函数执行时,可以说控制进入到函数代码(Function code)的执行上下文。全局代码执行时,进入到全局代码(Global code)的执行上下文。

正如你所见,执行上下文逻辑上来自一个栈。首先可能是有自己作用域的全局代码,代码中可能调用一个函数,它有自己的作用域,函数可以调用另外一个函数,等等。即使函数递归地调用它自身,每一次调用都进入一个新的执行上下文。

四、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

全局变量变成了全局对象的属性,但是,那些在函数代码(Function code)中定义的局部变量又会如何呢?行为其实很相似:它成了变量对象的属性。唯一的差别在于在函数代码(Function code)中,变量对象不是全局对象,而是所谓的激活对象(Activation object)。每次函数代码(Function code)进入执行作用域时,就会创建一个激活对象(Activation object)。

不仅函数代码(Function code)中的变量和函数成为激活对象的属性,而且函数的每一个参数(与形参相对应的名称)和一个特定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 代码(Eval code)中声明的变量作为正在调用的上下文的变量对象的属性被创建。Eval 代码(Eval code)只使用它正在被调用的哪个执行上下文的变量对象。
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 
*/ 
})();

五、属性特性

现在变量会怎样已经很清楚(它们成为属性),剩下唯一的需要理解的概念是属性特性。每个属性都有来自下列一组属性中的零个或多个特性--ReadOnly, DontEnum, DontDelete 和Internal,你可以认为它们是一个标记,一个属性可有可无的特性。为了今天讨论的目的,我们只关心DontDelete 特性。

当声明的变量和函数成为一个变量对象的属性时--要么是激活对象(Function code),要么是全局对象(Global code),这些创建的属性带有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 变量(或者,正如我们现在了解的,激活对象的属性),任何函数实例的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');

七、未声明的赋值

简单地就是未声明的赋值在一个全局对象上创建一个可删除的属性。

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("var x = 1;"); 
console.log(x); // 1 
delete x; 
console.log(typeof x); // undefined eval("function test(){ var x=1; console.log(delete x);/* false */;return 1;}"); 
console.log(test()); // 1 
delete test; 
console.log(typeof test); // undefined

注意,这里说的在Eval中创建的变量或方法不包括方法内部的变量或方法,如上面代码中的红色部分,仍然跟之前讲的一致:不能被删除。

九、FireBug的困惑

我们看一段在FireBug中执行的代码结果:

var x=1; 
delete x; 
console.log(typeof x);//undefined function y(){ 
var z=1; 
console.log(delete z);//false 
} 
y(); 
delete y; 
console.log(typeof y);//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、http://perfectionkills.com/understanding-delete/(英文)

2、http://nanto.asablo.jp/blog/2008/01/09/2552470(日文)

本文首发http://jscode.cnblogs.com

Javascript 相关文章推荐
一个符号插入器 中用到的js代码
Sep 04 Javascript
input:checkbox多选框实现单选效果跟radio一样
Jun 16 Javascript
jQuery实现鼠标单击网页文字后在文本框显示的方法
May 06 Javascript
详解js中构造流程图的核心技术JsPlumb
Dec 08 Javascript
js 定义对象数组(结合)多维数组方法
Jul 27 Javascript
bootstrap组件之按钮式下拉菜单小结
Jan 19 Javascript
ES6 Promise对象概念与用法分析
Apr 01 Javascript
微信小程序使用navigateTo数据传递的实例
Sep 26 Javascript
vue移动端弹框组件的实例
Sep 25 Javascript
vue中axios的二次封装实例讲解
Oct 14 Javascript
Node.js控制台彩色输出的方法与原理实例详解
Dec 01 Javascript
weui上传多图片,压缩,base64编码的示例代码
Jun 22 Javascript
JS判断当前日期是否大于某个日期的实现代码
Sep 02 #Javascript
Javascript this 的一些学习总结
Aug 31 #Javascript
javascript动画浅析
Aug 30 #Javascript
jquery方法+js一般方法+js面向对象方法实现拖拽效果
Aug 30 #Javascript
JS跨域代码片段
Aug 30 #Javascript
JS跨域总结
Aug 30 #Javascript
js中判断Object、Array、Function等引用类型对象是否相等
Aug 29 #Javascript
You might like
PHP Zip压缩 在线对文件进行压缩的函数
2010/05/26 PHP
php中Smarty模板初体验
2011/08/08 PHP
JavaScript 特殊字符
2007/04/05 Javascript
js检测客户端不是firefox则提示下载
2007/04/07 Javascript
用javascript实现在小方框中浏览大图的代码
2007/08/14 Javascript
ExtJS 2.0实用简明教程 之Border区域布局
2009/04/29 Javascript
JS 参数传递的实际应用代码分析
2009/09/13 Javascript
JS获取农历日期具体实例
2013/11/14 Javascript
深入理解JavaScript中的传值与传引用
2013/12/09 Javascript
extjs_02_grid显示本地数据、显示跨域数据
2014/06/23 Javascript
jQuery实现的感应鼠标悬停图片色彩渐显效果
2015/03/03 Javascript
微信小程序 tabs选项卡效果的实现
2017/01/05 Javascript
全面总结Javascript对数组对象的各种操作
2017/01/22 Javascript
nodejs require js文件入口,在package.json中指定默认入口main方法
2018/10/10 NodeJs
使用jquery Ajax实现上传附件功能
2018/10/23 jQuery
JS实现悬浮球只在一侧滑动并且是横屏状态下
2020/08/19 Javascript
微信小程序入门之绘制时钟
2020/10/22 Javascript
Python 文件读写操作实例详解
2014/03/12 Python
使用Python对Excel进行读写操作
2017/03/30 Python
Django安装配置mysql的方法步骤
2018/10/15 Python
Python中三元表达式的几种写法介绍
2019/03/04 Python
详解python 破解网站反爬虫的两种简单方法
2020/02/09 Python
Python3爬虫中关于Ajax分析方法的总结
2020/07/10 Python
法国票务网站:Ticketmaster法国
2018/07/09 全球购物
什么是接口(Interface)?
2013/02/01 面试题
使用C#编写创建一个线程的代码
2013/01/22 面试题
经理秘书找工作求职信
2013/12/19 职场文书
销售经理工作职责
2014/02/03 职场文书
12月红领巾广播稿
2014/02/13 职场文书
党支部反对四风思想汇报
2014/10/10 职场文书
群众路线教育实践活动学习笔记
2014/11/05 职场文书
2014年司法所工作总结
2014/11/22 职场文书
秋菊打官司观后感
2015/06/03 职场文书
2016党员党章学习心得体会
2016/01/14 职场文书
理解深度学习之深度学习简介
2021/04/14 Python
Vertica集成Apache Hudi重磅使用指南
2022/03/31 Servers