本文深入解析JavaScript中delete操作符的行为机制。重点阐述全局变量与对象属性的微妙关系,明确var声明的变量及原型链属性因具有dontdelete特性而无法被删除。同时探讨eval环境下的例外情况以及delete返回值的判断逻辑,帮助开发者准确掌握属性删除的边界与注意事项。
变量与对象属性的关系
一、变量
在探讨JavaScript中的delete操作符之前,有必要厘清变量与对象属性之间的内在联系。
JavaScript中的变量与对象属性关系紧密,甚至在许多场景下可以视为等同。这是因为JavaScript在执行脚本前会创建一个全局对象(在浏览器环境中即window对象),所有全局变量实际上都是该全局对象的属性。当执行函数时,会创建激活对象(activation object),局部变量则是该激活对象的属性。如需深入了解,可进一步研究JavaScript的作用域与闭包机制。
代码如下:
//原型中声明的属性无法被删除
var global = 1;
this.global; // 1, 可以通过this来访问global对象
this.global2 = 2;
global2; // 12
function foo() {
var local = 36;
// 不过无法直接访问activation,
// 因此无法通过 foo.local 的方式来访问local变量
}
尽管变量看似等同于对象属性,但二者在delete操作符的处理上存在差异。变量声明必须通过var语句完成,而未通过var声明的全局变量则直接成为window对象的属性。这种机制清晰地界定了变量与对象属性的关系。
delete操作符的基本用法
二、delete操作符
delete操作符用于删除对象的属性。对于引用类型的值,它仅删除属性本身,而不删除属性所指向的对象。若对基本类型与引用类型的区别有疑问,可参考相关文档或测试以下代码:
代码如下:
var o = {};
var a = { x: 10 };
o.a = a;
delete o.a; // o.a属性被删除
console.log(o.a); // undefined
console.log(a.x); // 10, 因为{ x: 10 } 对象依然被 a 引用,所以不会被回收
此外,delete o.x与delete o["x"]效果相同。
不可删除的变量与属性
三、变量是不能删除的
通过var声明的变量和通过function声明的函数拥有dontdelete特性,是不能被删除。未通过var声明的全局变量(全局对象的属性)
代码如下:
var global = {
a: 123,
b: {
c: 1345
}
};
delete global; //无效
console.log(global)
obj = {
a: 123
};
delete obj; // 删除obj全局变量,window对象的obj属性
console.log(obj);//obj is not defined四、原型中声明的属性和对象自带的属性无法被删除
原型prototype中声明的属性和对象自带的属性(其实这些属性也是在原型prototype中的)可以认为是带有dontdelete的特性的,无法被删除。例如
代码如下:
//原型中声明的属性无法被删除
function obj() {
this.x = 1;
}
obj.prototype.x = 2;
var o = new obj();
console.log(o.x); // 1, 构造函数中定义的o.x
delete o.x;
console.log(o.x); // 2, prototype中定义的o.x,即使再次执行delete o.x也不会被删除
//对象自带的属性无法被删除
var strings = "123456";
console.log(strings.length);//6
delete strings.length;
console.log(strings.length);//依然是6
eval环境下的删除例外
五、eval语句下的几个例外
eval执行的代码中,通过var声明的变量虽然与正常的var声明变量同属于global对象,但它们不具有dontdelete特性,能被删除。但是eval的代码中的函数内通过var定义的变量具有dontdelete,不能被删除。
代码如下:
eval("var x = 42;");
x; // => 42
delete x;
x; // => referenceerror: x is not defined
eval("function f() { return 12; }");
f(); // => 12
delete f;
f(); // => referenceerror: f is not defined
//eval执行的代码中,通过var声明的变量虽然与正常的var声明变量同属于global对象,
// 但它们不具有dontdelete特性,能被删除。
eval("(function () {" +
" var x = 42;" +
" delete x;" +
" return x;" +
"})();")
// => 42
// eval的代码中的函数内通过var定义的变量具有dontdelete,不能被删除。
delete操作符的返回值机制
六、delete的返回值
delete是普通运算符,会返回true或false。当被delete的对象的属性存在并且拥有dontdelete时 返回false,否则返回true。 这里的一个特点就是,对象属性不存在时也返回true,所以返回值并非完全等同于删除成功与否。
代码如下:
function c() {
this.x = 42;
}
c.prototype.y = 12;
var o = new c();
delete o.x; // true
o.x; // undefined
"x" in o; // false
// o.x存在并且没有dontdelete,返回true
delete o.y; // true
o.y; // 12
// o自身没有o.y属性,所以返回true
// 从这里也可以看到prototype链的存在,对象自身属性和prototype属性是不同的
delete o; // false
// global.o拥有dontdelete特性所以返回false
delete undefinedproperty; // true
// global没有名为undefinedproperty的属性因此返回true
delete 42; // true
// 42不是属性所以返回true。有的实现会抛出异常(违反ecmascript标准)
var x = 24;
delete x++; // true
x; // 25
// 被删除的是x++的返回值(24),不是属性,所以返回true