原生JS:delete、in、typeof、instanceof、void详解

来源:http://www.chinese-glasses.com 作者:Web前端 人气:162 发布时间:2020-03-16
摘要:时间: 2019-11-15阅读: 106标签: 语法 delete、in、typeof、instanceof、void详解 本文出自周爱民《JavaScript 核心原理解析》专栏。 本文参考MDN做的详细整理,方便大家参考[MDN]( https://developer.mozi

时间: 2019-11-15阅读: 106标签: 语法

delete、in、typeof、instanceof、void详解

本文出自周爱民《JavaScript 核心原理解析》专栏。

本文参考MDN做的详细整理,方便大家参考[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)**
**

你好,我是周爱民,今天想和大家从 JavaScript 中最不起眼的、使用率最低的一个运算——delete 聊起。

delete

你知道,JavaScript 是一门面向对象的语言。它很早就支持了 delete 运算,这是一个元老级的语言特性。但细追究起来,delete 其实是从 JavaScript 1.2 中才开始有的,与它一同出现的,是对象和数组的字面量语法。有趣的是,JavaScript 中最具恶名的 typeof 运算其实是在 1.1 版本中提供的,比 delete 运算其实还要早。这里提及 typeof 这个声名狼籍的运算符,主要是因为 delete 的操作与类型的识别其实是相关的。

delete 运算符用来删除对象的自有属性.

习惯中的“引用”

语法:delete expression

早期的 JavaScript 在推广时,仍然采用传统的数据类型的分类方法,也就是说,它宣称自己同时支持值类型和引用类型的数据,并且呢,所谓值类型中的字符串是按照引用来赋值和传递引用(而不是传递值)的。这些都是当时“开发人员的概念集”中已经有的、容易理解的知识,不需要特别地解释。

  • expression 的计算结果应该是一个对象的属性引用,如果不是一个对象的属性引用,那么,delete不会起任何作用,直接返回true。

  • 严格模式中,如果属性是一个不可配置(non-configurable)属性,删除时会抛出异常,非严格模式下返回 false。其他情况都返回 true。

  • delete 操作符与直接释放内存(只能通过解除引用来间接释放)没有关系。可查看内存管理页面。

  •  可以使用 delete 操作符来删除一个隐式声明的全局变量,也就是没有使用 var 定义的全局变量

  •  如果 delete 操作符删除成功,则被删除的属性将从所属的对象上彻底消失。然后,如果该对象的原型链上有一个同名属性,则该对象会从原型链上继承该同名属性

但是什么是引用类型呢?

删除属性后,属性将从所属的对象上彻底消失,而删除数组元素后,会在数组内留下一个空洞,读取它的值将为undefined,数组长度不变;

在这件事上,JavaScript 偷了个懒,它强行定义了“Object 和 Function 就是引用类型”。这样一来,引用类型和值类型就给开发人员讲清楚了,对象和函数呢,也就可以理解了:它们按引用来传递和使用。

delete无法删除:尝试删除无法删除的属性返回false,若删除成功或删除操作不起作用时均返回true

绝大多数情况下,这样解释起来是行得通的。但是到了 delete 运算这里,就不行。

1、内置核心、客户端属性不能删除;

因为这样一来,delete 0就是删除一个值,而delete x就既可能是删除一个值,也可能是删除一个引用。然而,当时 JavaScript 又同时约定:那些在 global 对象上声明的属性,就“等同于”全局变量。于是,这就带来了第三个问题:delete x还可能是删除一个 global 对象上的属性。而它在执行这个操作的时候,看起来却像是一个全局变量(的名字)。

2、用户通过var语句声明的变量不能删除;

这中间有哪些细节的区别呢?delete 这个运算的表面意思,是该运算试图销毁某种东西。然而,delete 0中的 0 是一个具体的、字面量表示的“值”。一个字面量值“0”如何在现实世界中销毁呢?假定它销毁了,那是不是说,在这个语言当前的运行环境中,就不能使用 0 这个值了呢?显然,这不合理。

3、通过function语句定义的函数和函数参数也不能删除

所以,JavaScript 认为“所有删除值的 delete 就直接返回 true”,表明该行为过程中没有异常。很不幸,JavaScript 1.2 的时代并没有结构化异常处理(即 try…catch 语句)。所以,通过函数调用中返回 true 来表明“没有异常”,其实是很常规的做法。

4、不可配置的属性无法删除

然而,返回值只表明执行过程中没有异常,但实际的执行行为是“什么也没发生”。你显然不可能真的将“0”从执行系统中清理出去。

5、不能删除继承来属性,不过可以从原型上直接删掉它

那么接下来,就还剩下删除变量和删除属性。由于全局变量实际上是通过全局对象的属性来实现的,因此删除变量也就存在识别这两种行为的必要性。例如:

6、有一个例外:eval内的显示声明的属性可以被删除!eval('var a = 1')

delete x

这行代码究竟是在删除什么呢?出于 JavaScript 是动态语言这项特性,所以从根本上来说,我们是没有办法在语法分析期来判断x的性质的。所以现在,需要有一种方法在运行期来标识x的性质,以便进一步地处理它。

  1. function foo(){
  2. delete x;
  3. let x;
  4. }

这就导致了一种新的“引用”类型呼之欲出。(“引用”到底有什么用?它对我们的编程有什么实际的影响呢?我会在《JavaScript 核心原理解析》专栏的 02 讲中详细为你讲解。)

inhttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in

到底在删除什么?

语法:prop in objectName 

探索工作往往如此,是所谓“进五退一”,甚至是“进五退四”。在今后的专栏文章中,你往往会看到,我在碰触到一种新东西的时候会竭力向前,但随后又后退好几步,再来讨论一些更基础层面的东西。这是因为如果不把这些基础概念说得清楚明白,那么往前冲的那几步常常就被带偏了方向。

  • prop 一个字符串类型或者symbol类型的属性名,或者数组索引。

  • objectName 需要检测的对象(必须是一个对象,不能是原始类型)比如,可以是一个String包装对象,但不能是一个字符串原始值。

  • 它会遍历objectName的所有属性(含继承属性、键为 Symbols  类型的属性)

  • 如果你使用 delete 运算符删除了一个属性,则 in 运算符对所删除属性返回 false

  • 如果你只是将一个属性的值赋值为 undefined,而没有用 delete 删除它,则 in 运算仍然会返回true。

  • 如果一个属性是从原型链上继承来的,in 运算符也会返回 true。

一如现在这个问题:delete 0到底是在删除什么?

typeof

对于一门编译型语言来说,所谓“0”,就是上面所述的一个值,它可以是基础值(Primitive values),也可以是数值类型。但如果将这个问题上升到编译之前的、所谓语法分析的阶段,那么“0”就会被称为一个记号(Tokens)。一个记号是没有语义的,记号既可以是语言能识别的,也可以是语言不能识别的。唯有把这二者同时纳入语言范畴,那么这个语言才能识别所谓的“语法错误”。

typeof 运算符用来判断给定对象的类型.

delete 不仅仅是要操作 0 或 x 这样的单个记号或标识符(例如变量)。因为这个语法实际起作用的是一个对象的属性,也就是“删除对象的成员”。那么它真正需要的语法其实是:

语法:typeof operand

delete obj.x
  • operand 是一个表达式,表示对象或原始值,其类型将被返回。返回一个字符串,指示未经计算的操作数的类型

  • operand无论引用的是什么类型的对象,只要带引用,它都返回 "object"

  • 在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是0。由于 null 代表的是空指针(大多数平台下值为0x00),因此,null的类型标签也成为了0,typeof null就错误的返回了"object"。在ES6中仍然未被修复

  • 对正则表达式字面量的类型判断在某些浏览器中不符合标准:

只不过因为全局对象的成员可以用全局变量的形式来存取,所以它才有了

  1. typeof /s/ === 'function'; // Chrome 1-12 , 不符合 ECMAScript 5.1
  2. typeof /s/ === 'object'; // Firefox 5+ , 符合 ECMAScript 5.1
delete x
  • 在 IE 6, 7 和 8 中,大多数的宿主对象是对象,而不是函数,例如:typeof alert === 'object'

这样的语法语义而已。所以,这正好将你之前所认识的倒转过来,是删除 x 这个成员,而不是删除 x 这个值。不过终归有一点是没错的:既然没办法表达异常,而 delete 0 又不产生异常,那么它自然就该返回 true。

typeof 可能的返回值:

然而,如果你理解了delete obj.x,那么就一定会想到:obj.x既不是之前说过的引用类型,也不是之前说过的值类型,它与typeof(x)识别的所有类型都无关。因为,它是一个表达式。

类型

结果

Undefined

"undefined"

Null (引用了空指针)

"object" (见下方)

Boolean字面量

"boolean"

Number字面量

"number"

String字面量

"string"

Symbol (ECMAScript 6 新增)

"symbol"

宿主对象(由JS环境提供)

Implementation-dependent

函数对象 ( [[Call]] 在ECMA-262条款中实现了)

"function"

任何其他对象(带引用)

"object"

所以,delelet 这个操作的正式语法设计并不是“删除某个东西”,而是“删除一个表达式的结果”:

 

delete UnaryExpression

instanceof

表达式的结果是什么?

语法:obj instanceof constructor

在 JavaScript 中表达式是一个很独特的东西,所有一切表达式运算的终极目的都是为了得到一个值,例如字符串。然后再用另外一些操作将这个值输出出来,例如变成网页中的一个元素(element)。这是 JavaScript 语言创生的原力,也是它的基础设计。也只因为有了这种设计,它才变得既像面向对象的,又像函数式语言的样子。

  • instanceof 运算符判断一个对象是否是另一个对象的实例。返回true或false

  • instanceof 运算符用来检测 constructor.prototype 是否存在于参数 obj 的原型链上(或者说:检测obj的原型链上是否存在constructor.prototype )

表达式的执行特性,以及表达式与语句的关系等等细节,回头我放在第二阶段的内容中讲给你听。现在我们只需要关注一个要点,表达式计算的结果到底是什么?因为就像上面所说的,这个结果,才是delete这个操作要删除的东西。

  1. // 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
  2. function Aoo(){}
  3. function Foo(){}
  4. Foo.prototype = new Aoo();//JavaScript 原型继承
  5. ``
  6. var foo = new Foo();
  7. console.log(foo instanceof Foo)//true
  8. console.log(foo instanceof Aoo)//true

在 JavaScript 中,有两个东西可以被执行并存在执行结果值(Result),包括语句和表达式。比如你用eval()来执行一个字符串,那么事实上,你执行的是一个语句,并返回了语句的值;而如果你使用一对括号来强制一个表达式执行,那么这个括号运算得到的,就是这个表达式的值。

  • 需要注意的是,如果表达式 obj instanceof Foo 返回true,则并不意味着该表达式会永远返回ture,因为Foo.prototype属性的值有可能会改变,改变之后的值很有可能不存在于obj的原型链上,这时原表达式的值就会成为false。
  • 另外一种情况下,原表达式的值也会改变,就是改变对象obj的原型链的情况,虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的__proto__魔法属性,是可以实现的。比如执行obj.__proto__ = {}之后,obj instanceof Foo就会返回false了。

表达式的值,在 ECMAScript 的规范中,称为“引用”。

void

这是一种称为“规范类型”的东西。

它会对给定的表达式进行求值,然后直接返回 undefined

规范中的“引用”

语法:void expression

事实上这个概念出现得也很早。从 JavaScript 1.3 开始,ECMAScript 规范就在语言定义的层面,正式地将上述的天坑补起来,推出了上面说到的这个“(真正的)引用类型”。但是,由于这个时候规范的影响力在开发人员中并不那么大,所以开发人员还是习惯性地将对象和函数称为引用,而其它类型就称为值,并且继续按照传统的理解来解释 JavaScript 中对数据的处理。

  • void 运算符通常只用于获取 undefined 的原始值,一般使用 void(0)(等同于 void 0)

  • 在使用立即执行的函数表达式时,可以利用 void 运算符让 JavaScript 引擎把一个函数识别成函数表达式而不是函数声明(语句)。效果等同于给整个函数加个'( )'

本文由10bet发布于Web前端,转载请注明出处:原生JS:delete、in、typeof、instanceof、void详解

关键词:

上一篇:Webpack的基础信息和使用方法

下一篇:没有了

最火资讯