`

再谈JavaScript的数据类型问题

 
阅读更多

 

再谈JavaScript的数据类型问题

JavaScript的数据类型问题已经讨论过很多次了,但许多人还有许多书仍然沿用着错误的、混乱的一些观点,所以就再细讲一回。

提及这个讨论的原因在于argb同学在我的MSN博客(现在变成了wordproess,在这里)上的一段回复,又更早的起源则是两年前关于《JavaScript征途》一书的大讨论:

从“装B被雷劈讲起

——这个事就让它过去了过去了吧。在讨论中我提及到该书对JavaScript类型系统介绍的混乱,而argb翻出了这篇历史文章,指我的混乱更混乱。于是我列了以下几个问题给他:

=============

我想很难很快速地解释你的问题。那么,接着你的思路,我就问几个问题好了:
1、函数是不是类型?是什么类型?
2、为什么说JavaScript中的函数是“第一型”的?
3、undefined如何“包装成object”?即使你所说的是笔误,那么对于“一切都是对象”的JavaScript,undefined是什么?
4、true与Boolean(true)在类型上有什么不同?

最后强调一下你的用词问题:Undefined是类型,undefined是值,’undefined’是类型的名称。此外,应留意JavaScript中存在着值类型与引用类型。

=============

随后argb的回复让我觉得一切已经混乱到不得不讲的地步。因为此前也没有讨论过《JavaScript征途》中的类型系统问题,于是这干脆就补个功课。下面认认真真地谈谈,也顺便回复了argb同学。

再次感谢argb。若非如此,我这篇功课还要欠很久。有读者与朋友们的关心,总是好的。答疑释解,于人于已,皆成美事。

 

再谈JavaScript的数据类型问题

=============

首 先我们谈两点体会。其一,JavaScript不是纯粹的面向对象语言,它是混合语言,所以所谓“一切面向对象”既是宣传用语,也是一种语言处理技巧。仅 从“面向对象”来理解这个语言的类型,会犯很多错误。其二,ECMAScript的描述总是很准确而又迟滞于这门语言的发展。所以要理解一些现象,既要从 JavaScript的历史中去找,也要考虑到JavaScript现在的发展。ECMAScirpt是一个标准的、规范化的参考,但不是全部。

接 下来说说类型。JavaScript既是过程式语言,也是面向对象的语言。这一定程度上,也表现为:它事实上有两套类型系统。第一套类型系统是用 typeof来识别,这是这个语言的基本类型系统,只有六种类型,即undefined、number、boolean、string、object与 function。我一般也称之为基础类型系统。之所以称为“基础”,是因为第二套类型系统是以它为基础,从object这一种类型中发展起来的,即对象 类型系统。

对象类型系统用instanceof来识别,它相当于其它高级语言中的is操作/运算。面向对象的多态主要通过as和is来表达,对于JavaScript来说,由于是弱类型的(没有强制类型检查),所以不需要as。

对 象类型系统与基础类型系统存在映射关系,例如基础类型的string影射到对象系统中的String。但这只是影射,所以本质上来说string类型不是 String类型。两者本质上不同。具体来说,undefined,string、number和boolean是“值类型”;object与 function是“引用类型”。由于String、Number与Boolean在基础类型中都属于object类型,是Object()的子类,因此 是引用类型。Function()也是引用类型。所有引用类型都可以看着Object()的子类,所以任意函数也是Object()的子类。例 如"<匿名函数> instanceof Object"返回true。

undefined是值类型,它没有对应的对象类型 ——我们通常可以称之为Undefined类型,但它没有对应的构造器。undefined只有一个值,即undefined。准确地 说,undefined表明声明(或产生)了但没有值的变量。而Null也是一个类型,null是它的惟一值(按照语言规则,null也是一个关键字)。 Null类型是对象类型,亦即是引用类型。所以Null与Undefined本质的不同,是它们分属在不同的类型系统中,解释着不同类型系统中的“无”的 概念。一般来说,DOM中的某个属性或成员如果无值,应该使用null;而JavaScript运算过程中如果出现无值,应该使用undefined。

上 面强调要从“两套类型系统”的角度来理解上述类型。而这两个类型系统在JavaScript中是可以混用的,实现这一特性的技术被称为“类包装”。这是 JavaScript对Java的主要借鉴,也是后来的.NET对Java的主要借鉴之一——类包装也被称为“装箱”(以及“拆箱”)。 JavaScript中的类包装过程出现然属性存取中,即“.运算符”或“[]运算符”。当这两个运算符发现左操作数x是一个“值类型”数据时,将隐式地 调用Object(x)过程将它转为对象,因此
'abc'.length
这个运算实际上就等效于
Object('abc').length

 

最后,我们回到原始的问题上来。所以我说:
====
JavaScript 里面有6种基本类型,对象是其中一种,各种对象是“对象(object)”这一种类型中的子类(类型)。
====
是没有什么错误的。而朱先生在他的书中说:
====
- JavaScript 语言只有 3 种最原始的数据类型:数值型、字符串型和布尔型
- JavaScript 还定义了几个特殊的数据类型,如空类型(null)和未定义类型(undefined)。
- 基本数据类型按值传送,而复杂数据类型按引用传送。
====
这 几个观点都不太靠谱。其一,这三种是原始的数据类型没错,但并不是“只有3种”,这个稍后一点我再说。其二,空类型与未定义类型这两种说法都是错的,应该 是Null类型和Undefined类型——小写的,是它们的值;首字母大写才是它们的类型。其三,undefined也是按值传递的,然而在朱先生的分 类里头,就不知道如何归属。他起码提到了:原始数据类型,特殊数据类型,值(传递的)类型,引用(传递的)类型。这样复杂的分类,会更容易让读者混淆。


最 后说一下“原始的数据类型”。这个用词在ECMAScript里面有,称为"primitive types",但这个概念主要是从“primitive values"里面引申出来的,而非单独作为一个类型分类的依据——ECMAScript中只提到过一次primitive type,并且也没有称之为“types”。ECMAScript用“primitive values"来说明一些类型的原始值,例如Boolean Types具有原始值true/false。但这并没有说明Boolean对象类型与值类型之间的差异或关系,例如不能表明true与 Boolean(true)之间有什么不同。

ECMAScript中使用“primitive values",并陈述了这些原始值的定义,主要是ECMAScript要兼顾JavaScript语言的实现方案。在ECMAScript中相当大的一 部分是在描述一个语言的实现,许多地方需要将一个对象转换成“primitive values",或使用“primitive values"这样的名词来讲述它的实际实现——但我必须强调,这与类型系统的定义与规划没什么关系。例如ECMA讲述“属性(property)”这一 概念时,原文是:
“Properties are containers that hold other objects, primitive values, or functions. A primitive value is a member of one of the following built-in types: Undefined, Null, Boolean, Number, and String; an object is a member of the remaining built-in type Object; and a function is a callable object. A function that is associated with an object via a property is a method.”

翻译过来就是:
属性可以包括其它对象、原始值或函数。一 个原始值(primitive value)是以下内建类型的一个成员(即一个值,value):Undefined, Null, Boolean, Number, 以及String;一个对象(object)是其它内建对象类型的一个成员(实例,instance),函数(function)是一个可调用的对象。如 果一个函数作为一个对象的属性,则我们称为方法(method)。

上面的描述与“类型系统如何划分”有什么关系吗?没有。关键在于上列5种原始值,都是可以跨语言来声明或使用的。然而,要更细节地叙述这一点,需要完整地讨论ECMAScript如何声明与实现语言的全过程。

所以如果将“primitive value"作为类型系统来讨论,就会相当地令人混乱了。这也是我一开始提出那几个问题的原因。

 

最后,强调一点。function是类型。所以你提到:
====
函数不是类型,函数是函数,是类型(type)为object的一个分类(class)
====
大概是所有混乱的总和了。关于第一型(first-class data types)的问题就不再讲了,以前已讲得太多。大家自己翻吧。

提及这个讨论的原因在于argb同学在我的MSN博客(现在变成了wordproess,在这里)上的一段回复,又更早的起源则是两年前关于《JavaScript征途》一书的大讨论:

从“装B被雷劈讲起

——这个事就让它过去了过去了吧。在讨论中我提及到该书对JavaScript类型系统介绍的混乱,而argb翻出了这篇历史文章,指我的混乱更混乱。于是我列了以下几个问题给他:

=============

我想很难很快速地解释你的问题。那么,接着你的思路,我就问几个问题好了:
1、函数是不是类型?是什么类型?
2、为什么说JavaScript中的函数是“第一型”的?
3、undefined如何“包装成object”?即使你所说的是笔误,那么对于“一切都是对象”的JavaScript,undefined是什么?
4、true与Boolean(true)在类型上有什么不同?

最后强调一下你的用词问题:Undefined是类型,undefined是值,’undefined’是类型的名称。此外,应留意JavaScript中存在着值类型与引用类型。

=============

随后argb的回复让我觉得一切已经混乱到不得不讲的地步。因为此前也没有讨论过《JavaScript征途》中的类型系统问题,于是这干脆就补个功课。下面认认真真地谈谈,也顺便回复了argb同学。

再次感谢argb。若非如此,我这篇功课还要欠很久。有读者与朋友们的关心,总是好的。答疑释解,于人于已,皆成美事。

 

再谈JavaScript的数据类型问题

=============

首 先我们谈两点体会。其一,JavaScript不是纯粹的面向对象语言,它是混合语言,所以所谓“一切面向对象”既是宣传用语,也是一种语言处理技巧。仅 从“面向对象”来理解这个语言的类型,会犯很多错误。其二,ECMAScript的描述总是很准确而又迟滞于这门语言的发展。所以要理解一些现象,既要从 JavaScript的历史中去找,也要考虑到JavaScript现在的发展。ECMAScirpt是一个标准的、规范化的参考,但不是全部。

接 下来说说类型。JavaScript既是过程式语言,也是面向对象的语言。这一定程度上,也表现为:它事实上有两套类型系统。第一套类型系统是用 typeof来识别,这是这个语言的基本类型系统,只有六种类型,即undefined、number、boolean、string、object与 function。我一般也称之为基础类型系统。之所以称为“基础”,是因为第二套类型系统是以它为基础,从object这一种类型中发展起来的,即对象 类型系统。

对象类型系统用instanceof来识别,它相当于其它高级语言中的is操作/运算。面向对象的多态主要通过as和is来表达,对于JavaScript来说,由于是弱类型的(没有强制类型检查),所以不需要as。

对 象类型系统与基础类型系统存在映射关系,例如基础类型的string影射到对象系统中的String。但这只是影射,所以本质上来说string类型不是 String类型。两者本质上不同。具体来说,undefined,string、number和boolean是“值类型”;object与 function是“引用类型”。由于String、Number与Boolean在基础类型中都属于object类型,是Object()的子类,因此 是引用类型。Function()也是引用类型。所有引用类型都可以看着Object()的子类,所以任意函数也是Object()的子类。例 如"<匿名函数> instanceof Object"返回true。

undefined是值类型,它没有对应的对象类型 ——我们通常可以称之为Undefined类型,但它没有对应的构造器。undefined只有一个值,即undefined。准确地 说,undefined表明声明(或产生)了但没有值的变量。而Null也是一个类型,null是它的惟一值(按照语言规则,null也是一个关键字)。 Null类型是对象类型,亦即是引用类型。所以Null与Undefined本质的不同,是它们分属在不同的类型系统中,解释着不同类型系统中的“无”的 概念。一般来说,DOM中的某个属性或成员如果无值,应该使用null;而JavaScript运算过程中如果出现无值,应该使用undefined。

上 面强调要从“两套类型系统”的角度来理解上述类型。而这两个类型系统在JavaScript中是可以混用的,实现这一特性的技术被称为“类包装”。这是 JavaScript对Java的主要借鉴,也是后来的.NET对Java的主要借鉴之一——类包装也被称为“装箱”(以及“拆箱”)。 JavaScript中的类包装过程出现然属性存取中,即“.运算符”或“[]运算符”。当这两个运算符发现左操作数x是一个“值类型”数据时,将隐式地 调用Object(x)过程将它转为对象,因此
'abc'.length
这个运算实际上就等效于
Object('abc').length

 

最后,我们回到原始的问题上来。所以我说:
====
JavaScript 里面有6种基本类型,对象是其中一种,各种对象是“对象(object)”这一种类型中的子类(类型)。
====
是没有什么错误的。而朱先生在他的书中说:
====
- JavaScript 语言只有 3 种最原始的数据类型:数值型、字符串型和布尔型
- JavaScript 还定义了几个特殊的数据类型,如空类型(null)和未定义类型(undefined)。
- 基本数据类型按值传送,而复杂数据类型按引用传送。
====
这 几个观点都不太靠谱。其一,这三种是原始的数据类型没错,但并不是“只有3种”,这个稍后一点我再说。其二,空类型与未定义类型这两种说法都是错的,应该 是Null类型和Undefined类型——小写的,是它们的值;首字母大写才是它们的类型。其三,undefined也是按值传递的,然而在朱先生的分 类里头,就不知道如何归属。他起码提到了:原始数据类型,特殊数据类型,值(传递的)类型,引用(传递的)类型。这样复杂的分类,会更容易让读者混淆。


最 后说一下“原始的数据类型”。这个用词在ECMAScript里面有,称为"primitive types",但这个概念主要是从“primitive values"里面引申出来的,而非单独作为一个类型分类的依据——ECMAScript中只提到过一次primitive type,并且也没有称之为“types”。ECMAScript用“primitive values"来说明一些类型的原始值,例如Boolean Types具有原始值true/false。但这并没有说明Boolean对象类型与值类型之间的差异或关系,例如不能表明true与 Boolean(true)之间有什么不同。

ECMAScript中使用“primitive values",并陈述了这些原始值的定义,主要是ECMAScript要兼顾JavaScript语言的实现方案。在ECMAScript中相当大的一 部分是在描述一个语言的实现,许多地方需要将一个对象转换成“primitive values",或使用“primitive values"这样的名词来讲述它的实际实现——但我必须强调,这与类型系统的定义与规划没什么关系。例如ECMA讲述“属性(property)”这一 概念时,原文是:
“Properties are containers that hold other objects, primitive values, or functions. A primitive value is a member of one of the following built-in types: Undefined, Null, Boolean, Number, and String; an object is a member of the remaining built-in type Object; and a function is a callable object. A function that is associated with an object via a property is a method.”

翻译过来就是:
属性可以包括其它对象、原始值或函数。一 个原始值(primitive value)是以下内建类型的一个成员(即一个值,value):Undefined, Null, Boolean, Number, 以及String;一个对象(object)是其它内建对象类型的一个成员(实例,instance),函数(function)是一个可调用的对象。如 果一个函数作为一个对象的属性,则我们称为方法(method)。

上面的描述与“类型系统如何划分”有什么关系吗?没有。关键在于上列5种原始值,都是可以跨语言来声明或使用的。然而,要更细节地叙述这一点,需要完整地讨论ECMAScript如何声明与实现语言的全过程。

所以如果将“primitive value"作为类型系统来讨论,就会相当地令人混乱了。这也是我一开始提出那几个问题的原因。

 

最后,强调一点。function是类型。所以你提到:
====
函数不是类型,函数是函数,是类型(type)为object的一个分类(class)
====
大概是所有混乱的总和了。关于第一型(first-class data types)的问题就不再讲了,以前已讲得太多。大家自己翻吧。

分享到:
评论

相关推荐

    浅谈JavaScript数据类型

    本文从javascript的数据类型的简介开始谈起,接着讲述了javascript的6种数据类型,分别为Number、Boolean、String、Null、Undefined和Object,并给出了一个typeof操作符的示例,这里分享给大家。

    浅谈JavaScript数据类型及转换

    JavaScript数据类型 1.Boolean(布尔) 布尔:(值类型)var b1=true;//布尔类型 2.Number(数字) 数值:(值类型)var n1=3.1415926;//数值类型 n1.toFixed(3);//四舍五入保留3位小数。 3.String(字符串) 代码...

    javascript中的五种基本数据类型

    在javascript中申明变量使用的关键字都是var,这点与其他的编程语言不尽相同,但是javascript亦含有五种基本的数据类型(也可以说是简单数据类型),它们分别是:Undefined,Null,Boolean,Number和String。...

    浅谈javascript的数据类型检测

    悟透JavaScript&gt;&gt;写得太传神,印象太深刻了】 二、javascript的数据类型检测 1、万能的typeof 我们先测试一下通过typeof来获取简单数据类型。什么也别说了,上代码是王道: 代码如下: // 获取变量obj的数据类型 ...

    浅谈javascript六种数据类型以及特殊注意点

    在js中常见的六种数据类型:String类型、Null类型、Number类型、Boolean类型、Object类型。 1、typeof的注意点 涉及到数据类型,不免会提到,操作符 typeof。要注意: 1、typeof是操作符,不是方法。虽然我们经常...

    浅谈javascript中的数据类型转换

    本文主要对javascript中的数据类型转换进行介绍,具有一定的参考价值,下面跟着小编一起来看下吧

    浅谈javascript中基本包装类型

    在JavaScript中,和JAVA类似,也提供了对于基本数据类型的包装类型。例如Number、Boolean、String类型。下面我们就来详细探讨下吧。

    浅谈js基本数据类型和typeof

    JavaScript数据类型是非常简洁的,它只定义了6中基本数据类型 •null:空、无。表示不存在,当为对象的属性赋值为null,表示删除该属性 •undefined:未定义。当声明变量却没有赋值时会显示该值。可以为变量赋值为...

    再谈Javascript中的基本类型和引用类型(推荐)

    js中数据类型的值包括:基本类型值和引用类型值 基本数据类型:undefined;null;boolean;number;string 引用类型值:保存在内存中,js不允许直接访问内存位置,因此时操作引用而不是实际对象 二、如何检测数据类型 1....

    浅谈Javascript面向对象编程

    一、数据类型与包装类 包装类 …… 类型名 …… 常见值 …… 分类 Number …… number …… 123.123 …… 基本数据类型 Boolean …… Boolean …… true、false …… 基本数据类型 String …… string …… “hello...

    JavaScript实战

    2.3 数据类型 17 2.3.1 数值 17 2.3.2 字符串 18 2.3.3 Boolean 19 2.4 变量 19 2.4.1 创建变量 20 2.4.2 使用变量 22 2.5 操作数据类型和变量 23 2.5.1 基本算术 24 2.5.2 操作的顺序 25 2.5.3 组合字符串 25 2.5.4...

    浅谈Javascript数组(推荐)

    在程序语言中数组的重要性... 数组,即Array类型,是开发中最常用的类型之一,javascript中的数组和其他语言最大的区别就是每一项可以保存任何类型的数据,而且数组的大小是可以动态调整的,有点绕?看看代码吧  1.

    浅谈JavaScript中的string拥有方法的原因

    我们都知道,JavaScript数据类型分两大类,基本类型(或者称原始类型)和引用类型。 基本类型的值是保存在栈内存中的简单数据段,它们是按值访问的。JS中有五种基本类型:Undefined、Null、Boolean、Number和String...

Global site tag (gtag.js) - Google Analytics