JavaScript 类型系统深度解析:基本类型、对象与函数机制

作者:青柠时间:2026-10-21 21:11:03浏览:383

本文深入解析 JavaScript 的类型系统,涵盖五种基本数据类型(Undefined、Null、Boolean、Number、String)与复杂类型 Object。重点探讨 typeof 运算符的返回值特性、基本包装类型的自动装箱机制,以及函数(Function)作为特殊对象的实例化关系与创建方式,帮助开发者厘清类型判断与对象创建的底层逻辑。

引言与系列背景

原计划继续撰写由浅入深的表达式系列最后一篇,但近期团队工作突然繁忙,这种忙碌程度前所未有。不过,关注表达式系列的朋友请放心,相关内容正在撰写中。在实际工作中发现,许多开发者对 JavaScript 的基本原理存在理解上的模糊或误区,因此决定先花时间整理这些基础知识并与大家分享。起初计划只写一篇,但随着内容扩展,决定将其整理为一个系列。本系列聚焦于 JavaScript 的基础知识,虽然不涉及前沿技术,但这些基础概念有助于深入理解更复杂的特性。

  • JavaScript 基础回顾之(一) 类型
  • JavaScript 基础回顾 (二) 作用域
  • JavaScript 基础回顾之(三) 面向对象

JavaScript 类型系统概览

提到 JavaScript,给人的感觉往往是有趣且好玩。那么,具体哪些地方有趣?为什么有趣?让我们通过实践来深入理解 JavaScript,在探索中彻底掌握它。本文涵盖以下内容:

基本类型 Object 与 object 基本包装类型 值类型和引用类型 function类型

基本数据类型与 typeof 特性

基本类型 JavaScript 包含 5 种基本数据类型(也称简单数据类型):Undefined、Null、Boolean、Number、String,以及 1 种复杂数据类型 Object。


var undefinedVariable;
var nullValue = null;
var cnblogs = new Object();
var func = function () { };

typeof ("string"); 
typeof (100);
typeof (true);
typeof (undefinedVariable);
typeof (cnblogs);
typeof (undeclaredValue);
typeof (nullValue);
typeof (null)
typeof (func)

请思考以下代码的输出结果是什么?

引言与系列背景

 

  引言与系列背景

有趣点一:声明但未赋值的变量与未声明的变量,typeof 结果均为 undefined。 有趣点二:只有声明并赋值为 null,其值才为 null。 有趣点三:typeof(Object) 返回 function。 有趣点四:typeof(null) 返回 object。

Null 和 Undefined 这两种类型各自仅有一个值,即 null 和 undefined。从逻辑层面看,null 值表示一个空对象的指针,这解释了为什么 typeof(null) 会返回 Object。此外,undefined 是派生自 null 值的,因此……

有趣点五:null == undefined 的比较结果为 true。

然而,Null 和 Undefined 毕竟是两种不同的类型。即便它们存在父子类关系,在 C# 等语言中父类与子类通常不相等。实际上,null == undefined 是 ECMAScript 规范中的硬性规定,在进行相等性测试时强制返回 true。这类似于在 C# 中重写了 equals 方法的行为。

至于为什么 typeof(Object) 返回 function,请参见下文关于 Object 与 object 的讨论。

Object 与 object 的区别

Object 与 object 《JavaScript 高级程序设计》一书中提到:"函数在 ECMAScript 中是对象,不是一种数据类型"。这似乎是译者的添加内容。既然 typeof(Object) 返回 function,为何又说函数不是一种数据类型?Object 和 function 之间究竟存在何种关系?

从我的理解来看,Object 本质上就是一个 function,或者更准确地说,Object 是一个函数的名称,官方术语称为构造函数。


var p = new Object();
p.name = "jesse";
p.age = 18;

function Person(name,age)
{
 this.name = name;
 this.age = age;
}

var p2 = new Person("jesse", 18);

