原文: 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]看起来与对象很类似的原因了. 在数组中, 这些数字就是它的属性.

在我们上述的数组中, 它有两个属性01.

类似我们对对象作出操作, 删除数组中的第一个属性(元素), 可以这么做:

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

谢谢!!!

文章来源于腾讯云开发者社区,点击查看原文