Skip to content

Instantly share code, notes, and snippets.

@markyun
Created January 2, 2014 02:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markyun/8214081 to your computer and use it in GitHub Desktop.
Save markyun/8214081 to your computer and use it in GitHub Desktop.
delete操作符(一)
delete操作符(一)
Published by 陈漫凯 | on March 19, 2012 | Posted in 豆知识
这里想分享的是,一个大家可能挺熟悉,却挺少用的操作符,delete操作符。
 
什么是delete操作符
delete操作符,可以用于删除对象的属性,或者数组的某个值,如果删除成功则返回true。
语法
delete一元表达式
很简单的语法,可以这样使用
/* 代码片断1 */
var obj ={a:1, b:2};
delete obj.a;// true
console.log(obj.a);// undefined
/* 代码片断2 */
var arr =[1,2,3];
delete arr[0];// true
console.log(arr[0]);// undefined
但,当delete的是变量和function时:
/* 代码片断3 */
var x =1;
delete x;// false
console.log(x);// 1
/* 代码片断4 */
function x(){}
delete x;// false
console.log(typeof x);// "function"
返回了false,delete并不能用于删除所有。
 
未声明变量
对比代码片断3,看另外的写法:
/* 代码片断5 */
x =1;
delete x;// true
console.log(x);// undefined
与代码片断3唯一不同的是,这里的x没有被声明,这种未声明的赋值,出乎意料的可以被delete。
 
对象引用
delete操作符,删除的只是引用,而不是对象本身。这已经足以,js中没被引用的对象,最终会被垃圾回收。
/* 代码片断6 */
var obj ={a:1, b:2};
var a = obj.a;
delete obj.a;// true
console.log(obj.a);// undefined
console.log(a);// 1
把一个对象的属性值设置为null也能达到断开引用的目的,不同的是delete做得更彻底一些,delete同时会把对象的属性删除。
 
delete数组元素
当delete的是数组中的某个值,数组的长度并不会受影响,访问它的值,将会是undefined,如代码片断2
另外,纵观以上的代码,所有delete成功后,变量/函数的值都是     undefined,需要注意的是,delete并非简单的把值设置为undefined,而是把值永久的删除,变量/函数将不再存在。
/* 代码片断7 */
var arr =[1,2,3];
//设置某个之为undefined后,该值可以被枚举出来
arr[0]=undefined;
for(var i in arr){
     console.log(arr[i]);// undefined, 2, 3
}
//直接delete之后,该值不再存在
delete arr[0];
for(var k in arr){
     console.log(arr[k]);// 2, 3
}
 
firebug的bug
没错,firebug也有出bug的时候。
如果有谁试了把上面的所说的代码片断3和4粘贴到firebug控制台中运行,会发现运行的结果与这里描述的不一致。但如果把这段代码放到浏览器中运行,就会发现,被firebug的控制台“骗了”。这个又到底是为什么呢?
 
留给悬念
至此,可以大致了解了delete操作符,这是一个简单的却有充满陷阱的东西。
似乎,还没有说到重点,留下了两个问题:
  > 声明变量不可以被delete,而未声明变量却可以?
  > 为何同样代码,firebug控制台运行结果和实际运行结果却不同?
这次先点到为止,鉴于还有活要干,留在下期再分享。
delete操作符(二)
Published by 陈漫凯 | on March 19, 2012 | Posted in 豆知识
delete操作符(一)
带着上周遗留的两个问题,继续介绍delete操作符。
首先,似乎还没有介绍delete的必要性,为此,做了一下试验:
TestA:重复100,000次,为一个对象添加属性,并马上移除属性。
function testA(){
     var x ={};//Local variable
     for(var i =0; i <100000; i++){
          x['a'+i]= i;//new String(i)
          delete x['a'+i];
     }
};
测试结果:
内存基本平稳,内存基本能被释放,增幅不大。
TestB:为一个对象添加100,000次属性后,再重复100,000移除属性。
function testB(){
     var x ={};//Local variable
     for(var i =0; i <100000; i++){
          x['a'+i]= i;//new String(i)
     }
     for(var i =0; i < count; i++){
          delete x['a'+i];
     }
};
测试结果:
内存先升高后下降。虽然最终内存得到释放,所需内存幅度比较大。
TestC:为一个对象添加100,000次属性后,再直接把该对象移除。
function testC(){
     x ={};//Global variable
     for(var i =0; i <100000; i++){
          x['a'+i]= i;//new String(i)
     }
     delete x;
};
测试结果:
内存增加,并无法被释放。
从曲线可以看出,对于无需要的属性,越早删除越有助于内存释放。
delete和GC,delete一个对象的属性,对象属性所占用的内存并不会马上被回收,只是断开了与对象属性占用的内存的引用。而JS的垃圾回收(GC),会定期检查并回收栈中没有被引用的内存。各个浏览器的GC实现不尽相同,但原理效果基本一致。
对于JS的运行过程,内存的占用是无可以避免的,但是如果有些已经没有使用的对象或象属性,需要及时断开引用(设置为null或者delete),以便垃圾回收。至于那些已经没有用了,只有你自己知道。
对于较大的JS对象,比如Ajax请求产生的JSON对象。仅仅设置为null并不能达到回收的效果,垃圾回收似乎不擅长处理大数据。只有循环delete才能达到效果。
 
