原文: Understanding the Delete Operator in JavaScript - Chidume Nnambi
学习与理解delete操作符如何处理可变更与不可变更属性, 以及一些别的东西.
delete操作符
根据ECMA的定义与解释:
Delete(O, P)
这个方法常常被用来移除一些对象中的特定的属性. 如果属性本身是不可变更的, 那么它将抛出一个错误.
这个操作符在调用时被传入O - 要变更的对象, P - 要移除的属性的key.
let obj = {
d: 88
}
console.log(obj.d);
delete obj.d;
console.log(obj.d);
我们创建了一个包含d属性, 且值为88的对象obj. 首先我们打印输出d的值, 很显然会出现88. 然后我们通过delete操作符来移除这个属性, 然后我们在打印它. 结果将会是undefined.
88
undefined
undefined是JS中用来表示_非值_的一个基本数据类型, 意味着数据被定义过了, 但尚未被赋值. 所以当通过delete删除了对象的一个属性之后, 这个属性的值就会变成undefined.
不可变更(non-configuration)属性与delete
delete操作符只会对可变更(configuration)属性起作用. delete不能移除对象的一个不可变更的属性.
我们的obj对象是可变更的, 所以d属性能够被移除. 如果我们定义一个不可变更的属性, 那么delete就不能删除这个属性:
let obj = {
d: 88
}
Object.defineProperty(obj, d, { configurable: false });
console.log(obj.d);
delete obj.d;
console.log(obj.d);
88
88
可以看到, 属性d并没有被移除. 我们通过Object#defineProperty方法把d属性设置为不可变更的, 只需要把configurable设置为false就行.
默认情况下, 对象的属性都是可变更的.
let obj = {
d: 88
}
console.log(Object.getOwnPropertyDescriptor(obj, 'd'));
{ value: 88,
writable: true,
enumerable: true,
configurable: true }
之后如果通过Object#defineProperty方法来将configurable设置为false, 这个属性就不会被delete操作符删除了.
var, let, const与delete
var, let, const声明的属性(变量)都是不可变更的, 因此它们声明的属性(变量)也不能通过delete来进行删除.
const a = 1;
let b = 2;
var c = 3;
console.log(a, b, c);
delete a;
delete b;
delete c;
console.log(a, b, c);
1 2 3
1 2 3
函数与delete
函数(Function)也不能通过delete来进行删除
function func() {
console.log('inside func);
}
delete func;
func();
inside func
但是, 作为属性被定义的函数是可以被删除的
var obj = {
d: function() {
console.log('inside obj.d function');
}
}
obj.d();
delete obj.d;
obj.d();
inside obj.d function
TypeError: obj.d is not a function
与普通的属性一样, 如果这个属性被设置为不可变更的, 那么这个属性也不能被delete操作符删除
const obj = {
d: function() {
l("inside obj d function")
}
}
obj.d()
Object.defineProperty(obj, "d", { configurable: false })
delete obj.d
obj.d()
inside obj d function
inside obj d function
delete与全局作用域
当我们不使用var或者let或者const定义了一个全局作用域下的变量时, 这个变量是可变更的
f = 90;
console.log(Object.getOwnPropertyDescriptor(global, 'f'))
{ value: 90,
writable: true,
enumerable: true,
configurable: true }
因此他们是可以被delete移除的.
f = 90;
console.log(f);
delete f;
console.log(f);
90
l(f)
^
ReferenceError: f is not defined
delete与局部作用域
delete不会影响局部作用于与函数作用域
{
var g = 88
delete g
}
console.log(g)
function func() {
var funch = 90
delete funch
console.log(funch)
}
func()
88
90
delete与不存在的属性
如果一个本身就不存在的属性被传递给delete, 它不会抛出一个错误, 而是会返回true
var obj = {
d: 90
}
console.log(delete obj.f);
true
delete与原型链
delete不会影响到一个对象的原型链
function Foo() {
this.bar = 90;
}
Foo.prototype.bar = 88;
var f = new Foo();
我们创建构造器Foo, 并且在它的属性和原型链属性上都声明了一个属性bar, 它们同名但具有不同的值.
当直接引用这个对象是, Foo构造函数中定义的bar会被返回.
f.bar // 90
当我们删除了这个属性:
delete f.bar
他只会影响到Foo构造函数中定义的bar, 而不会影响到原型链中的. 当我们再次应用这个属性时, 原型链中的bar就会被返回
console.log(f.bar);
delete f.bar
console.log(f.bar);
90
88
delete与JS内建静态属性
delete操作符不能移除任何API内建的API, 包括Array, Math, Object, Date等. 对这些属性进行delete操作会的到返回值false
console.log(delete Math.PI);
false
delete与其在数列上的留洞性质(holey nature)
所有JS中的类型都继承自JSObject的C++等价定义. 每一个我们定义的对象的属性, 都是C++ JSObject的一个成员
obj = {
d: 90,
f: 88
}
JSObject {
d -> 90,
f -> 88
}
数列也属于JSObject, 只是他们之间存在一些差别. 差别在于, Array的JSObject并不是由数列自己定义的, 而是通过数字排序定义的
obj = [90, 88];
JSObject {
0 -> 90
1 -> 88
}
这也是为什么我们在引用数组时的方式obj[1]看起来与对象很类似的原因了. 在数组中, 这些数字就是它的属性.
在我们上述的数组中, 它有两个属性0和1.
类似我们对对象作出操作, 删除数组中的第一个属性(元素), 可以这么做:
delete obj[0];
console.log(obj[0]); // undefined
但是这个操作并没有减少数组中元素的个数
obj = [90, 88];
console.log(obj.length); // 2
delete obj[0];
console.log(obj.length); //2
//obj
index: 0 1
[ , 88];
这样就在数列中留下了_孔洞_
实际上在对象中, delete操作也并不是完全抹除被删除的属性, 而是将它们的值设置为undefined. 可以通过对这些属性重新赋值来填满这些被留下的_孔洞_
总结
我们了解delete操作符是用来干什么的, 它对可变更与不可变更属性的影响, 它对全局与局部作用域的影响, 它对数组等_有洞_的属性的影响.
了解这些, 你能在使用delete操作符时变得更舒服, 更谨慎.
如果有任何问题, 请随意问我, 或者发一封邮件或者DM
谢谢!!!