原文: Understanding the Delete Operator in JavaScript - Chidume Nnambi
学习与理解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
.
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
来进行删除.
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
当我们不使用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
不会影响局部作用于与函数作用域
{
var g = 88
delete g
}
console.log(g)
function func() {
var funch = 90
delete funch
console.log(funch)
}
func()
88
90
如果一个本身就不存在的属性被传递给delete
, 它不会抛出一个错误, 而是会返回true
var obj = {
d: 90
}
console.log(delete obj.f);
true
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
操作符不能移除任何API内建的API, 包括Array
, Math
, Object
, Date
等. 对这些属性进行delete
操作会的到返回值false
console.log(delete Math.PI);
false
所有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
谢谢!!!