本文深入解析JavaScript的六种数据类型,辨析弱类型与强类型的本质差异。重点对比null与undefined的历史渊源及行为表现,并详细讲解对象类型中的属性描述符(Data和Accessor Descriptor),通过Object.defineProperty演示如何控制属性的writable、configurable及enumerable特性。
JavaScript数据类型概述
学习一门编程语言主要涉及两个核心要素:语法与数据类型。类C语言的语法通常包含条件判断、循环、函数定义及算术运算等,面向对象语言在此基础上增加了对象概念。语法是语言设计者预设的规则体系,尽管不同语言的具体语法存在差异,但共通点较多。对于熟悉一门或多门编程语言的开发人员而言,掌握新语言的语法通常不是障碍(除非是从类C语言转向Lisp等差异极大的语言)。学习的重心往往在于数据类型及其相关操作,正如那句经典名言所言:“数据结构加算法等于程序”。此外,部分语言的语法设计本身可能存在缺陷(JavaScript的情况较为典型),我们无需过度深究这些设计瑕疵,除非是出于极客精神的探索需求。
本文将全面且详细地介绍JavaScript中的数据类型体系。
弱类型与强类型的区别
弱类型与强类型的对比
基于JavaScript的设计哲学,它被归类为一种弱类型语言。在此有必要厘清弱类型与强类型的核心区别。许多人误以为两者的差异仅在于“强类型语言声明变量时需指定类型,而弱类型则不需要”,这种观点并不准确。例如以下Java代码片段:
代码如下:
String s = "hello";
int l = s.getBytes().length;
编译器如何判断.length是合法表达式?因为编译器知晓s的数据类型为String,当调用String的getBytes方法时,其返回值类型为byte[],因此.length属性访问是合法的。两者真正的区别在于:
在强类型语言中,每个表达式的类型都能在编译时确定,且仅允许执行适用于该类型的操作;而弱类型语言允许对任意类型执行任何操作,只是这些操作可能在运行时引发错误。
JavaScript的六种数据类型
根据ECMAScript 5.1规范,JavaScript中共存在六种数据类型:Undefined、Null、Boolean、Number、String以及Object。前五种被归类为基本类型(Primitive Types),最后一种属于对象类型(Object Type)。
基本数据类型详解
基本数据类型
Undefined类型仅有一个值:undefined,代表“无值(no value)”,适用于所有数据类型。Null类型也仅有一个值:null,代表“空对象(no object)”,仅适用于对象类型。Boolean类型包含两个值:true与false。Number类型的值遵循IEEE 754标准的64位浮点数集合,类似于Java中的double类型,JavaScript中没有专门的整型数据结构。此外,Number类型还包含三个特殊值:NaN、Infinity和-Infinity。String类型的值由有限数量的Unicode字符组成,必须使用单引号或双引号包裹。
null与undefined的历史与行为
null与undefined的区别
null与undefined都表达了“无值(non-value)”的概念,但若严格区分:null表示“空”,而undefined表示“不存在”。未初始化的变量、函数中缺失的参数以及没有显式return值的函数,其返回值均为undefined。
在其他编程语言中,通常仅使用null来表示空值。JavaScript为何引入undefined?这源于历史原因:
JavaScript借鉴了Java的语法结构,将类型划分为基本类型与对象类型。在Java中,null用于表示空对象,JavaScript沿用了这一概念;在C语言中,null转换为数字时为0,JavaScript也采取了相同策略:
代码如下:
> Number(null)
0
> 5 + null
5
在JavaScript 1.0版本中,尚未引入异常处理机制(exception handling)。针对某些异常情况(如未初始化的变量、调用函数时缺失参数等),需要将其标记为一种特殊值。null本是一个不错的选择,但Brendan Eich(JavaScript之父)同时希望避免以下两点: - 该特殊值不应具备引用特性,因为这是对象特有的属性 - 该特殊值不应能转换为0,因为这会掩盖程序中的错误 基于上述原因,Brendan Eich选择了undefined,它可以被强制转换为NaN。
代码如下:
> Number(undefined)
NaN
> 5 + undefined
NaN
当与JSON对象交互时,两者的表现截然不同:
代码如下:
> JSON.parse(null)
null
> JSON.parse(undefined)
//Firfox SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
//Chrome SyntaxError: Unexpected token u
> JSON.stringify(null)
"null"
> JSON.stringify(undefined)
undefined
对象类型与属性描述符
对象类型
JavaScript作为一门脚本语言,其核心功能较为精简,许多高级功能(如文件读写、网络通信等)由宿主环境提供。宿主环境与JavaScript语言之间的桥梁是对象。宿主环境通过提供一系列符合JavaScript语法的对象,来提供各种各样的功能。
在《JavaScript面向对象编程》一文中(如果你不清楚prototype的概念,强烈建议阅读该文),我多次强调对象在JavaScript中本质上是一系列键值对的集合,类似于Java中的HashMap。不过,JavaScript对象的属性可以包含描述符(property descriptor),这是HashMap所不具备的特性。
数据描述符与访问描述符
属性描述符
属性描述符分为两类:
数据描述符(data descriptor),包含一系列布尔值,用于说明该属性是否允许修改或删除。 访问描述符(accessor descriptor),包含get与set函数。 这两种描述符均为对象,它们都拥有以下两个布尔属性:
configurable:用于指定该描述符是否允许修改或删除,默认为false。 enumerable:用于指定在遍历对象(使用for...in循环或Object.keys方法)时,是否访问该属性,默认为false。 除了上述两个共有属性外,数据描述符还包含以下两个属性: - value:用于指定该属性的值,默认为undefined - writable:用于指定该属性的值是否允许被修改,默认为false
访问描述符还包含以下两个属性: - get:用于指定访问该属性时的访问器(getter,本质是一个函数),该访问器的返回值即为该属性的值,默认为undefined - set:用于指定访问该属性时的赋值器(setter,本质是一个函数),该赋值器接受一个参数,默认为undefined
我们可以使用Object.defineProperty来设置对象的属性描述符。例如:
代码如下:
// using __proto__
Object.defineProperty(obj, 'key', {
__proto__: null, // no inherited properties
value: 'static' // not enumerable
// not configurable
// not writable
// as defaults
});
通过上述示例可以看出,描述符具有继承特性。我们在示例中显式地将描述符对象的__proto__设为null,从而避免了从Object.prototype中继承相应属性。当然,我们也可以显式地设置描述符的所有属性:
代码如下:
// being explicit
Object.defineProperty(obj, 'key', {
enumerable: false,
configurable: false,
writable: false,
value: 'static'
});
这样的效果与第一段代码完全一致。
下面再举一个访问描述符的例子:
代码如下:
// Example of an object property added with defineProperty with an accessor property descriptor
var bValue = 38;
Object.defineProperty(obj, 'key', {
get: function() { return bValue; },
set: function(newValue) { bValue = newValue; },
enumerable: true,
configurable: true
});
需要注意的是,不能混淆访问描述符与数据描述符。以下写法是错误的:
代码如下:
// You cannot try to mix both:
Object.defineProperty(obj, 'conflict', {
value: 0x9f91102,
get: function() { return 0xdeadbeef; }
});
// throws a TypeError: property descriptors must not specify a value
// or be writable when a getter or setter has been specified
typeof操作符与null的类型标志
typeof
如果想在运行时获知某变量的类型,可以使用typeof操作符。typeof的返回值如下表所示:

其中有一处需要注意,即typeof null == "object"。按照ECMAScript 5.1标准,Null类型应属于基本类型,为何此处返回object?原因如下:
在JavaScript 1.0中,JavaScript中的值由一个类型标志(type tag)和一个实际值组成的结构表示。对象的类型标志为0,而null在C语言中表示NULL指针(0x00),因此null的类型标志也被设为0。
以上就是本文的全部内容了,有需要的小伙伴参考下吧。