在上面的代码中,如果我们将 Object 视为一个函数名,那么 new Object() 和 new Person() 的性质是相同的。通过 new 操作符获取一个 function 实例,这里的 function 已经具备了类的概念。因此,这里的 Object 实际上是一个 function。这也就解释了为什么 typeof(Object) 的结果是 function。

那么,我们在前文提到的复杂类型 Object,它究竟是什么呢?

引言与系列背景

Object 是 function,但 new Object() 的结果则是 object。至此,首字母大写的 Object 是一个 function,而首字母小写的 object 才是一种数据类型,这一概念已清晰。因此,建议大家在讨论类型时统一使用小写,我们的基本类型是 string、number、boolean。大写的 String、Number、Boolean 仅仅代表函数。而调用这些函数所得到的结果,确实是 object。

引言与系列背景

最后,我们无法找到 Undefined 和 Null 这两个函数,因此这两种数据类型的值就是 undefined 和 null(至于为什么 typeof(null) 会得到 object,前文已作解释)。

  引言与系列背景

有趣点六:Object 不是 object 类型。

基本包装类型机制

基本包装类型 上文已讲过 string、number、boolean 是基本类型。基本类型与复杂类型最大的区别在于,基本类型没有 prototype 属性。这意味着你不能随意给基本类型添加方法或属性。


var str = "str"; // typeof(str): string
var strObj = new String("str"); // typeof(strObj):object

strObj.name = "strObj";
strObj.alert = function () {
 alert(this.name);
};
strObj.alert(); // strObj

str.name = "str"; //wrong...
str.alert = function () {
 alert(this);
}
str.alert(); // this is wrong.... nothing is gonna happen.

同时,我们提到首字母大写的 String 是一个 function,因此 new String("str") 得到的是一个 object 而非一个 string,这一点需要明确。由此引发的问题是:为什么基本类型 string 会拥有一些初始方法?它既然是基本类型,这些方法是如何添加上去的?


str = str.concat("str2");
strObj = strObj.concat("str2"); 
strObj.alert(); //之后返回 string 不再是一个对象了, 所以这里也不再有alert方法了。

str 是 string 类型的变量,请记住它不是一个对象。它本不应拥有方法,那么它的 concat 方法从何而来?在后台调用 str.concat 时,实际上秘密完成了以下步骤:

  • 基于 str 创建一个 String 类型的实例。
  • 在该实例上调用指定的方法。
  • 销毁这个实例。

可以将这三个步骤想象为以下过程:


var str2 = new String(str);
str = str2.concat("str2");
str2= null;

我们可以将 String、Number、Boolean 称为封装类型,它们类似于 C# 中的自定义类型。但不要忘记,真正的基本类型是 string、number、boolean。由 String 构造出的对象属于 object 类型。

值类型、引用类型与类定义

有趣点七:String 不是 string。 值类型和引用类型 上文讲述了 5 种基本类型:string、number、boolean、null、undefined,它们全部属于值类型。JavaScript 中只有一种引用类型,即复杂类型 object。有人可能会好奇,像 Date、RegExp、Array 等是什么类型?这里存在概念上的混淆。如果你很好地理解了上文关于 Object 与 object 的区别,可能会更容易理解。我们可以将 function 视为 C# 中的 class 关键字,可以用 class 定义类,同样可以在 JavaScript 中使用 function 来定义类。

在 C# 中定义类:


namespace ConsoleApplication1
{
class Person
{
 public string Name { get; set; }
 
 public int Age { get; set; }
}
 
class Program
{
 static void Main(string[] args)
 {
 var p =new Person();
 Console.WriteLine(p.GetType()); // ConsoleApplication1.Person
 
 Console.ReadLine();
 }
}
}

在 JavaScript 中定义类:


function Person(name,age)
{
 this.name = name;
 this.age = age;
}

var p = new Person();
typeof(p); //object

你发现区别了吗?如果在 JavaScript 中使用 function 定义类,其实例将永远是 object,包括原生的 Date、Array、RegExp。


