深入理解JavaScript-JavaScript 由什么组成

简介: 深入理解JavaScript-JavaScript 由什么组成

这是一个基础结构问题,一个完整的 JavaScript 包括:ECMAScript 、文档对象模型(DOM)、浏览器对象模型(BOM),本章讲解 ECMAScript

ECMAScript[1] 规定这门语言由以下组成:


  1. 语法
  2. 变量和数据类型
  3. 关键字和保留字
  4. 操作符
  5. 语句
  6. 对象


也就是说,这六大模块组成了这门语言

这六大支柱中,像「语法」、「变量」和「数据类型」、「关键字」和「保留字」、「操作符」、「语句」都是极好理解的,只「对象」有点复杂,下节会对其进行说明,这里我们来看看「数据类型」


数据类型


JavaScript 的数据类型分为「基本类型」和 「引用类型」


基本类型在不同的书(教程中)叫法不同,它同样被叫做基本类型/值类型/原始值/原始类型。当然引用类型也有不一样的叫法,如对象类型/复杂类型


基本类型包含 undefined 、null、string、number、boolean、symbol(ES6 新增)、bigint(ES10新增)


引用类型则是object(一组属性的集合)


特此说明:

在《JavaScript 高级程序设计第四版》中曾把数据类型归纳为驼峰形式的Undefined、Null、Boolean、Number、String 、 Symbol、BigInt、Object,而在网站现代 JavaScript 教程[2]MDN[3]中则以小写的形式展示展示数据类型,笔者这里以网站为更大依据


基本类型和引用类型的区别


基本类型储存在栈内存中

引用类型储存在堆内存中

基本类型花销的内存小,所以拷贝值

引用类型花销的内存大,所以拷贝引用地址

image.png


// 案例1
var string1 = 'foo';
var string2 = string1;
string1 = 'bar';
console.log(string1, string2); // 输出 bar, foo
// 案例2
var object1 = {
    name: 'johan',
    age: 23,
};
var object2 = object1;
object1.name = 'elaine';
object2.age = 22;
console.log(object1, object2);
// 输出 { name: 'elaine', age: 23 } { name: 'elaine', age: 23 }


因为基本类型是存放在栈内存中,string1 赋值给 string2 后,相当于开了一个独立空间给 string2,string1 和 string2 是完全独立的两个个体,再给 string1 赋值什么,都与 string2 无关


而引用类型之所以叫引用类型(或叫复杂类型/复杂值),因为对象的地址是引用的,Object 可以一直嵌套,Function 也可以嵌套很多很多层(回调函数),Array 可以变花样的做出三维数组之类,总之,对象值可以无限大。值越大,就越占内存,如果一个很大的值随意复制,那对使用者来说就是灾难(不知不觉中就占了很大的内存)。所以它不可能随意复制,而是用指针的方式来将对象指向同一个地址(这里可以衍生到深拷贝)


当你创建一个 object1,就在堆内存中开辟一个内存。如果你赋值给 object2,其实就是把 object1 指向堆内存的地址复制给 object2,它们指向的是同一个地址。再在 object1 或者 object2 做修改的话,相当于在同一内存上做修改,无论 object1 修改还是 object2 修改都会改变


var obj1 = {};
var obj2 = obj1;
obj1.name = 'elaine';
obj2.age = 23;
console.log(obj1); // { name: 'elaine', age: 22};
console.log(obj2); // { name: 'elaine', age: 22};


衍生思考:虽然说 JavaScript 的动态性很是方便,但是如果一些新手修改了对象类型的值而不告知,那么就成了灾难。因为动态性+全局作用域,所有变量命名就成了问题,所以一般的库都是用IIFE、闭包等方法破局,再后面就有了模块化(ES module)的概念,其本质是为了解决变量命名、语言动态性的问题


光知道 JavaScript 的数据类型不够,如何更准确的知道每个数据类型

判断数据类型的方式有四种

  • 使用 typeof
  • 使用 instanceof
  • 使用 constructor
  • 使用 Object.prototype.toString.call()


类型判断


typeof 操作符


作用:返回正在使用的值的基本类型


