JavaScript是一种脚本语言
JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。
JavaScript 是 web 开发人员必须学习的 3 门语言中的一门:
- HTML 定义了网页的内容
- CSS 描述了网页的布局
- JavaScript 控制了网页的行为
- 因为你没有选择。在Web世界里,只有JavaScript(TypeScript是JS的超集)能跨平台、跨浏览器驱动网页,与用户交互。
核心特性:
- JavaScript 是一种轻量级的编程语言。
- JavaScript 是可插入 HTML 页面的编程代码。
- JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行
JavaScript 让静态的网页拥有了 “生命力”,实现了用户与页面的实时交互。它的跨平台特性与丰富的生态系统,使其从最初的网页脚本语言,成长为贯穿全栈开发的通用语言,是 Web 开发人员不可或缺的核心技能,也是进入前端、全栈开发领域的必备基础。
Flash背后的ActionScript曾经流行过一阵子,不过随着移动应用的兴起,没有人用Flash开发手机App,所以它目前已经边缘化了。相反,随着HTML5在PC和移动端越来越流行,JavaScript变得更加重要了。并且,新兴的Node.js把JavaScript引入到了服务器端,JavaScript已经变成了全能型选手。
JavaScript一度被认为是一种玩具编程语言,它有很多缺陷,所以不被大多数后端开发人员所重视。很多人认为,写JavaScript代码很简单,但这是完全错误的理解。JavaScript确实很容易上手,但其精髓却不为大多数开发人员所熟知。编写高质量的JavaScript代码更是难上加难。
JavaScript历史
https://liaoxuefeng.com/books/javascript/history/index.html
要了解JavaScript,我们首先要回顾一下JavaScript的诞生。
在上个世纪的1995年,当时的网景公司正凭借其Navigator浏览器成为Web时代开启时最著名的第一代互联网公司。
由于网景公司希望能在静态HTML页面上添加一些动态效果,于是叫Brendan Eich这哥们在两周之内设计出了JavaScript语言。你没看错,这哥们只用了10天时间。
为什么起名叫JavaScript?原因是当时Java语言非常红火,所以网景公司希望借Java的名气来推广,但事实上JavaScript除了语法上有点像Java,其他部分基本上没啥关系。
ECMAScript
因为网景开发了JavaScript,一年后微软又模仿JavaScript开发了JScript,为了让JavaScript成为全球标准,几个公司联合ECMA(European Computer Manufacturers Association)组织定制了JavaScript语言的标准,被称为ECMAScript标准。
所以简单说来就是,ECMAScript是一种语言标准,而JavaScript是网景公司对ECMAScript标准的一种实现。
那为什么不直接把JavaScript定为标准呢?因为JavaScript是网景的注册商标。
不过大多数时候,我们还是用JavaScript这个词。如果你遇到ECMAScript这个词,简单把它替换为JavaScript就行了。
JavaScript版本
JavaScript语言是在10天时间内设计出来的,虽然语言的设计者水平非常NB,但谁也架不住“时间紧,任务重”,所以,JavaScript有很多设计缺陷,我们后面会慢慢讲到。
此外,由于JavaScript的标准——ECMAScript在不断发展,最新版ECMAScript 6标准(简称ES6)已经在2015年6月正式发布了,所以,讲到JavaScript的版本,实际上就是说它实现了ECMAScript标准的哪个版本。
由于浏览器在发布时就确定了JavaScript的版本,加上很多用户还在使用IE6这种古老的浏览器,这就导致你在写JavaScript的时候,要照顾一下老用户,不能一上来就用最新的ES6标准写,否则,老用户的浏览器是无法运行新版本的JavaScript代码的。
不过,JavaScript的核心语法并没有多大变化。
JavaScript数据类型
JavaScript 是一种动态类型语言,这意味着变量的类型可以在运行时改变。
1 | var x; // 现在 x 是 undefined |
JavaScript 中的数据类型主要分为两大类:原始类型 (Primitive Types) 和 引用数据类型 (Object Types)。
原始类型 (Primitive Types)
Number (数值)
JavaScript 不区分整数和浮点数,所有数字都属于 Number 类型。
基本的数值字面量格式是十进制整数,十进制整数可以像下面这样直接在代码中输入:
1 | let intNum = 666; // 整数 |
JS可以直接的在字面表示不同进制的数字,八进制的值第一位必须是零0,后面每一位数的范围在0~7。如果某一位数超出范围,首位的0会被忽略,后面的数值会按照十进制来解析。用前缀0表示的八进制字面量在严格模式下是无效的,会报错。需要用前缀0o表示。
1 | let octalNum1 = 070; // 八进制的 56 |
十六进制的值前两位必须是0x,后面每一位十六进制数字的范围在09及AF,字母A~F可以大写也可以小写。
1 | let hexNum1 = 0xA; // 十六进制的 10 |
进行算术运算时,所有八进制和十六进制的数值都会被视为十进制。
特殊数值:
NaN (Not a Number):表示非数字值。
任何涉及 NaN 的操作(例如 NaN/10)都会返回 NaN。
重要特性:NaN == NaN 为 false。NaN 与任何值(包括它自身)都不相等。
判断方法:使用全局函数 isNaN() 来判断一个值是否为 NaN。
1
2
3
4
5console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false(10是一个数值)
console.log(isNaN("10")); // false(可以被转换成数值 10)
console.log(isNaN("blue")); // true(不能转换成数值)
console.log(isNaN(true)); // false(可以被转换成数值 1)
Infinity (无穷大):表示超过 JavaScript 可表示的最大数值。
-Infinity (负无穷大):表示超过 JavaScript 可表示的最小数值。
浮点数的数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。如果小数点后面没有数字,或者本身表示的就是有一个整数(例如1.0),那么该值会被转换为整数保存。
1 | let floatNum1 = 1.1; |
同样,js支持e,对于那些极大或极小的数值,可以用e表示法(即科学计数法)来表示。e后面的数字代表10的幂中的指数,该幂值用来与前面的数相乘。
1 | let floatNum1 = 6.123e7; // 等于61230000 |
浮点数值的最高精度是17位小数,但是进行算数运算时是有误差的。
浮点数问题:JavaScript 遵循 IEEE 754 标准,浮点数运算可能存在精度损失。
示例:0.1 + 0.2 !== 0.3 (结果是 0.30000000000000004)。
避免方法:尽量避免直接比较浮点数,可以使用一个极小的误差范围进行比较:Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON (ES6 引入 Number.EPSILON 表示机器精度)。
最可靠的是使用 toFixed() 方法对计算结果进行四舍五入:
1
2
3
4
5
6console.log((0.1 + 0.2).toFixed(1)); // 0.3
console.log(((0.28 * 100 + 0.14 * 100) / 100).toFixed(2)); // 0.42
// 要注意的是 toFixed() 方法返回的是字符串,如果是将计算结果展示到页面上是很方便的。
// 如果是需要数值的情况,还要进行类型转换操作,例如使用"+"将其转换为数值:
console.log(+(0.1 + 0.2).toFixed(1)); // 0.3
console.log(typeof +(0.1 + 0.2).toFixed(1)) // number
大整数 (BigInt):
- JavaScript 整数的精确范围是 -(2^53 - 1) 到 2^53 - 1 (即 15 位十进制数)。超过这个范围的整数运算可能不准确。
- ES2020 引入 BigInt 类型,用于表示任意精度的整数。
- 创建方法:在整数末尾添加 n (如 123n) 或调用 BigInt() 函数 (如 BigInt(“12345678901234567890n”))。
- 限制:BigInt 不能有小数。不允许在 BigInt 和 Number 之间直接进行算术运算(除非显式类型转换)。
常见数值方法:
- toString():将数字转换为字符串。
- toExponential(digits):将数字转换为指数表示的字符串,并四舍五入到指定位数。
- toFixed(digits):将数字格式化为指定小数位数的字符串(四舍五入)。
- toPrecision(precision):将数字格式化为指定总位数的字符串(四舍五入)。
- valueOf():返回数字的原始值。
- Number():可以将字符串、布尔值甚至日期转换为数字。Number(new Date()) 返回自 1970-01-01 00:00:00 UTC 以来的毫秒数。
由于内存限制,JavaScript不能保存所有的数值。如果某次计算的结果得到了一个超出 JavaScript数值范围的值,那么这个数值将被自动转换成特殊的 Infinity 值。具体来说,如果这个数值是负数,则会被转换成-Infinity(负无穷),如果这个数值是正数,则会被转 换成 Infinity(正无穷)。
String (字符串)
和其他语言一样,js 的字符串用于表示文本数据。
创建方式:使用单引号 (’)、双引号 (“) 或反引号 (`) 包裹。
转义字符:使用 进行转义,如 (换行)、制表符)、'、"、\。
模板字符串 (Template Literals) (ES6):使用反引号 (`) 包裹,支持:
多行字符串:可以直接在字符串中换行,无需 。
字符串插值 (String Interpolation):使用 ${expression} 将变量或表达式嵌入字符串中。
1
2
3
4var name = "Alice";
let age = 30;
let greeting = `Hello, ${name}! You are ${age} years old.`;
console.log(greeting); // "Hello, Alice! You are 30 years old."
字符串是不可变的 (Immutable):一旦创建,字符串的值就不能被改变。所有看似修改字符串的方法,实际上都返回一个新的字符串。
字符字面量:String 类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者具有其 他用途的字符。这些字符字面量可以出现在字符串中的任意位置,而且也将被作为一个字符来解析。
常用属性和方法:
length:获取字符串的长度。[index]:通过下标访问字符(只读)。toUpperCase():转换为大写。toLowerCase():转换为小写。charAt(index):返回指定索引位置的字符。indexOf(searchValue, fromIndex):返回指定值第一次出现的索引,未找到返回 -1。lastIndexOf(searchValue, fromIndex):返回指定值最后一次出现的索引,未找到返回 -1。startsWith(searchString, position):检查字符串是否以指定值开头。endsWith(searchString, position):检查字符串是否以指定值结尾。includes(searchString, position):检查字符串是否包含指定值。substring(startIndex, endIndex):提取字符串的一部分,不包含 endIndex 处的字符。slice(startIndex, endIndex):与 substring 类似,但可以接受负数索引(从字符串末尾开始计数)。replace(searchValue, replaceValue):替换字符串中的一部分。split(separator):将字符串分割成数组。
模板字符串相当于加强版的字符串,可以定义多行字符串。还可以利用${}在字符串中插入变量和表达式。这个就很像 spring 中引用配置了
1 | let s1 = `hello |
Boolean (布尔值)
JavaScript中也是一样,只有两个值:true (真) 和 false (假),也是使用逻辑运算符&& (与), || (或), ! (非)。
这两个值与数字值不是一回事,因此 true 不一定等于 1,而 false 也不一定等于 0。
布尔值转换:其他 ECMAScript 类型的值都有与布尔值等价的形式。可以调用 Boolean() 函数来将其他类型转换为布尔值。不同类型转换为布尔值的规则如下表:
Undefined
Undefined 类型只有一个值,即undefined。在声明变量但是没有初始化时,这个变量的值就是undefined。
表示变量已声明但未赋值,或者访问对象上不存在的属性。
typeof undefined 返回
undefined,所以正常情况下,调用未声明的变量会报错,但是对未声明的变量使用typeof操作符,同样会返回”undefined”
Null
表示一个空值,通常由开发者明确赋值,表示“无对象”或“没有值”。Null 类型也是只有一个值,即null。
null表示一个空对象指针,typeof null返回
object(有人说这是一个历史遗留的
bug,但它仍然是原始类型)。
核心特点:
- 与
undefined的区别:null是 “主动清空”(如let obj = null;表示释放对象引用),而undefined是 “被动未定义”。 - 历史遗留问题:
typeof null返回object(JavaScript 早期实现的 bug,因null被误判为对象的空指针),但null本质是原始类型,可通过Object.prototype.toString.call(null)准确判断为[object Null]。 - 常见用途:初始化变量(预留对象位置)、释放对象引用(帮助垃圾回收)、作为函数返回值表示 “无结果”。
注意:
null == undefined// true (值相等,不考虑类型)null === undefined// false (类型不同,严格相等为假)
Symbol (ES6)
表示一个独一无二的值。主要用于定义对象的唯一属性键,避免属性名冲突。
Symbol() 函数返回一个 Symbol 值,该值是唯一的。
typeof Symbol() 返回 symbol
1 | let s = Symbol(); |
该函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,使得在控制台显示,或者转换为字符串进行判断时容易区分
1 | let s1 = Symbol('foo'); |
Symbol() 函数的参数只有描述作用,即使两个 Symbol() 函数的参数相同 ,它们的返回值也是不相等的。
一般用 Symbol 值作为对象的属性名
因为每一个 Symbol 值都是不相等的,所以 Symbol 值可以用作唯一标识,用于对象的属性名,就能防止属性名的同名冲突。对于一个对象是由多个模块构成的情况下非常有用,避免某个 key 被改写或者覆盖。
1 | let s = Symbol(); |
而 Symbol.for(key)会将这个Symbol 注册到全局 symbol
注册表,而普通的Symbol()不会。
Symbol.for() 调用不会直接生成新的 Symbol ,它会先从注册表中搜索给定的key是否存在。如果存在,那么返回的是同一个 Symbol,否则生成新的。
内置 Symbol:JavaScript 提供了一系列内置
Symbol(如 Symbol.iterator
用于定义对象的迭代器),用于扩展语言原生行为。
注意:Symbol
不能与其他类型直接拼接字符串(需显式转换:String(s1) 或
s1.description)。
对象类型 (Object Types)
在 JavaScript 中,除了原始类型(Number、String、Boolean
等),其余所有值都是对象。对象是一组键值对的无序集合,可包含数据(属性)和函数(方法),是实现复杂逻辑和数据结构的核心载体。所有对象的基础是
Object 类型,其他对象(如数组、日期)均继承自它。
Object (对象)
Object 是 JavaScript
中最原生的对象类型,用于存储无序的键值对。
创建方式:
通过对象字面量:使用花括号 {} 定义。
1
2
3
4
5
6var person = {
name: "zjm",
age: 3,
tags: ['js', "web", 'java'],
'full-name': 'Zhu Jinming' // 属性名包含特殊字符时需用引号
};- 特殊属性名处理:
- 包含空格、特殊字符(如
-)或关键字时,需用引号包裹:'full-name': 'Zhang San'。 - 数值属性会自动转为字符串:
5: true等价于'5': true。
- 包含空格、特殊字符(如
- 特殊属性名处理:
通过
new Object()构造函数1
2
3
4
5// 创建空对象
let person = new Object();
// 动态添加属性
person.name = "小明";
person.age = 20;这种方式通过构造函数实例化对象,适合逐步添加属性的场景。
对象属性的存取
- 点表示法:
person.name(常用)。 - 方括号表示法:
person['age'](当属性名包含特殊字符、是变量或数字时必须使用)。
对象属性的操作
添加属性
直接赋值即可(无论对象是否为空):
1 | person.gender = "male"; // 新增 gender 属性 |
删除属性
用
delete运算符彻底删除属性(仅删除自身属性,不影响原型链):
1 | delete person.age; // 删除 age 属性 |
判断属性是否存在
‘属性名’ in 对象:检查属性是否存在于对象自身或原型链上:
1
2console.log('name' in person); // true(自身属性)
console.log('toString' in person); // true(继承自 Object 原型)对象.hasOwnProperty(‘属性名’):仅检查属性是否为对象自身的属性(排除原型链):
1
2console.log(person.hasOwnProperty('name')); // true(自身属性)
console.log(person.hasOwnProperty('toString')); // false(原型链属性)
JavaScript 中的对象可分为三类:
- 内置对象:由 ECMAScript
标准定义的对象,无需手动创建即可使用,例如:
Array(数组)、Math(数学工具)、Date(日期)、RegExp(正则)等。
- 自定义对象:开发者通过字面量或构造函数创建的对象,例如前面的
person对象。 - 宿主对象:由执行环境(如浏览器、Node.js)提供的对象,不属于
ECMAScript 标准:
- 浏览器环境:
window(窗口对象)、document(文档对象,DOM)等。 - Node.js
环境:
global(全局对象)、fs(文件系统模块)等。
- 浏览器环境:
所有 Object
的实例(包括自定义对象、内置对象)都继承以下属性和方法:
| 方法 / 属性 | 作用 |
|---|---|
constructor |
指向创建该对象的构造函数(例如 person.constructor 指向
Object)。 |
hasOwnProperty(prop) |
检查 prop 是否是对象自身的属性(非原型链)。 |
isPrototypeOf(obj) |
检查当前对象是否是 obj 的原型(用于原型链判断)。 |
propertyIsEnumerable(prop) |
检查 prop 是否可通过 for...in
循环枚举(自身属性且可枚举)。 |
toLocaleString() |
返回与地区相关的对象字符串表示(如 Date
会按本地时间格式返回)。 |
toString() |
返回对象的字符串表示(默认是
[object Object],可被重写)。 |
valueOf() |
返回对象的原始值表示(通常与 toString()
结果相同,可被重写)。 |
Array (数组)
数组是 JavaScript 中用于存储有序数据集合的引用类型,可包含任意类型元素(数值、字符串、对象等),是处理批量数据的核心工具。
创建方式:使用方括号 [] 或 new Array()。
1 | var arr = [1, 2, "3", null, NaN, true]; // JavaScript 数组可以包含不同类型的数据 |
特性:
- 动态长度:数组的长度可以随时改变。
- 给
arr.length赋值可以改变数组大小。如果赋值过小,会截断数组;如果赋值过大,会创建 undefined 元素。
- 给
- 越界访问:访问越界的索引不会报错,而是返回 undefined。
稀疏数组
数组中存在未赋值的索引时,会形成稀疏数组,未赋值位置为
empty(与 undefined 不同,empty
不会被 forEach、map 等方法遍历)。
1 | const arr = [1, , 3]; // 索引1为empty |
数组与类数组的区别
数组是
Array类型的实例,自带数组方法(如push、map)。类数组(如函数参数
arguments、DOM 集合document.querySelectorAll())是具有length属性的对象,但无数组方法,需通过Array.from()或[...类数组]转为真数组:1
2const likeArray = { 0: 'a', 1: 'b', length: 2 };
const realArray = Array.from(likeArray); // 转为真数组 ['a', 'b']
数组的本质
数组本质是特殊的对象,索引本质是字符串类型的属性名(如
arr[0] 等价于 arr['0']),但数组会自动维护
length 属性,且索引按数值排序。
数组的静态方法
Array 构造函数本身提供了静态方法,无需实例即可调用:
Array.isArray(value):判断一个值是否为数组(比typeof更可靠,typeof []返回object)1
2Array.isArray([1, 2]); // true
Array.isArray({}); // falseArray.of(...values):创建一个包含指定值的数组(解决new Array(n)当n为数字时创建长度为n的空数组的问题)。1
2Array.of(3); // [3](与 new Array(3) 不同,后者是 [empty × 3])
Array.of(1, 2, 3); // [1, 2, 3]
常用方法:
length:获取数组长度。[index]:通过索引访问/修改元素。indexOf(element):查找元素第一次出现的索引,未找到返回 -1。push(element1, ...):向数组末尾添加一个或多个元素,并返回新长度。pop():移除并返回数组的最后一个元素。unshift(element1, ...):向数组开头添加一个或多个元素,并返回新长度。shift():移除并返回数组的第一个元素。slice(startIndex, endIndex):截取数组的一部分,返回一个新数组(不修改原数组)。splice(startIndex, deleteCount, item1, ...):添加/删除数组元素(修改原数组)。concat(array1, ...):拼接数组,返回一个新数组(不修改原数组)。join(separator):将数组的所有元素连接成一个字符串。sort(compareFunction):对数组进行排序。- 默认按字符串顺序排序,因此数值排序时需要提供比较函数:arr.sort((a, b) => a - b) (升序)。
reverse():反转数组元素。fill(value, start?, end?):用value填充数组从start到end(不包含end)的元素,修改原数组。copyWithin(target, start?, end?):将数组中start到end的元素复制到target位置,修改原数组。- 迭代方法 (ES5+):
- forEach(callback):对数组的每个元素执行一次回调函数。
- map(callback):创建一个新数组,其结果是调用提供的回调函数在每个元素上。
- filter(callback):创建一个新数组,其中包含通过测试的所有元素。
- reduce(callback, initialValue):对数组中的每个元素执行一个 reducer 函数(从左到右),将其结果汇总为单个返回值。
- every(callback):测试数组的所有元素是否都通过了指定函数的测试。
- some(callback):测试数组中是不是至少有一个元素通过了指定函数的测试。
- 查找类方法(ES6+)
find(callback):返回数组中第一个满足回调条件的元素(未找到返回undefined)findIndex(callback):返回数组中第一个满足回调条件的元素的索引(未找到返回-1),类似indexOf但支持复杂条件。includes(value):判断数组是否包含指定值(返回true/false),与indexOf相比能正确识别NaN
- 数组转置与扁平化(ES6+)
flat(depth):将嵌套数组 “扁平化”,depth为扁平化深度(默认 1,Infinity表示无限深度)flatMap(callback):先对数组元素执行map,再对结果执行flat(1)(常用于处理嵌套数组)
注意,sort 方法默认按 Unicode
编码排序,对特殊场景需自定义比较函数:
1 | // 按对象属性排序 |
Function (函数)
- JavaScript 中的函数也是对象。typeof functionName 返回 “function”。
- 将在“函数类型”部分详细介绍。
Date (日期)
- 用于处理日期和时间。
- 将在“日期类型”部分详细介绍。
typeof 运算符
- typeof 运算符用于确定变量或表达式的类型。
typeof ""// “string”typeof 666// “number”typeof true// “boolean”typeof undefined// “undefined”typeof null// “object” (历史遗留问题)typeof []// “object”typeof {}// “object”typeof function(){}// “function”typeof Symbol()// “symbol”typeof 1n// “bigint”