typeof (new Date()); // object
typeof (new Array()); // object
typeof (new RegExp()); // object

引言与系列背景

有趣点八:全部都是 object。 如果全部都是 object,那我如何知道这个对象到底是不是 Date 或者 Person 的实例呢?借助于 instanceof 运算符即可判断。

引言与系列背景

函数类型与 Function 构造器

终极有趣点:我用 function 创建了一个 Person 类,然后用 new 得到一个 Person 的实例,结果它却不是 Person 类型的。这就像生了个孩子,供他吃穿,但他却不跟你姓,这需要多么无私伟大才能做到! function类型 function 类型有两种:函数声明和函数表达式。函数声明具有优先级,可以在声明之前被使用,而表达式则不能。


sayGoodNight(); // right
sayHello(); // wrong

var sayHello = function (name) {
 alert("Hello, " + name);
};

function sayGoodNight(Name) {
 alert("Good Night, "+ name);
}

此外,函数表达式还可以像 object 一样,随意添加属性。


var sayHello = function (name) {
 alert("Hello, " + name);
};

sayHello.age = 18;
sayHello.sayAge = function () {
 alert("I am" + 18) ;
}
sayHello.sayAge(); // I am 18

但是,函数表达式到底是什么?它不能实例化,但可以随意添加属性,它与 object 有什么区别?我们在上文说过,object 其实就是一个对象实例。

我们还有大写的 Function,它和 function 之间的关系会不会和 String 与 string 一样?(以下内容比较费脑力,请谨慎阅读!)


var sayHello = new Function('name','alert("My name is " + name );');
sayHello('Jesse');
sayHello instanceof Function; // true

var sayHello2 = function (name) {
 alert('My name is' + name);
};
sayHello2 instanceof Function; // true

上文我们调用 Function 去构造了一个函数。既没有使用函数声明,也没有使用函数表达式,这无论如何算是第三种创建函数的方法。虽然肯定很少有人使用它,因为无论是参数还是函数体全部都是字符串,写起来确实令人崩溃。

引言与系列背景

  引言与系列背景

看出其中的规律了吗?所谓的函数表达式,其实是用一个变量接收了一个 function 的对象而已。而这个 function 的对象则是 Function 的实例。包括用函数声明写出来的函数也是 Function 的实例。


function sayHello3(name)
{ 
 alert('My name is' + name);
}
sayHello3 instanceof Function; // true

但是,等等,我们前面提到的 String、Date、Array 都是 function 类型的,那 Function 也是吗?

引言与系列背景

我们前面提到所有 function 的实例都是 object 类型的,但对于 Function 这个特殊的 function 来说,它的实例仍然是 function 类型的,并且我们可以用 Function 的实例再创造实例。原来我们所说的用 function 创造出来的类,它不是类,而是 Function 的实例。


function Person(name) {
 this.name = name;
}
Person instanceof Function; // true

我们再结合自执行函数来理解,可能会更清晰:


(function () {
 alert("something...");
}());

实际上,我们上面的 function(){} 会返回给我们一个 function 的实例,那么我们当然可以直接执行它了。这么看来,function 应该是 JavaScript 里面最特别的类型了。

有趣点十:所有的 function 都是 Function 的实例。 有趣点十一:Function 本身也是一个 function。

引言与系列背景

类型系统总结

最后我们来总结一下:

  • JavaScript 中有 5 种基本类型:string、number、boolean、null、undefined。
  • 另外一种复杂类型 object 其实是 function 的实例。
  • 除了 Function 这个系统里的 function 构造器以外,其他所有 function 的实例都是 object 类型的。
  • Date、Array、RegExp 这些都是 function 类型,同时也是 Function 的实例。同理,它们的实例也是 object 类型的。

总结完毕,内容似乎并不多,不是吗?关于 function,JavaScript 是一个非常强大的功能,作用域以及面向对象的一些知识也与它息息相关。我们下一篇将探讨作用域的问题。感谢大家的关注!

作者最新文章