// 基本类型/值类型/原始值/原始类型 
var null1 = null;
var undefined1 = undefined;
var string1 = 'foo';
var number1 = Number('10');
var boolean1 = Boolean('true');
var symbol1 = Symbol('foo');
console.log(typeof null1); // object, 需要注意
console.log(typeof undefined1); // undefined
console.log(typeof string1); // string
console.log(typeof number1); // number
console.log(typeof boolean1); // boolean
console.log(typeof symbol1); // symbol
// 引用类型/复杂值
var myString = new String('male');
var myNumber = new Number(23);
var myBoolean = new Boolean(false);
var myObject = new Object();
var myArray = new Array('foo', 'bar');
var myFunction = new Function('x', 'y', 'return x * y');
var myDate = new Date();
var myRegExp = new RegExp('\\bt[a-z]+\\b');
var myError = new Error('error');
console.log(typeof myString); // 输出 object
console.log(typeof myNumber); // 输出 object
console.log(typeof myBoolean); // 输出 object
console.log(typeof myObject); // 输出 object
console.log(typeof myArray); // 输出 object
console.log(typeof myFunction); // 输出 function 需要注意
console.log(typeof myDate); // 输出 object
console.log(typeof myRegExp); // 输出 object
console.log(typeof myError); // 输出 object


提示:typeof xxx 和 typeof(xxx) 一个意思


通过 typeof 操作符能判断出使用的值的类型。需要注意判断引用类型时的问题

  1. null 类型会返回 object
  2. new Function,返回的是 function


随便一说,这也是我们看很多源码或者自己写代码时常用 typeof 来判断 function 类型

if (typeof XXX === 'function') {
    // 如果XXX是function的话做什么操作
}


PS:《JavaScript 启示录》里第一章第八节里说 RegExp() 的类型返回的是 function,但是笔者检验后发现并不是,笔者猜测因为老版本的浏览器对 RegExp 的判断为 funtion,而笔者用 Chrome 浏览器表示 RegExp 的类型为 object


instanceof 运算符


instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上


function People(name, age) {
    this.name = name;
    this.age = age;
}
const elaine = new People('elaine', 23);
console.log(elaine instanceof People);
console.log(elaine instanceof Object);


instanceof 运算符能让我们找到它的爸爸是谁(谁制造了它,从生理上讲应该是妈妈是谁),以及它的祖宗十八代,这也是面试中常遇到的——instanceof 的原理是什么


constructor 构造方法


constructor 是一种用于创建和初始化 class 创建的对象的特殊方法。请注意,它是函数(方法)


就好比:

function sayHello() {
    console.log('hello');
}

不要因为单词陌生误以为它是属性


语法:

constructor([arguments]) { ... }


这里多说一句,React 类组件是这样写的:

class HelloWorld extends React.Component {
    constructor(props) {
        super(props);
    }
}


这里的意思很明白,HelloWorld 组件继承了 React.Component 组件,constructor(props) 意味着调用了父类的构造函数,并将 props 传递给 HelloWorld,使用 super 是因为在派生类中,必须调用 super 才能使用 this,当然,这部分就关系到 class 的知识,在此不表


关于构造函数(constructor)需要知道的是它是 Object 原型上的方法,即 Object.prototype.constructor,所有对象都会有 constructor 属性,它指向该对象的构造函数,关于继承后续文章会讲解


说完题外话,我们继续来看 constructor 能否检验数据类型吗


// 借 typeof 中的例子,直接打印看效果
// 基本类型
console.log(null1.constructor); // Cannot read properties of null (reading 'constructor')
console.log(undefined1.constructor); // Cannot read properties of null (reading 'constructor')
console.log(string1.constructor); // String() { [native code] }
console.log(number1.constructor); // Number() { [native code] }
console.log(boolean1.constructor); // Boolean() { [native code] }
console.log(symbol1.constructor); // Symbol() { [native code] }
// 引用类型
console.log(myString.constructor); // String() { [native code] }
console.log(myNumber.constructor); // Number() { [native code] }
console.log(myBoolean.constructor); // Boolean() { [native code] }
console.log(myObject.constructor); // Object() { [native code] }
console.log(myArray.constructor); // Array() { [native code] }
console.log(myFunction.constructor); // Function() { [native code] }
console.log(myDate.constructor); // Date() { [native code] }
console.log(myRegExp.constructor); // RegExp() { [native code] }
console.log(myError.constructor); // Error() { [native code] }