delete原生对象
别试图delete原生对象。没意义,也没用的。。。
deleteMath.PI;// false
console.log(Math.PI);// 3.141592653589793
试图删除浏览器的宿主对象更不靠谱。
delete window.alert;// true
typeof window.alert;// 'function',表明实际上并没有真正删除
 
属性特性
ECMAScript-262中提到,每个属性都有来自下列一组属性中的零个或多个特性--ReadOnly, DontEnum, DontDelete 和Internal。这里我们需要关心的是DontDelete特性。DontDelete就是一个特殊的标记,用来表明某一个属性能否被删除。
在声明变量或者函数时,他们都变成了当前上下文对象的属性(对于函数代码来说是活动对象,对于全局代码来说则是全局对象),而值得注意的是这些属性在创建时都带有DontDelete标记.但是任何显式或者隐式的赋值语句所产生的属性并不会带有这个标记!这就是为什么有一些属性我们可以删除,但另一些却不可以。
现在在看回前面提到的代码,便好理解很多:
/* x被正常声明,所以带有DontDelete标记,从而不能被删除! */
var x =1;
delete x;// false
console.log(x);// 1
 
/* x直接通过一个赋值而没有声明,不会持有DontDelete标记,才可以被删除! */
x =1;
delete x;// true
console.log(x);// undefined
 
arguments
跟Math.PI的例子一样,一些原生对象是自动持有这个标记的,从而不能被删除。
(function(){
     /*arguments对象默认持有DontDelete标记,不能被删除。 */
     delete arguments;// false
     typeof arguments;// 'object'
})();
 
/* 函数的传入参数也是一样的 */
(function(arg){
     delete arg;// false
     console.log(arg);// 1
})(1);
 
邪恶的eval
通过eval执行的代码中,通过var声明的变量虽然与正常的var声明变量同属于Global对象,但它们不具有DontDelete特性,能被删除。
eval('var x = 1;');// 1
delete x;// true
console.log(x);// undefined
但是这也有一点例外,eval的代码中的函数内通过var定义的变量具有DontDelete,不能被删除。
var x =eval('(function() { var x = 1; delete x; return x; })();');
console.log(x);// 1;
这点就可以解释,为何会有前面提到的Firebug的bug:Firebug控制台中所有文本似乎是作为eval代码的方式来解析和执行的,而不是作为一个全局对象或函数对象,显然,任何声明的变量没有DontDelete特性,因此通过在Firebug的控制台里输入代码创建出来的变量,是可以删除的。相对来说,Chrome等webkit系的浏览器的开发工具没有这个问题. 这一点在调试代码时需要予以注意.
小结
     分两次啰嗦了那么多,可以做点有总结性意义的事情了:
     > delete操作符有助于垃圾回收,可以释放不需要的内存空间。
     > delete操作符,删除的只是引用,而不是对象本身。
     > 属性都有一个DontDelete标记,用于表明该属性是否能被delete。
     > 变量和函数的声明创建的属性都会带有DontDelete标记。
     > 任何显式或者隐式的赋值语句所产生的属性并不会带有DontDelete标记。
     > 原生对象总会带有DontDelete标记,浏览器宿主对象更不可预料。
     > 在eval代码块中声明的变量和方法都不带有DontDelete标记。
     > 应该意识到正常全局代码和Firebug控制台运行的差异。
 
@markyun
Copy link
Author

markyun commented Jan 3, 2014

有三种不能被delete:声明的对象,arguments参数,原生对象如Math.PI

[[Configurable]]的值表示能否被delete。
属于JS高级程序设计,第6章 面向对象程序设计 139页。在 ES6 中可以通过 Object.create() 进行设置

@markyun
Copy link
Author

markyun commented Jan 3, 2014

如果它被作为属性调用,那么this的值将变成该属性的对象,否则this的值将被指派为全局对象或window。如果在严格模式下,this的值将是undefined。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment