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 相关文章推荐
[全兼容哦]--实用、简洁、炫酷的页面转入效果loing
May 07 Javascript
javascript控制frame,iframe的src属性代码
Dec 31 Javascript
Js 冒泡事件阻止实现代码
Jan 27 Javascript
js根据日期判断星座的示例代码
Jan 23 Javascript
jQuery随机密码生成的方法
Mar 09 Javascript
全面解析Bootstrap表单使用方法(表单控件)
Nov 24 Javascript
node.js中fs文件系统目录操作与文件信息操作
Feb 24 Javascript
微信公众号平台接口开发 菜单管理的实现
Aug 14 Javascript
JavaScript find()方法及返回数据实例
Apr 30 Javascript
有关vue 开发钉钉 H5 微应用 dd.ready() 不执行问题及快速解决方案
May 09 Javascript
js实现拖拽与碰撞检测
Sep 18 Javascript
vue3.0封装轮播图组件的步骤
Mar 04 Vue.js
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执行速度全攻略(下)
2006/10/09 PHP
使用PHP连接多种数据库的实现代码(mysql,access,sqlserver,Oracle)
2016/12/21 PHP
php删除二维数组中的重复值方法
2018/03/12 PHP
PHP生成随机字符串实例代码(字母+数字)
2019/09/11 PHP
laravel Validator ajax返回错误信息的方法
2019/09/29 PHP
jscript之Open an Excel Spreadsheet
2007/06/13 Javascript
jquery构造器的实现代码小结
2011/05/16 Javascript
有关于JS构造函数的重载和工厂方法
2013/04/07 Javascript
jQuery中html()方法用法实例
2014/12/25 Javascript
JS交换变量的方法
2015/01/21 Javascript
JS实现表格数据各种搜索功能的方法
2015/03/03 Javascript
JS实现的自定义右键菜单实例二则
2015/09/01 Javascript
JS实现控制文本框的内容
2016/07/10 Javascript
全屏滚动插件fullPage.js使用实例解析
2016/10/21 Javascript
vue实现导航栏效果(选中状态刷新不消失)
2017/12/13 Javascript
你可能不知道的前端算法之文字避让(inMap)
2018/01/12 Javascript
原生js封装的ajax方法示例
2018/08/02 Javascript
一篇文章,教你学会Vue CLI 插件开发
2019/04/17 Javascript
js实现踩五彩块游戏
2020/02/08 Javascript
在vue中动态修改css其中一个属性值操作
2020/12/07 Vue.js
在Django框架中设置语言偏好的教程
2015/07/27 Python
浅谈Django中的数据库模型类-models.py(一对一的关系)
2018/05/30 Python
python操作excel的方法(xlsxwriter包的使用)
2018/06/11 Python
Python3标准库glob文件名模式匹配的问题
2020/03/13 Python
解决reload(sys)后print失效的问题
2020/04/25 Python
matplotlib相关系统目录获取方式小结
2021/02/03 Python
解释DataSet(ds) 和 ds as DataSet 的含义
2014/07/27 面试题
Delphi工程师笔试题
2013/09/21 面试题
品学兼优的大学生自我评价
2013/09/20 职场文书
班子群众路线教育实践个人对照检查材料思想汇报
2014/09/30 职场文书
流动人口婚育证明
2014/10/19 职场文书
基层组织建设年活动总结
2015/05/09 职场文书
2016高考感言
2015/08/01 职场文书
重阳节主题班会
2015/08/17 职场文书
优秀乡村医生事迹材料(2016精选版)
2016/02/29 职场文书
Python实现滑雪小游戏
2021/09/25 Python