综上所述,constructor 对 underfined 和 null 无效(因为它们不是对象,不能从 Object.prototype 上继承 constructor 属性)


此外, constructor 的指针是可以改变的(因为它就是个属性,以下例子属于属性赋值)

function Person() {}
function Student() {}
Student.prototype = new Person();
var student = new Student();
console.log(student.constructor); // Person() {}


具体我们在原型一文中做详细介绍


Object.prototype.toString.call(source)


toString() 方法返回一个表示该对象的字符串

每个对象都有一个toString() 方法(继承自 Object.prototype 老祖),用它能真正做到对类型的检测


// 继续引用上述例子
// 基本类型
console.log(Object.prototype.toString.call(null1)); //[object Null]
console.log(Object.prototype.toString.call(undefined1)); //[object Undefined]
console.log(Object.prototype.toString.call(string1)); //[object String]
console.log(Object.prototype.toString.call(number1)); //[object Number]
console.log(Object.prototype.toString.call(boolean1)); //[object Boolean]
console.log(Object.prototype.toString.call(symbol1)); //[object Symbol]
// 引用类型
console.log(Object.prototype.toString.call(myString)); //[object String]
console.log(Object.prototype.toString.call(myNumber)); //[object Number]
console.log(Object.prototype.toString.call(myBoolean)); //[object Boolean]
console.log(Object.prototype.toString.call(myObject)); //[object Object]
console.log(Object.prototype.toString.call(myArray)); //[object Array]
console.log(Object.prototype.toString.call(myFunction)); //[object Function]
console.log(Object.prototype.toString.call(myDate)); //[object Date]
console.log(Object.prototype.toString.call(myRegExp)); //[object RegExp]
console.log(Object.prototype.toString.call(myError)); //[object Error]


我们可以看到它返回 [object NativeConstructorName] 格式的字符串,它能清晰的判断我们所需要的原生构造函数。同样,它的缺点是不能检测非原生构造函数


在 jquery 中的$.type() 是内部的原理用的就是 Object.prototype.toString.call(),让我们手写一个类型判断的小小工具库吧


function isType(source) {
    const target = Object.prototype.toString.call(source);
    switch(target) {
        case "[object Null]":
            return 'null';
        case "[object Undefined]":
            return 'undefined';
        case "[object String]":
            return 'string';
        case "[object Number]":
            return 'number';
        case "[object Boolean]":
            return 'boolean';
        case "[object Object]":
            return 'object';
        case "[object Array]":
            return 'array';
        case "[object Function]":
            return 'function';
        case "[object Date]":
            return 'date';
        case "[object RegExp]";
            return 'regexp';
        case "[object Error]";
            return 'error'
    }
}
function getType(target) {
    return Object.prototype.toString.call(target);
}


这里你或许会感到疑惑,Object.prototype 的 constructor 不能调用 null、undefined,而Object.prototype.toString 却能调用。这是为什么?


这里牵扯到隐式原型继承,即用对象字面量时,就已经实现了继承,而Object.prototype.toString 用的是原始方法,不信,你看


console.log(null1.toString()); // Cannot read properties of null (reading 'toString')
console.log(undefined1.toString()); // Cannot read properties of undefined (reading 'toString')


总结


这一节我们讲了 JavaSript 由什么组成

从组成上分,它包括语法,变量和数据类型,关键字和保留字,操作符,语句,对象

其中数据类型包括基本类型(简单类型/值类型)和引用类型(复杂类型)。基本类型有 number、string、boolean、null、undefined 、symbol、bigInt,引用类型则是 object

那如何判断数据类型呢?笔者总结四种方法


名称 能检测 不能检测
typeof string、number、boolean、undefined 以及 function null 以及除 function 外的对象,结果都为 object
instanceof 准确地判断复杂引用数据类型 不能正确判断基础数据类型
constructor string、number、boolean、array、object、function 以及 构造函数 undefined、null。不安全,因为指向可以改变
Object.prototype.toString() 内置(原生)构造函数 自定义构造函数


我们了解了基本类型、引用类型,它们的区别以及如何区分它们,但基本类型是简单的,而引用类型虽然只有 object,但它却在 JavaScript 占了大部分知识点,只有掌握 object,才算学会 JavaScript

下一篇,我们讲讲 JavaScript 中的 KING—— 对象


参考资料


[1] ECMAScript: https://zh.wikipedia.org/wiki/ECMAScript

[2] 现代 JavaScript 教程: https://zh.javascript.info/types

[3] MDN: https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive

相关文章
|
5天前
|
人工智能 定位技术 SEO
我学 GEO 第 15 天:终于知道AI GEO该如何做?
我是暴走的莉莉酱,边旅行边研究AI GEO的数字游民。专注普通人如何提升“AI可见度”——让AI在回答用户问题时准确识别、理解并推荐你。不讲玄学,只做可测、可调、可持续的GEO实践。
421 125
|
8天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
712 5
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
5天前
|
缓存 人工智能 运维
阿里云618百炼大模型Qwen3.7-Max功能、免费试用、订阅计费、配置接入详解
Qwen3.7-MAX是阿里云百炼平台推出的通义千问3.7系列旗舰大语言模型,专为智能体时代复杂任务打造,依托阿里云全域算力与自研技术,在逻辑推理、长文本处理、代码工程、长周期自主执行等领域达到行业顶尖水平。2026年618期间,该模型推出多重免费试用权益、按量计费5折、订阅套餐优惠等专属福利,覆盖个人开发者、团队与企业全场景需求,以下从核心功能、免费试用、订阅计费、配置接入四方面展开详细解析。
414 123
|
4天前
|
人工智能 自然语言处理 API
阿里云Token Plan团队版解析:功能、三档套餐与省钱订阅指南
阿里云百炼平台推出的Token Plan团队版,是面向企业与团队的AI大模型订阅服务,以Credits为统一计量单位,整合文本与图像生成模型,提供团队管理、数据安全、多工具兼容等核心能力,解决团队零散订阅AI服务的管理混乱、成本失控、数据安全等痛点。本文将从核心定位、套餐详情、计费规则、团队管理、工具兼容、便宜订阅技巧等方面,全面解析Token Plan团队版,帮助企业与团队高效、低成本地使用AI服务。
308 108
|
5天前
|
存储 人工智能 数据可视化
别再手动复制 Skill 了:多 Agent 时代的 Skill 管理方案
多 Agent 场景下 Skill 的统一管理与同步。
256 123
|
19天前
|
缓存 测试技术 API
Qwen 3.7 Plus 与 Max 实测:性价比与多模态能力差异解析(2026)
2026 年 6 月 1 日,阿里悄无声息地发布了 Qwen 3.7 Plus,距 Qwen 3.7 Max 上线刚好 11 天。同样的 1M 上下文,同样的 35 小时自治上限。但价格才是头条:Plus 是 0.40/M输入,Max是 2.50/M——便宜约 6 倍——并且还能看图、看视频。Vision Arena 上 Plus 已经排到 #16。所以这周真正值得讨论的问题不是”要不要为视觉能力买单”,而是”Max 凭什么用 6 倍价格换来 2 个百分点的 benchmark 领先”。
|
12天前
|
缓存 人工智能 运维
GLM 5.2自托管全流程实战:硬件选型、vLLM/SGLang部署与成本盈亏测算
2026年智谱发布GLM 5.2超大混合专家模型,区别于以往仅开放API的闭源大模型,该模型权重以MIT开源协议对外发布,企业与开发者可完整下载、本地审计、私有化部署,实现数据不出环境、自定义微调、自主调度推理资源。GLM 5.2拥有753B总参数,原生支持百万级上下文窗口,在代码生成、长文档推理、数学逻辑等多项基准测试中对标国际顶尖商用模型,是首款可完整自托管的前沿代码向大模型。
937 0
|
13天前
|
Linux 程序员 数据格式
【2026最新】Notepad++下载、安装和使用一篇搞定(附中文版安装包)
Notepad++ 是一款免费开源、轻量高效的 Windows 文本编辑器,支持 C/Python/HTML 等 80+ 语言语法高亮、代码折叠、正则替换、编码转换及插件扩展,专为程序员与文本处理用户打造,完美替代系统记事本。(239字)