欢迎光临
我们一直在努力

三.前端web开发:100%零基础吃透 JavaScript 全部核心知识(全文共计38000字左右)

系列文章目录

提示:这里是系列文章的专栏 并不喜欢吃鱼的C++专栏


提示:以下是文章目录哦!

文章目录

目录

系列文章目录

文章目录

前言

一、JavaScript 基础入门

1.1 JavaScript 是什么

1.2 ECMAScript 与 JavaScript 的关系

1.3 JavaScript 版本发展史

语法提案的批准流程

ES6 带来的主要新特性

1.4 为什么学 JavaScript

1.5 开发环境准备:命令行工具

1.5.1 打开命令行

1.5.2 常用命令速查

1.5.3 PowerShell

1.6 JavaScript 的引入方式

方式一:直接嵌入 HTML 中

方式二:引入本地独立的 JS 文件(推荐)

方式三:引入网络上的 JS 文件(CDN)

1.7 基础语法:语句与标识符

1.7.1 语句

1.7.2 标识符(命名规则)

1.8 注释与输出方式

1.8.1 注释

1.8.2 三种输出方式

二、变量与数据类型

2.1 变量的声明与赋值

2.2 变量提升(Hoisting)

2.3 let 命令详解

特性一:块级作用域

特性二:不存在变量提升

特性三:不允许重复声明

2.4 const 命令详解

2.5 var、let、const 对照表

2.6 数据类型全解

原始类型(基础类型)

复合类型(引用类型)

2.7 typeof 运算符

2.8 null 和 undefined 的区别

三、运算符

3.1 算术运算符

自增 ++ 和自减 —

3.2 赋值运算符

3.3 比较运算符

== 和 === 的区别 —— 非常重要!

3.4 布尔运算符(逻辑运算符)

取反 !

且运算符 &&

或运算符 ||

3.5 三元运算符

四、流程控制

4.1 if 条件语句

4.2 if…else 条件语句

多个条件连用 —— else if

if…else 嵌套

4.3 switch 条件语句

4.4 for 循环

实战示例

4.5 while 循环

4.6 break 和 continue

break —— "我不玩了,退出整个循环"

continue —— "这轮跳过,下一轮继续"

五、字符串

5.1 字符串基础知识

多行字符串

length 属性

5.2 字符串常用方法

charAt() —— 获取指定位置的字符

concat() —— 拼接字符串

substring() —— 提取子字符串(按起止位置)

substr() —— 提取子字符串(按起止位置 + 长度)

indexOf() —— 查找子字符串的位置

trim() —— 去除首尾空格

split() —— 分割字符串为数组

5.3 ES6 字符串扩展

模板字符串 —— 用反引号 ` 包裹(波浪号~下面的)

includes()、startsWith()、endsWith() —— 更强的搜索

repeat() —— 重复字符串

padStart()、padEnd() —— 补全长度

at() —— 支持负索引

字符串遍历器:for…of

Unicode 表示法

六、数组

6.1 数组基础

length 属性

6.2 数组遍历

6.3 数组常用方法

Array.isArray() —— 判断是不是数组

push() / pop() —— 尾部操作

shift() / unshift() —— 头部操作

join() —— 数组转字符串

concat() —— 合并数组

reverse() —— 反转数组

indexOf() —— 查找元素位置

6.4 ES6 数组扩展

扩展运算符 … —— 把数组 "摊开"

Array.from() —— 类数组转真数组

Array.of() —— 把一组值转为数组

7.1 函数基础

函数的声明

函数名的提升

函数参数

函数返回值 —— return

7.2 ES6 箭头函数

各种变体

箭头函数和普通函数的最大区别 —— this

八、对象

8.1 对象概述

对象的链式引用

8.2 Math 对象

Math.abs() —— 绝对值

Math.max() / Math.min() —— 最大值 / 最小值

Math.floor() / Math.ceil() —— 向下取整 / 向上取整

Math.random() —— 随机数

8.3 Date 对象

Date.now() —— 获取当前时间戳

时间戳是什么?

Date 常用 get 方法

实战:计算今年还剩多少天

8.4 ES6 对象扩展

属性的简洁表示法

属性名表达式

对象的扩展运算符

8.5 对象解构赋值

巧用解构:提取对象的方法

注意事项

九、Set 数据结构

9.1 Set 是什么

快速去重

类型区分

size 属性

9.2 Set 的常用方法

十、DOM 操作 —— 操控网页的魔法

10.1 DOM 是什么

节点(Node)

节点树

10.2 获取页面元素

getElementById() —— 按 ID 查找(最常用)

getElementsByTagName() —— 按标签名查找

getElementsByClassName() —— 按 class 查找

getElementsByName() —— 按 name 属性查找

querySelector() —— CSS 选择器(最灵活)

querySelectorAll() —— 查找所有匹配

NodeList 是什么?(重点)

10.3 创建页面元素

createElement() —— 创建元素节点

createTextNode() —— 创建文本节点

createAttribute() —— 创建属性节点

10.4 Element 对象属性

id

className / classList

classList 四个方法逐个拆解

① add (' 类名 ') → 添加一个 class

② remove (' 类名 ') → 删除指定 class

③ toggle (' 类名 ') → 自动切换(最常用)

④ contains (' 类名 ') → 判断是否存在,返回布尔值

innerHTML / innerText

10.5 获取元素位置和尺寸

常用场景

10.6 JavaScript 操作 CSS

方式一:通过 style 属性

方式二:通过 cssText 一次性设置

方式三:通过 setAttribute

十一、事件处理

11.1 事件处理的三种方式

方式一:HTML 事件(不推荐)

方式二:DOM0 级事件处理

方式三:DOM2 级事件处理(推荐!)

11.2 鼠标事件

11.3 键盘事件​编辑

通过 keyCode 判断按了哪个键

11.4 表单事件

① e

② e.target

③ .value

11.5 Event 事件对象

Event.target —— 事件发生在哪个元素上

Event.type —— 事件类型

Event.preventDefault() —— 阻止默认行为

Event.stopPropagation() —— 阻止事件冒泡

11.6 事件代理(事件委托)

用事件代理之前(低效)

用事件代理之后(高效)

十二、异步编程

12.1 什么是异步

12.2 定时器:setTimeout

this 指向问题

取消定时器

12.3 定时器:setInterval

实战:用 setInterval 实现渐隐动画

CSS 属性:opacity

12.4 Promise 对象

为什么需要 Promise?

Promise 基本用法

实战:用 Promise 封装图片加载

实战:用 Promise 封装 Ajax 请求

12.5 Async 函数

基本语法

实战:async + Promise 处理多个 Ajax 请求

12.6 防抖(Debounce)

问题场景

防抖的思想

1. 参数说明

2. 闭包 let timer = null

3. 内部执行逻辑(重点)

翻译滚动场景:

适用场景

12.7 节流(Throttle)

防抖的问题

节流的思想

实现代码

核心逻辑拆解

防抖 vs 节流 —— 对比总结

实际开发选择

十三、面向对象编程 —— Class

13.1 传统方式:构造函数和原型

13.2 Class 基本语法

constructor 方法

类的实例化

类不存在提升

13.3 Class 的属性与方法

实例属性

实例方法

静态方法

十四、开发工具与环境

14.1 Node.js 环境搭建

Node.js 是什么

安装 Node.js

14.2 npm 包管理工具

常用 npm 命令

npm 镜像配置

14.3 Babel 转码器(了解即可)

为什么需要 Babel?

Babel 安装与配置

命令行转码

附录:快速命令速查表

Windows 命令行常用命令

JavaScript 学习路线图


前言

提示:这里可以添加本文要记录的大概内容:   如果把网页比作一个人,HTML 是骨架(结构),CSS 是衣服和妆容(表现),而 JavaScript (JS) 就是肌肉和神经(行为/交互)

        JS 不仅仅能做网页特效,还能做游戏、服务器(Node.js)、甚至桌面软件

        很多刚接触前端的小伙伴都会觉得 JavaScript 难,分不清变量、循环、DOM、事件这些概念,写代码总出现undefined、死循环、事件不触发等 bug。

        这份笔记完全基于基础 JS 核心知识点整理,覆盖基础语法→数据类型→运算符→流程控制→字符串 / 数组→函数→内置对象→DOM 操作→事件→定时器→防抖节流全流程。 全程避开晦涩专业术语,每个知识点配套极简可运行 demo 代码,同时标注高频易错点、面试基础考点,零基础跟着复制代码就能看懂、上手练习,不用额外翻阅其他资料。


提示:以下是本篇文章正文内容

一、JavaScript 基础入门

1.1 JavaScript 是什么

        JavaScript(简称 JS)是一种轻量级的脚本语言。所谓 "脚本语言",指的是它不像 C 语言、Java 那样能独立开发操作系统,而是被设计用来 "嵌" 在其他大型应用程序里,充当控制角色——最常见的就是嵌在浏览器中,让静态网页 "活" 起来。

        你可以这样理解:如果说 HTML 是网页的骨架、CSS 是网页的衣服,那么 JavaScript 就是网页的肌肉和大脑——它负责让网页动起来、响应用户操作、与服务器通信。

JavaScript 有三大突出优点:

  • 操控浏览器的能力:可以动态修改网页内容、样式、响应用户点击、键盘输入等,几乎无所不能。
  • 使用领域广泛:从前端到后端(Node.js),从移动端到桌面端,JavaScript 几乎无处不在。
  • 易学性:语法相对直观,入门门槛低,适合零基础学习。
  • 1.2 ECMAScript 与 JavaScript 的关系

    很多初学者会困惑:JavaScript 和 ECMAScript 到底什么关系?一句话概括:

    ECMAScript 是 JavaScript 的 "语法标准",JavaScript 是 ECMAScript 的一种 "具体实现"。

            打个比方:ECMAScript 好比是 "普通话的语法规范",而 JavaScript 就是大家日常说的 "普通话"。在不同的浏览器里,还有各自的名字(比如 Chrome 的 V8 引擎),但它们都遵循 ECMAScript 这个标准。日常交流中,这两个词基本可以换着用。

    1.3 JavaScript 版本发展史

    JavaScript 的标准由 TC39 委员会(Technical Committee 39)负责制定。下面是重要版本一览:

    特别注意:ES6 既指 2015 年发布的那一版,也泛指 ES5.1 以后的所有新标准(ES2015、ES2016、ES2017 等)。

    语法提案的批准流程

    一个新的语法从提出到正式成为标准,需要经过 五个阶段(Stage):

    • Stage 0 —— Strawman(展示阶段):有人提出一个想法。
    • Stage 1 —— Proposal(征求意见阶段):委员会觉得值得讨论。
    • Stage 2 —— Draft(草案阶段):开始起草具体规范。进入这一阶段的提案,基本跑不掉了。
    • Stage 3 —— Candidate(候选人阶段):规范基本完成,等待最后审批。
    • Stage 4 —— Finished(定案阶段):正式纳入标准!

    所有提案都可以在 github.com/tc39/ecma262https://github.com/tc39/ecma262 上查看。

    ES6 带来的主要新特性

    ES6 是一次 "大版本更新",引入的新特性非常多,本教程会逐一讲解:

  • let 和 const 命令 —— 更安全的变量声明方式
  • 变量的解构赋值 —— 更优雅的取值方式
  • 字符串扩展 —— 模板字符串、新方法
  • 函数扩展 —— 箭头函数
  • 对象扩展 —— 属性简写、扩展运算符
  • 数组扩展 —— 扩展运算符、新方法
  • Promise 对象 —— 解决回调地狱
  • Class —— 面向对象编程的语法糖
  • Async 函数 —— 让异步代码像同步一样写
  • Set 数据结构 —— 不重复的集合
  • 1.4 为什么学 JavaScript

    如果你在犹豫该不该学 JS,这里有三个理由说服你:

  • 就业面广:前端工程师、全栈工程师、小程序开发者……几乎所有互联网公司都在招 JavaScript 开发者。
  • 生态庞大:npm 上超过 100 万个开源包,几乎你能想到的功能都有人写好了。
  • 学习友好:打开浏览器就能写代码、立即看到效果,反馈极快,适合自驱学习。
  • 1.5 开发环境准备:命令行工具

    写代码离不开命令行。但是问题不大,我们只需要掌握几个最基本的命令。

    1.5.1 打开命令行

    • Windows:按 Win + R,输入 cmd,回车。
    • Mac:按 Command + 空格,输入 terminal,回车。

    1.5.2 常用命令速查

    当然如果你会Linux的话,这个直接就会了

      

    1.5.3 PowerShell

    Windows 用户还可以使用 PowerShell(在开始菜单搜索即可打开),功能比 CMD 更强大。或者在任意文件夹按住 Shift + 右键,选择 "在此处打开 PowerShell 窗口"。日常命令和 CMD 基本一致。

    1.6 JavaScript 的引入方式

    JavaScript 代码必须通过 HTML 页面来运行。有三种引入方式:

    方式一:直接嵌入 HTML 中

    <script> 标签里直接写 JS 代码,适合代码量少的情况。

    方式二:引入本地独立的 JS 文件(推荐)

            把 JS 代码写在单独的 .js 文件里,HTML 中通过 src 属性引入。这是实际开发中最常用的方式,方便维护和复用。

            在现代网页开发中,type="text/javascript" 这个属性完全可以省略。

            这是因为从 HTML5 标准开始,浏览器默认就会把 <script> 标签里的内容当作 JavaScript 来执行。所以,就算写成下面这样,效果是完全一样的:

    <script src="./myScript.js"></script>

            只有一种情况需要显式声明 type:当使用 ES6 模块时。这时需要写成:

    <script type="module" src="./myScript.js"></script>

    方式三:引入网络上的 JS 文件(CDN)

    <body>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    </body>

            从 CDN(内容分发网络)直接加载,常用于引入第三方库(如 jQuery)。

    1.7 基础语法:语句与标识符

    1.7.1 语句

    JavaScript 程序一行一行地执行,每一行通常就是一个语句,语句以分号 ; 结尾:

    1.7.2 标识符(命名规则)

    标识符就是给变量、函数等起的名字。规则很简单:

    • 只能由字母、美元符号 $、下划线 _ 和数字组成
    • 数字不能开头
    • 不能使用 JavaScript 的保留关键字

    中文虽然也能做标识符,但强烈不推荐

            保留关键字(不需要死记,用多了自然记得):break、case、catch、class、const、continue、debugger、default、delete、do、else、export、extends、false、finally、for、function、if、import、in、instanceof、let、new、null、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield 等。

    1.8 注释与输出方式

    1.8.1 注释

    注释是写给自己和同事看的,计算机不会执行。善用注释是一个好习惯。

    HTML 中的注释写法不一样:

    1.8.2 三种输出方式

    写代码时要看结果,JS 提供了三种最常用的输出方式:

            小提示:开发调试时,console.log() 是最常用的,因为不会干扰页面显示。按 F12 打开浏览器开发者工具就能看到控制台。


    二、变量与数据类型

    2.1 变量的声明与赋值

            变量可以理解为一个 "贴了标签的盒子"——标签就是变量名,盒子里放的就是值。

    • 先声明、后赋值也是可以的:

    • 变量的值也可以修改(重新赋值):

    2.2 变量提升(Hoisting)

            这是 JavaScript 一个特有的、有点 "反直觉" 的行为。请仔细看:

            为什么没有报错?因为 JavaScript 引擎在执行代码前,会先 "扫一遍",把所有的 var 声明 "提升"(hoist)到代码最前面,但赋值操作留在原地。

    上面代码实际上相当于:        

            所以:变量可以在声明之前使用,但值是 undefined。这是 JS 的一个历史遗留问题,后面我们会学到 let 和 const 来解决它。

    2.3 let 命令详解

            ES6 新增了 let 命令,专门用来解决 var 的一些问题。let 有三个核心特性:

    特性一:块级作用域

    let 声明的变量,只在它所在的代码块(一对 {})里有效:

    for 循环的计数器用 let 最合适:

    经典对比:var 和 let 在循环中的表现差异很大:

    特性二:不存在变量提升

    let 声明的变量必须先声明后使用,这更符合正常的编程逻辑。

    特性三:不允许重复声明

    2.4 const 命令详解

    const 用于声明常量——一旦声明,值就不能再改:

    const 的规则总结:

            1.声明时必须立即赋值,不能留到后面:

            2.块级作用域,和 let 一样:

            3.不存在提升:

            4.不可重复声明:

            一个重要细节:const 保证的是变量的引用地址不变,而不是变量指向的内容不变。如果 const 声明的是一个对象,对象的属性是可以修改的。

    2.5 var、let、const 对照表

    最佳实践:默认使用 const,需要重新赋值时用 let,尽量避免使用 var。

    2.6 数据类型全解

    JavaScript 的每一个值都属于某一种数据类型。总体上分为两大类:

    原始类型(基础类型)

    JavaScript 有三种最基本的原始类型:

    ES6 新增了 Symbol 类型和 BigInt 类型,入门阶段暂不涉及。

    复合类型(引用类型)

    对象(Object):对象是多个原始类型值的 "集合",可以理解为一个收纳盒:

            对象里面的 name、age、learn 叫做属性(property),冒号后面的值可以是任何类型。如果一个属性的值是函数,我们就把它叫做方法(method)。

    2.7 typeof 运算符

    想知道一个值是什么类型,用 typeof:

    注意:不需要加括号()

    2.8 null 和 undefined 的区别

    两者都表示 "没有",但含义不同:

    • undefined:变量声明了但没赋值,系统自动给的值。
    • null:开发者主动设置的空值,表示 "这里应该有一个对象,但目前没有"。

            两者的历史渊源很有趣:JavaScript 最初设计时只打算用一个 null,但后来发现需要区分 "没赋值" 和 "主动置空" 两种情况,于是有了 undefined。这也算是 JS 的一个 "设计遗产" 了。

    三、运算符

    3.1 算术运算符

    就是做数学计算的符号,和小学学的没什么两样,会C语言,这个就会了:

    自增 ++ 和自减 —

    这两个运算符需要特别注意写在前面和后面的区别:

    再来看一个容易出错的例子:

    记忆口诀:「++ 在前先加后用,++ 在后先用后加」。

    3.2 赋值运算符

    最基本的赋值就是等号 =:

    赋值运算符可以和算术运算符结合,形成 "简写":

    3.3 比较运算符

    比较运算符用来比较两个值的大小,结果是一个布尔值(true 或 false):

    == 和 === 的区别 —— 非常重要!

    这是 JavaScript 面试最常问的问题之一:

    最佳实践:永远使用 === 做比较,避免 == 带来的意外类型转换。

    3.4 布尔运算符(逻辑运算符)

    取反 !

    把布尔值颠倒:true 变 false,false 变 true:

            如果对非布尔值取反,JavaScript 会先把它转成布尔值。记住这 六个 "假值",它们取反后都是 true:

    • undefined
    • null
    • false
    • 0
    • NaN
    • 空字符串 ''

    且运算符 &&

    所有条件都满足才是 true,有一个不满足就是 false:

    或运算符 ||

    只要有一个条件满足就是 true:

    3.5 三元运算符

    三元运算符 ? : 是 if…else 的简写形式,由三部分组成:

    • 条件为 true → 执行表达式1
    • 条件为 false → 执行表达式2

    例子:判断奇偶数

    三元运算符特别适合简单的条件判断,让代码更加干净。


    四、流程控制

    程序不会永远按直线执行,流程控制让程序能 "做选择" 和 "循环重复"。

    4.1 if 条件语句

    if 语句的意思是 "如果条件成立,就执行某段代码":

    ⚠️ 常见陷阱:不要混淆 =(赋值)和 ===(比较)!

    var x = 1;
    var y = 2;
    if (x = y) { // 注意!这里是把 y 的值赋给了 x,而不是比较
    console.log(x); // 输出 2,因为赋值表达式返回被赋的值
    }
    // 正确写法应该是 if (x === y)

    4.2 if…else 条件语句

    "如果……否则……" 的逻辑:

    if (m === 3) {
    // 条件为真时执行
    } else {
    // 条件为假时执行
    }

    多个条件连用 —— else if

    if (m === 0) {
    // 情况 A
    } else if (m === 1) {
    // 情况 B
    } else if (m === 2) {
    // 情况 C
    } else {
    // 以上都不是
    }

    if…else 嵌套

    var eat = true;
    var food = "猪肉炖粉条";

    if (eat) {
    if (food == "双椒鱼头") {
    console.log('双椒鱼头');
    } else {
    console.log("猪肉炖粉条"); // 输出这个
    }
    } else {
    console.log('不吃了');
    }

    注意:else 总是和离自己最近的那个 if 配对。

    4.3 switch 条件语句

    当一个变量有多个等值判断时,switch 比一串 else if 更清晰:

    switch (fruit) {
    case "banana":
    console.log("香蕉");
    break; // 别忘了 break!
    case "apple":
    console.log("苹果");
    break;
    default:
    console.log("其他水果");
    }

    ⚠️ 致命错误 —— 忘记写 break:

    var x = 1;
    switch (x) {
    case 1:
    console.log('x 等于1');
    // 没有 break!会继续往下执行!
    case 2:
    console.log('x 等于2');
    default:
    console.log('x 等于其他值');
    }
    // 输出:
    // x 等于1
    // x 等于2
    // x 等于其他值

    没有 break 会造成 "穿透",连续执行后面所有的 case。每个 case 后面一定要写 break!

    4.4 for 循环

    循环就是让一段代码反复执行。for 循环的格式:

    for (初始化; 条件; 迭代) {
    循环体
    }

    三个部分各司其职:

  • 初始化:循环开始前执行一次,通常用来定义计数器。
  • 条件:每轮循环开始前检查,条件为 true 才继续。
  • 迭代:每轮循环结束后执行,通常用来更新计数器。
  • for (var i = 0; i < 3; i++) {
    console.log(i);
    }
    // 输出:0 1 2

    实战示例

    计算 1~100 的和:

    var sum = 0;
    for (var i = 1; i <= 100; i++) {
    sum += i;
    }
    console.log(sum); // 5050

    输出 1000 以内的奇数:

    for (var i = 0; i < 1000; i++) {
    if (i % 2 == 1) {
    console.log(i);
    }
    }

    打印九九乘法表:

    for (var i = 1; i <= 9; i++) {
    for (var j = 1; j <= i; j++) {
    var sum = i * j;
    document.write(i + "*" + j + "=" + sum + " ");
    }
    document.write("<br>"); // 每行结束换行
    }

    ⚠️:三个表达式都可以省略,但如果全省略,就成了死循环(程序会一直跑,浏览器卡死):

    for ( ; ; ) {
    console.log('Hello World'); // 永远停不下来!
    }

    4.5 while 循环

    while 的意思是 "只要条件成立,就一直执行":

    while (条件) {
    循环体
    }

    var i = 0;
    while (i < 100) {
    console.log('i 当前为:' + i);
    i = i + 1;
    }

    while(true) 是另一个死循环的例子:

    while (true) {
    console.log('Hello, world'); // 永远不会停!
    }

    for 循环都可以改写成 while 循环:

    // for 版本
    for (var i = 0; i < 3; i++) {
    console.log(i);
    }

    // while 版本 —— 完全等价
    var i = 0;
    while (i < 3) {
    console.log(i);
    i++;
    }

    4.6 break 和 continue

    两者都能在循环中 "跳转",但效果不同:

    break —— "我不玩了,退出整个循环"

    for (var i = 0; i < 5; i++) {
    if (i === 3) {
    break; // i 等于 3 时,直接跳出循环
    }
    console.log(i);
    }
    // 输出:0 1 2

    continue —— "这轮跳过,下一轮继续"

    for (var i = 0; i < 5; i++) {
    if (i === 3) {
    continue; // i 等于 3 时,跳过本轮,继续下一轮
    }
    console.log(i);
    }
    // 输出:0 1 2 4(跳过了 3)

    五、字符串

    5.1 字符串基础知识

    字符串就是零个或多个排在一起的字符,放在单引号或双引号里:

    'CDSlice'
    "CDSlice"

    引号嵌套规则:单引号里可以放双引号,双引号里可以放单引号:

    'key = "value"' // 正确:单引号里面用双引号
    "It's a long CDSlice" // 正确:双引号里面用单引号

    如果要在单引号里面再用单引号,需要用反斜杠 \\ 转义:

    'Did she say \\'Hello\\'?' // 输出:Did she say 'Hello'?
    "Did she say \\"Hello\\"?" // 输出:Did she say "Hello"?

    多行字符串

    字符串默认只能写在一行。如果需要多行,可以在每行末尾加反斜杠 \\:

    var longString = 'Long \\
    long \\
    string';
    // 结果:"Long long string"

    (ES6 的模板字符串可以更方便地写多行,后面会讲到。)

    length 属性

    返回字符串的长度(字符个数):

    var s = 'CDSlice';
    s.length // 9

    5.2 字符串常用方法

    charAt() —— 获取指定位置的字符

    var s = 'CDSlice';

    s.charAt(1) // "t" —— 索引从 0 开始
    s.charAt(s.length – 1) // "n" —— 获取最后一个字符
    s.charAt(-1) // "" —— 负数返回空字符串
    s.charAt(9) // "" —— 超出范围也返回空字符串

    concat() —— 拼接字符串

    var s1 = 'CDSlice';
    var s2 = 'sxt';

    s1.concat(s2) // "CDSlicesxt"
    s1 // "CDSlice" —— 原字符串不变!

    可以一次接多个,非字符串参数会自动转为字符串:

    'sxt'.concat('CDSlice', 'bjsxt') // "sxtCDSlicebjsxt"

    var one = 1, two = 2, three = '3';
    ''.concat(one, two, three) // "123"
    console.log(one + two + three) // "33" —— 注意!加法运算符会先做数值运算:1+2=3,然后 3+'3'='33'

    substring() —— 提取子字符串(按起止位置)

    'CDSlice'.substring(0, 2) // "CD" —— 索引 0 到 2(不含 2)
    'CDSlice'.substring(2) // "Slice" —— 省略第二个参数,取到末尾

    // 如果第一个参数大于第二个,会自动交换
    'CDSlice'.substring(7, 2) // "Slice" —— 等同于 substring(2, 7)

    // 负数会被当成 0
    'CDSlice'.substring(-3) // "CDSlice" —— 负数变 0
    'CDSlice'.substring(2, -3) // "CD" —— 第二个参数 -3 变 0,即 substring(0, 2)

    substr() —— 提取子字符串(按起止位置 + 长度)

    'CDSlice'.substr(2, 7) // "Slice" —— 从索引 2 开始,取 7 个字符(实际只取到 5 个)
    'CDSlice'.substr(2) // "Slice" —— 省略第二个参数,取到末尾

    // 第一个参数负数表示倒数
    'CDSlice'.substr(-5) // "Slice" —— 倒数第 5 位开始

    // 第二个参数负数 → 返回空字符串
    'CDSlice'.substr(4, -1) // ""

    indexOf() —— 查找子字符串的位置

    'hello world'.indexOf('o') // 4 —— 第一次出现 'o' 的位置
    'CDSlice'.indexOf('sxt') // -1 —— 没找到返回 -1
    'hello world'.indexOf('o', 6) // 7 —— 从索引 6 开始往后找

    trim() —— 去除首尾空格

    ' hello world '.trim() // "hello world"

    // 还能去掉制表符、换行符等
    '\\r\\nCDSlice \\t'.trim() // 'CDSlice'

    // ES6 新增:trimStart() 只去头部,trimEnd() 只去尾部
    " CDSlice ".trimEnd(); // " CDSlice"
    " CDSlice ".trimStart(); // "CDSlice "

    split() —— 分割字符串为数组

    'it|sxt|CDSlice'.split('|') // ["it", "sxt", "CDSlice"]
    'a|b|c'.split('') // ["a", "|", "b", "|", "c"] —— 空字符串按字符拆开
    'it|sxt|bz'.split() // ["it|sxt|bz"] —— 不传参数,整个字符串作为数组唯一元素

    // 第二个参数:限制返回数组的元素个数
    'it|sxt|bz'.split('|', 2) // ["it", "sxt"]

    5.3 ES6 字符串扩展

    模板字符串 —— 用反引号 ` 包裹(波浪号~下面的)

    这是 ES6 带来的极大便利!用 反引号 和 ${} 在字符串中嵌入变量:

    // ES5 拼接 —— 又臭又长
    let url = "www.CDSlice.com";
    let h1 = "<a href='" + url + "'>CDSlice</a>";

    // ES6 模板字符串 —— 清爽!
    let h2 = `<a href='${url}'>CDSlice</a>`;

    模板字符串还可以直接写多行,不需要反斜杠。

    includes()、startsWith()、endsWith() —— 更强的搜索

    let s = 'Hello world!';

    s.startsWith('Hello') // true —— 'Hello' 在开头?
    s.endsWith('!') // true —— '!' 在结尾?
    s.includes('o') // true —— 包含 'o'?

    // 第二个参数:从哪个位置开始搜索
    s.startsWith('world', 6) // true —— 从索引 6 开始,确实是 'world'
    s.includes('Hello', 6) // false —— 从索引 6 开始没有 'Hello'

    repeat() —— 重复字符串

    'x'.repeat(3) // "xxx"
    'hello'.repeat(2) // "hellohello"
    'na'.repeat(0) // ""

    padStart()、padEnd() —— 补全长度

    'x'.padStart(5, 'ab') // 'ababx' —— 前面用 'ab' 补到 5 位
    'x'.padStart(4, 'ab') // 'abax'

    'x'.padEnd(5, 'ab') // 'xabab' —— 后面补
    'x'.padEnd(4, 'ab') // 'xaba'

    at() —— 支持负索引

    const str = 'hello';
    str.at(1) // "e"
    str.at(-1) // "o" —— 负数表示倒数
    // 超出范围返回 undefined

    字符串遍历器:for…of

    for (let char of 'CDSlice') {
    console.log(char);
    }
    // 逐个输出:C D S l i c e

    Unicode 表示法

    ES6 增强了 Unicode 支持,可以用 \\u 加码点表示字符:

    "\\u0061" // "a"


    六、数组

    6.1 数组基础

    数组是按顺序排列的一组值,每个值有一个编号(索引,从 0 开始),用方括号包裹:

    var arr = ['sxt', 'CDSlice', 'it'];
    // 索引0 索引1 索引2

    访问数组元素用 数组名[索引]:

    console.log(arr[0]); // "sxt"
    console.log(arr[2]); // "it"
    console.log(arr[5]); // undefined —— 索引越界

    数组可以存放任何类型的数据,甚至数组里面套数组(多维数组):

    var arr = [100, [1, 2, 3], false];
    // 数值 嵌套数组 布尔值

    var a = [[1, 2], [3, 4]];
    a[0][1] // 2 —— 第一个子数组的第二个元素
    a[1][1] // 4

    length 属性

    返回数组的元素数量:

    ['sxt', 'CDSlice', 'it'].length // 3

    6.2 数组遍历

    把数组的每个元素都过一遍,常用三种方式:

    var a = ['sxt', 'CDSlice', 'it'];

    // 方式一:for 循环(最经典)
    for (var i = 0; i < a.length; i++) {
    console.log(a[i]);
    }

    // 方式二:while 循环
    var i = 0;
    while (i < a.length) {
    console.log(a[i]);
    i++;
    }

    // 方式三:for…in 循环
    for (var i in a) {
    console.log(a[i]);
    }

    6.3 数组常用方法

    Array.isArray() —— 判断是不是数组

    var arr = ['sxt', 'CDSlice', 'it'];
    Array.isArray(arr) // true

    // 注意!typeof 判断数组会返回 "object",无法区分
    typeof arr // "object"

    push() / pop() —— 尾部操作

    // push:尾部添加,返回新长度
    var arr = [];
    arr.push("并不喜欢吃鱼"); // 1
    arr.push('CDSlice'); // 2
    arr.push(true, {}); // 4 —— 一次可以加多个
    console.log(arr); // ["并不喜欢吃鱼", "CDSlice", true, {}]

    // pop:尾部删除,返回被删的元素
    arr.pop(); // {} —— 最后一个元素被取出
    console.log(arr); // ["并不喜欢吃鱼", "CDSlice", true]

    shift() / unshift() —— 头部操作

    var arr = ['并不喜欢吃鱼', 'CDSlice', 'WEB前端'];

    // shift:头部删除
    arr.shift(); // '并不喜欢吃鱼'
    console.log(arr); // ['CDSlice', 'WEB前端']

    // unshift:头部添加,返回新长度
    arr.unshift('CDSlice'); // 3
    console.log(arr); // ['CDSlice', 'CDSlice', 'WEB前端']

    // 可以一次加多个
    arr.unshift('WEB前端', 'CDSlice'); // ['WEB前端', 'CDSlice', …]

    join() —— 数组转字符串

    var a = [1, 2, 3, 4];

    a.join(' ') // "1 2 3 4" —— 用空格连接
    a.join(' | ') // "1 | 2 | 3 | 4"
    a.join() // "1,2,3,4" —— 默认用逗号

    // undefined 和 null 会变成空字符串
    [undefined, null].join('#') // "#"
    ['a',, 'b'].join('-') // "a–b"

    join 和 split 互为逆操作,可以实现数组 ⇄ 字符串互转:

    var arr = ["a","b","c"];
    var str = arr.join(""); // "abc"
    str.split(""); // ["a","b","c"] —— 又转回来了

    concat() —— 合并数组

    ['hello'].concat(['world']) // ["hello", "world"]
    ['hello'].concat(['world'], ['!']) // ["hello", "world", "!"]
    [1, 2, 3].concat(4, 5, 6) // [1, 2, 3, 4, 5, 6]
    [1, 2, 3].concat(4, 5, 6, [7, 8, 9]) // [1, 2, 3, 4, 5, 6, 7, 8, 9](会拆开一层)

    典型应用:上拉加载更多,用 concat 把新数据和旧数据合并。

    reverse() —— 反转数组

    var a = ['a', 'b', 'c'];
    a.reverse(); // ["c", "b", "a"]
    // 注意:会直接修改原数组!

    配合 split 和 join 实现字符串反转:

    var str = "hello";
    str.split("").reverse().join(""); // "olleh"
    str.split("").reverse().join("-"); // "o-l-l-e-h"

    indexOf() —— 查找元素位置

    var arr = ['a', 'b', 'c'];

    arr.indexOf('b') // 1
    arr.indexOf('y') // -1 —— 没找到

    // 第二个参数:从哪个位置开始找
    ['并不喜欢吃鱼', '百战', 'it'].indexOf('并不喜欢吃鱼', 1) // -1 —— 从索引 1 开始找不到

    6.4 ES6 数组扩展

    扩展运算符 … —— 把数组 "摊开"

    console.log(…[1, 2, 3]); // 1 2 3
    console.log(1, …[2, 3, 4], 5); // 1 2 3 4 5

    替代 apply 方法:

    // ES5 写法 —— 老套
    Math.max.apply(null, [14, 3, 77]);

    // ES6 写法 —— 简洁
    Math.max(…[14, 3, 77]); // 77

    合并数组的优雅写法:

    const arr1 = ['a', 'b'];
    const arr2 = ['c'];
    const arr3 = ['d', 'e'];

    // ES5
    arr1.concat(arr2, arr3); // ['a', 'b', 'c', 'd', 'e']

    // ES6 —— 更直观
    […arr1, …arr2, …arr3] // ['a', 'b', 'c', 'd', 'e']

    Array.from() —— 类数组转真数组

            很多看起来像数组的东西(有索引和 length),其实不是真数组,Array.from 专门解决这个问题:

    // 场景1:arguments 对象
    function add() {
    let collect = Array.from(arguments);
    collect.push(40); // 转成真数组后才能用 push
    console.log(collect);
    }
    add(10, 20, 30); // [10, 20, 30, 40]

    // 场景2:DOM 元素集合
    let divs = document.querySelectorAll('div');
    console.log(Array.from(divs)); // 转成真数组

    // 场景3:类数组对象
    let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 };
    Array.from(arrayLike); // ['a', 'b', 'c']

    Array.of() —— 把一组值转为数组

    Array.of(3, 11, 8) // [3, 11, 8]
    Array.of(3) // [3] —— 一个元素

    // 注意和 Array() 的区别!
    Array(3) // [,,,] —— 创建长度为 3 的空数组!


    7.1 函数基础

    函数是一段可以反复调用的代码块,像一个 "加工机器":输入参数 → 内部处理 → 输出结果。

    函数的声明

    function 函数名(参数) {
    函数体 —— 要执行的代码
    }

    function print(s) {
    console.log(s);
    }

    print("Hello"); // 调用函数,输出 "Hello"

    函数名的提升

    和变量一样,function 声明的函数也会被提升到代码顶部,所以可以先调用、后定义:

    add(); // 没问题!
    function add() {
    console.log("执行了");
    }

    函数参数

    参数就是函数接收的外部数据,不同参数得到不同结果:

    function square(x) {
    console.log(x * x);
    }

    square(2); // 4
    square(3); // 9

    函数返回值 —— return

    函数通过 return 把结果传回给调用者:

    function getName(name) {
    return name; // 把 name 返回出去
    }

    var myName = getName("CDSlice");
    console.log(myName); // "CDSlice"

    注意:return 后面的代码不会被执行!函数遇到 return 就结束了。

    7.2 ES6 箭头函数

    ES6 的箭头函数(=>)让函数的写法大大简化:

    // 普通函数
    var add = function (x) {
    return x;
    };

    // 箭头函数 —— 等价
    var add = (x) => x;

    各种变体

    // 多个参数,需要括号
    var add = (x, y) => x + y;

    // 没有参数,括号不能省
    var sayHello = () => 100;

    // 函数体有多条语句,需要大括号 + return
    var add = (x, y) => {
    var z = 10;
    return x + y + z;
    };

    // 直接返回对象,要在对象外加括号
    var createObj = (x, y) => ({ x: 10, y: 20 });

    箭头函数和普通函数的最大区别 —— this

    箭头函数没有自己的 this,它的 this 就是外层作用域的 this。 这是最重要的一条规则。

    看这个经典例子:

    var name = "CDSlice";

    var user = {
    name: "iwen",
    getName() {
    setTimeout(() => {
    console.log(this.name); // 输出 "iwen"
    });
    }
    };

    user.getName();

            如果在 setTimeout 里用普通函数,this 会指向全局对象(输出 "CDSlice");但箭头函数从外层(getName 方法)继承了 this,指向 user 对象,所以输出 "iwen"。这解决了 JavaScript 中一个长久以来让人头疼的 this 指向问题。

    var name = "CDSlice";

    var user = {
    name: "iwen",
    getName() {
    setTimeout(function() {
    console.log(this.name); // 输出 "CDSlice"
    });
    }
    };

    user.getName();

    • setTimeout 中的回调函数是普通函数,它在执行时,this 不再继承外层 getName 的上下文。
    • 在非严格模式下,普通函数的 this 默认指向全局对象 window。
    • 由于 var name = "CDSlice" 是全局变量,它被挂载在 window.name 上。
    • 所以 this.name 实际上是 window.name,最终输出 "CDSlice"。

    八、对象

    8.1 对象概述

    对象是 JavaScript 最重要的数据类型。简单说,对象就是一组 "键值对"(key-value) 的集合:

    var user = {
    name: 'CDSlice', // 键 name,值 'CDSlice'
    age: 13 // 键 age,值 13
    };

    • 对象的每个键(key)叫属性名(property)。
    • 值可以是任何类型——数值、字符串、布尔值、甚至另一个对象。
    • 如果属性的值是一个函数,我们把它叫方法(method):

    var user = {
    getName: function (name) {
    return name;
    }
    };
    user.getName("CDSlice"); // "CDSlice" —— 调用对象的方法

    对象的链式引用

    对象里面可以再嵌套对象,形成链条:

    var user = {
    name: "CDSlice",
    age: 13,
    container: {
    frontEnd: ["Web前端", "Android", "iOS"],
    backEnd: ["Java", "Python"]
    }
    };

    user.container.frontEnd // ["Web前端", "Android", "iOS"]

    8.2 Math 对象

    Math 是 JavaScript 内置的数学工具箱,提供各种数学计算功能。

    Math.abs() —— 绝对值

    Math.abs(1) // 1
    Math.abs(-1) // 1

    Math.max() / Math.min() —— 最大值 / 最小值

    Math.max(2, -1, 5) // 5
    Math.min(2, -1, 5) // -1

    Math.min() // Infinity —— 空参数时返回 Infinity!
    Math.max() // -Infinity —— 空参数时返回 -Infinity!

    Math.floor() / Math.ceil() —— 向下取整 / 向上取整

    Math.floor(3.2) // 3 —— 向下
    Math.floor(-3.2) // -4 —— 记住!-4 比 -3.2 小

    Math.ceil(3.2) // 4 —— 向上
    Math.ceil(-3.2) // -3 —— -3 比 -3.2 大

    Math.random() —— 随机数

    返回 0(含)到 1(不含)之间的随机小数:

    Math.random() // 比如 0.28525367438365223

    生成指定范围的随机数:

    function getRandom(min, max) {
    return Math.random() * (max – min) + min;
    }

    getRandom(5, 10); // 返回 5 到 10 之间的随机数

    8.3 Date 对象

    Date 对象用来处理日期和时间。它以1970 年 1 月 1 日 00:00:00 UTC 为 "时间原点"(也叫 Unix 纪元)。

    Date.now() —— 获取当前时间戳

    Date.now(); // 1635216733395 —— 从 1970-01-01 到现在过了多少毫秒

    时间戳是什么?

            时间戳就是从 1970 年 1 月 1 日 0 点开始,到现在的总秒数(或毫秒数)。为什么选 1970 年?因为 Unix 操作系统在 70 年代初诞生,开发者觉得用 1970-01-01 作为计时起点很方便,JavaScript 也沿用了这个习惯。

    Date 常用 get 方法

    var d = new Date('January 6, 2022');

    d.getTime() // 返回毫秒时间戳
    d.getDate() // 6 —— 几号
    d.getDay() // 星期几(0 是周日)
    d.getFullYear() // 2022 —— 四位年份
    d.getMonth() // 0 —— 注意!0 表示 1 月,11 表示 12 月!
    d.getHours() // 小时(0-23)
    d.getMinutes() // 分钟(0-59)
    d.getSeconds() // 秒(0-59)
    d.getMilliseconds() // 毫秒(0-999)

    实战:计算今年还剩多少天

    function leftDays() {
    var today = new Date(); // 今天
    var endYear = new Date(today.getFullYear(), 11, 31, 23, 59, 59, 999); // 今年最后一天
    var msPerDay = 24 * 60 * 60 * 1000; // 一天的毫秒数
    return Math.round((endYear.getTime() – today.getTime()) / msPerDay); // 差值 / 一天的毫秒数
    }

    8.4 ES6 对象扩展

    属性的简洁表示法

    当属性名和变量名相同时,可以只写一个:

    let name = "iwen";
    const user = {
    name, // 等同于 name: name
    age: 20
    };

    方法的简写:

    const o = {
    method() { // 省略了 function 关键字
    return "Hello!";
    }
    };

    // 等价于
    const o = {
    method: function() {
    return "Hello!";
    }
    };

    作为函数返回值时特别方便:

    function getPoint() {
    const x = 1;
    const y = 10;
    return { x, y }; // 等同于 { x: x, y: y }
    }

    getPoint(); // { x: 1, y: 10 }

    属性名表达式

    可以用表达式(放在方括号里)动态计算属性名:

    let propKey = 'CDSlice';

    let obj = {
    [propKey]: true, // 属性名是变量的值 'CDSlice'
    ['a' + 'bc']: 123 // 属性名是 'abc'
    };

    注意:这样定义的属性不能用 . 来访问。obj.propKey 返回 undefined,应该用 obj[propKey] 或 obj['CDSlice']。

    对象的扩展运算符

    ES2018 把扩展运算符引入对象,可以方便地复制和合并对象:

    let z = { a: 3, b: 4 };
    let n = { …z };
    console.log(n); // { a: 3, b: 4 }

    // 合并对象
    { …{}, a: 1 } // { a: 1 }

    8.5 对象解构赋值

    解构赋值的本质是:用 "模式匹配" 的方式,把对象里的属性值快速提取到变量里:

    1. 基础写法

    // 原始对象
    let user = { name: "iwen", age: 20 }

    // 解构:直接创建 name、age 两个变量
    let { name, age } = user;

    console.log(name) // iwen
    console.log(age) // 20

    等价于传统写法:

    let name = user.name
    let age = user.age

    对象的属性没有次序,变量名必须和属性名一致,才能取到正确的值:

    例子 1:调换顺序照样取值

    // 解构顺序 age 在前,name 在后
    let { age, name } = { name: "iwen", age: 20 };

    console.log(age) // 20
    console.log(name) // iwen

    例子 2:解构不存在的属性 = undefined

    let { sex, age, name } = { name: "iwen", age: 20 };
    console.log(sex) // undefined

    巧用解构:提取对象的方法

    // 从 Math 对象里取出 random、floor 两个方法
    let { random, floor } = Math;

    // 以后直接调用,不用写 Math.random()
    console.log(random());

    // 从 console 对象取出 log 方法
    let { log } = console;
    log('测试') // 等价于 console.log('测试')

    注意事项

    如果变量已经声明过,再用解构赋值必须加括号,否则 JavaScript 会误认为你在写代码块:

    错误写法(会报错):

    let name;
    // JS 会把开头的 { } 误认为代码块,直接语法报错
    { name } = { name: "张三" };

    正确写法:

    let name;
    // 加括号告诉JS这是赋值表达式,不是代码块
    ({ name } = { name: "张三" });

    console.log(name) // 张三

    九、Set 数据结构

    9.1 Set 是什么

            ES6 新增了 Set 这种数据结构。Set 很像数组,但有一个关键区别:所有成员的值都是唯一的,不会重复

    const s = new Set();

    [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

    for (let i of s) {
    console.log(i);
    }
    // 输出:2 3 5 4 (重复的 5 和 2 自动被过滤掉了!)

    快速去重

    Set 可以直接接受一个数组,自动去重:

    const set = new Set([1, 2, 3, 4, 4]);
    […set] // [1, 2, 3, 4]

    一行代码实现数组去重:

    […new Set(array)]

    字符串去重:

    […new Set('ababbc')].join('') // "abc"

    类型区分

    Set 不会做类型转换,5 和 "5" 被认为是不同的值:

    var mySet = new Set();
    mySet.add("5");
    mySet.add(5);
    console.log(mySet); // Set(2) {'5', 5}

    size 属性

    返回成员总数(类似数组的 length):

    const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
    items.size // 5

    9.2 Set 的常用方法

    var mySet = new Set();

    mySet.add("5"); // 添加 '5'
    mySet.has("5"); // true —— 存在
    mySet.delete("5"); // true —— 删除成功
    console.log(mySet); // Set(0) {size: 0}

    mySet.add("5");
    mySet.clear(); // 清空
    console.log(mySet); // Set(0) {size: 0}


    十、DOM 操作 —— 操控网页的魔法

    10.1 DOM 是什么

            DOM(Document Object Model,文档对象模型) 是 JavaScript 操作网页的 "桥梁"。浏览器会把 HTML 页面解析成一棵 "节点树"(DOM Tree),JavaScript 通过这棵树来增、删、改、查网页内容。

            DOM 本身不是 JavaScript 的一部分,它是一个接口规范。但操作 DOM 是 JavaScript 最常见的任务。

    节点(Node)

    DOM 的最小单位叫节点,一共有 7 种类型:

    节点树

    所有节点按层级构成一棵树:

    • 父节点(parentNode):直接上级节点
    • 子节点(childNodes):直接下级节点
    • 兄弟节点(sibling):同级的节点

    最顶层是 document 节点,代表整个文档:

    document.nodeType // 9

    10.2 获取页面元素

    操作页面的第一步是 "找到想要的元素"。有以下几种方式:

    getElementById() —— 按 ID 查找(最常用)

    <!– 给标签设置 id="para1" –>
    <p id="para1">这是段落1的原始文字</p>
    var elem = document.getElementById('para1');
    // 返回单个元素,没找到返回 null

    参数区分大小写!

    getElementsByTagName() —— 按标签名查找

    <p>第一段p标签</p>
    <p>第二段p标签</p>

    var paras = document.getElementsByTagName('p'); // 所有 <p> 标签
    var allElements = document.getElementsByTagName('*'); // 所有元素
    // 返回一个类数组 HTMLCollection

    getElementsByClassName() —— 按 class 查找

    var elements = document.getElementsByClassName('foo');
    var elements = document.getElementsByClassName('foo bar'); // 多个 class

    getElementsByName() —— 按 name 属性查找

    var forms = document.getElementsByName('CDSlice');
    // 常用于表单元素

    querySelector() —— CSS 选择器(最灵活)

    <!– 1. class 类名 –>
    <div class="myclass">第一个.myclass盒子</div>
    <div class="myclass">第二个.myclass盒子(不会被选中)</div>

    <!– 2. id 选择器 –>
    <h2 id="myId">这是id为myId的标题</h2>

    <!– 3. 嵌套层级选择器 div 里面的 p –>
    <div class="box">
    <p>我是div内部的p标签</p>
    </div>
    <p>我是外面的p,不会被选中</p>
    var el = document.querySelector('.myclass'); // 第一个匹配的
    var el = document.querySelector('#myId'); // 按 ID
    var el = document.querySelector('div p'); // 按嵌套关系

    document.querySelector(「CSS选择器字符串」) 只返回页面中第一个匹配规则的 DOM 元素,找不到返回 null 它的规则和写 CSS 样式选择器完全一模一样。

    querySelectorAll() —— 查找所有匹配

    var elements = document.querySelectorAll('.myclass');
    // 返回 NodeList

    document.querySelectorAll('CSS选择器') 选中页面中所有符合条件的元素,打包成一个 NodeList 集合返回 和 querySelector 最大区别:

    • querySelector → 只拿第一个匹配元素
    • querySelectorAll → 全部匹配元素

    <!DOCTYPE html>
    <html lang="zh-CN">
    <body>
    <div class="myclass">盒子1</div>
    <div class="myclass">盒子2</div>
    <div class="myclass">盒子3</div>
    <p class="myclass">段落也带这个类</p>

    <script>
    // 拿到全部 .myclass 元素,返回 NodeList
    var elements = document.querySelectorAll('.myclass');
    console.log(elements); // NodeList(4) [div, div, div, p]
    console.log(elements.length); // 4

    // 直接用 forEach 遍历(NodeList 天生支持 forEach)
    elements.forEach(function(item) {
    item.style.color = 'orange';
    item.style.margin = '10px';
    })

    // 下标取值,和数组一样
    elements[0].style.fontWeight = 'bold';
    </script>
    </body>
    </html>

    NodeList 是什么?(重点)

  • 类数组对象 可以用 elements[0]、elements.length,长得像数组,但不是纯数组。
  • 自带 .forEach() 方法 直接循环遍历非常方便,不用像 HTMLCollection 那样转数组。
  • 静态集合(不会自动更新 DOM) 页面后续用 JS 新增了 .myclass 元素,elements 里面不会自动加上,是执行那一刻的快照
  • 补充:转成真正数组(想用 map /filter 时用)

    let arr = Array.from(elements);
    // 或者扩展运算符
    let arr2 = […elements];

    arr.map(item => item);

    10.3 创建页面元素

    除了获取已有的元素,JavaScript 还可以动态创建新元素。

    createElement() —— 创建元素节点

    var newDiv = document.createElement('div');

    createTextNode() —— 创建文本节点

    var newDiv = document.createElement('div');
    var newContent = document.createTextNode('Hello');
    newDiv.appendChild(newContent); // 把文本放进 div

    createAttribute() —— 创建属性节点

    // 1. 创建一个属性对象,属性名叫 CDSlice
    var attr = document.createAttribute('CDSlice');
    // 2. 给这个属性赋值为 it
    attr.value = 'it';

    // 3. 获取页面上 id="root" 的DOM元素
    var root = document.getElementById('root');

    // 4. 把刚才创建好的属性挂载到 root 标签上
    root.setAttributeNode(attr);
    // root 元素多了 CDSlice="it" 属性

    10.4 Element 对象属性

    id

    // HTML: <p id="foo">
    var p = document.querySelector('p');
    p.id // "foo"

    className / classList

    // HTML: <div class="one two three" id="myDiv"></div>
    var div = document.getElementById('myDiv');
    div.className // "one two three"

    // classList 更强大
    div.classList.add('myCssClass'); // 添加 class
    div.classList.remove('myCssClass'); // 移除 class
    div.classList.toggle('myCssClass'); // 切换(有则删,无则加)
    div.classList.contains('myCssClass'); // 有没有?

    className 是一整串字符串整体操作,改起来很麻烦; classList 是 JS 专门给元素设计的类名工具箱,专门用来增、删、切换、判断单个 CSS 类,日常开发必用

    classList 四个方法逐个拆解
    ① add (' 类名 ') → 添加一个 class

    div.classList.add('myCssClass');

    执行后 class 变成: one two three myCssClass

    ② remove (' 类名 ') → 删除指定 class

    div.classList.remove('two');

    执行后 class 变成:
    `one three`

    ③ toggle (' 类名 ') → 自动切换(最常用)

    逻辑:

    • 如果元素已经有这个类 → 删掉
    • 如果没有 → 加上

    经典场景:点击按钮切换高亮、展开收起菜单

    // 点击一下加,再点一下删
    div.onclick = function(){
    this.classList.toggle('active');
    }

    ④ contains (' 类名 ') → 判断是否存在,返回布尔值

    div.classList.contains('one') // true
    div.classList.contains('abc') // false

    可以用来做 if 判断:

    if(div.classList.contains('active')){
    console.log('处于激活状态')
    }

    innerHTML / innerText

    el.innerHTML = '<span>Hello</span>'; // 识别 HTML 标签
    el.innerText = '<span>Hello</span>'; // 纯文本,标签当字符串显示

    区分一下之前学的,它们底层都在操作 DOM,但属于两套完全不同的开发方式

  • innerHTML / innerText:快捷赋值语法糖,一行字符串直接渲染内容,写起来最简单;
  • createElement / createTextNode / createAttribute:原生 DOM 节点手动创建,一层层搭结构,更底层、更严谨。 createAttribute 只负责创建属性,和文本 / HTML 内容本身没有直接从属关系,只是都属于 DOM API。
  • 10.5 获取元素位置和尺寸

    这是一个容易混淆但非常重要的知识点。学会这张表,你就打通了 "元素定位" 的任督二脉。

    常用场景

    // 获取视口(浏览器窗口)的高度
    document.documentElement.clientHeight

    // 获取网页总高度
    document.documentElement.scrollHeight

    // 获取滚动条滚动了多少
    document.documentElement.scrollTop

    10.6 JavaScript 操作 CSS

    方式一:通过 style 属性

    注意:CSS 属性名 background-color 在 JavaScript 中要写成 驼峰式 backgroundColor。

    方式二:通过 cssText 一次性设置

    var divStyle = document.querySelector('div').style;

    divStyle.cssText = 'background-color: red;'
    + 'border: 1px solid black;'
    + 'height: 100px;'
    + 'width: 100px;';

    element.style.cssText 可以一次性批量写入一堆 CSS 样式字符串,等价于直接给标签写 style="xxx"

    方式三:通过 setAttribute

    div.setAttribute(
    'style',
    'background-color:red; border:1px solid black;'
    );

    给 div 元素添加 / 覆盖行内 style 属性,最终 HTML 变成:

    <div style="background-color:red; border:1px solid black;"></div>


    十一、事件处理

    11.1 事件处理的三种方式

    事件就是用户对网页做的操作(点击、按键、滚动等等)。JavaScript 通过 "事件处理程序" 来响应这些操作。有三种写法:

    方式一:HTML 事件(不推荐)

    <button id="btn1" onclick="demo()">按钮</button>
    <script>
    function demo() {
    alert("hello");
    }
    </script>

    把事件处理直接写在 HTML 属性里,不推荐,因为 HTML 和 JS 混在一起不好维护。

    方式二:DOM0 级事件处理

    var btn1 = document.getElementById("btn1");
    btn1.onclick = function() {
    alert("Hello DOM0级事件");
    };
    // 问题:同一个事件只能绑定一个函数,后面的会覆盖前面的
    btn1.onclick = function() {
    alert("这会把上一个覆盖掉!");
    };

    onclick 就是鼠标点击事件 btn1.onclick = 函数 意思:给按钮绑定点击行为,点一下就执行函数里面的代码

    方式三:DOM2 级事件处理(推荐!)

    var btn1 = document.getElementById("btn1");

    // addEventListener —— 可以绑定多个处理函数
    btn1.addEventListener("click", demo1);
    btn1.addEventListener("click", demo2);
    btn1.addEventListener("click", demo3);

    // removeEventListener —— 移除事件处理
    btn1.removeEventListener("click", demo2);

    DOM2 级的优势:可以给同一个事件绑定多个函数,互不覆盖。实际开发中一律使用这种方式。removeEventListener 解绑必须和绑定传入同一个函数引用,匿名函数无法解绑。

  • 无 DOM1 事件:DOM1 只规范节点增删改查,没碰事件体系;
  • DOM0:元素.onclick = 函数,简单但会覆盖,临时测试用;
  • DOM2:addEventListener('click', fn),官方标准,支持多绑定、精准解绑、控制事件流,工作必用
  • 11.2 鼠标事件

    这里的clik需要重点强调一下:

    1.DOM0 级事件(赋值写法):必须写 onclick

    // DOM0 元素属性绑定,前缀加 on
    btn.onclick = function(){}
    btn.onmouseenter = function(){}

    2.DOM2 级 addEventListener(标准绑定):只写 click,不带 on

    // 第二个参数是纯事件名字符串,不加 on
    btn.addEventListener('click', function(){})
    btn.addEventListener('mouseenter', function(){})

    除了 DOM2 级的 addEventListener,其他方式需要在事件名前加 on。

    11.3 键盘事件

    通过 keyCode 判断按了哪个键

    var username = document.getElementById("username");
    username.onkeydown = function(e) {
    if (e.keyCode === 13) { // 13 是回车键的 keyCode
    console.log("你按了回车!");
    }
    };

    11.4 表单事件

    // input 事件 —— 每输入一个字符都会触发
    var username = document.getElementById("username");
    username.oninput = function(e) {
    console.log(e.target.value); // 实时获取输入内容
    };

    // change 事件 —— 输入完离开输入框时才触发
    var email = document.getElementById("email");
    email.onchange = function(e) {
    console.log(e.target.value);
    };

    ① e

    事件处理函数自带的事件对象,里面存了这次触发事件的所有信息(点了哪个元素、按了什么键、输入了什么内容)。

    ② e.target

    target 翻译:目标 = 真正触发本次事件的那个 DOM 元素(这里就是你的 input 输入框) 等价于 username / email 这个变量本身。

    e.target = 鼠标实际点下去的那个最底层、最具体的 HTML 元素 谁被直接点击,它就指向谁。

    1. 最简单单层结构

    <p id="p">测试文字</p>

    2. 嵌套结构(最能理解 target)

    给 p 绑定点击事件,点文字: e.target === 这个<p>标签

    <div id="box">
    外层盒子
    <span>里面的小字</span>
    </div>

    给外层 box 绑定点击:

  • 点「外层盒子」文字 → target 是 <div id="box">
  • 点 <span> 里面小字 → target 是 <span>
  • ③ .value

    input 输入框自带属性,专门用来读取 / 修改框内的文本内容。 比如你在输入框敲了 abc123,input.value 就是字符串 "abc123"。

    input 和 change 的区别很重要:

    • input:你的手指每按下一次键盘,它就触发一次。适合做实时搜索提醒。
    • change:等你输入完了、鼠标点到别处了,它才触发。适合做表单验证。

    11.5 Event 事件对象

    事件触发时,浏览器会生成一个 事件对象(Event),包含关于这次事件的所有信息。

    Event.target —— 事件发生在哪个元素上

    function setColor(e) {
    console.log(e.target); // 被点击的那个具体元素
    e.target.style.color = 'red';
    }

    para.addEventListener('click', setColor);

    Event.type —— 事件类型

    e.type // "click"、"keydown" 等

    Event.preventDefault() —— 阻止默认行为

    btn.onclick = function(e) {
    e.preventDefault(); // 比如阻止 <a> 标签的跳转
    console.log("点击了链接,但没有跳转");
    };

    Event.stopPropagation() —— 阻止事件冒泡

    btn.onclick = function(e) {
    e.stopPropagation(); // 事件不再往父级传播
    console.log("btn 被点击,父级不会感知到");
    };

    11.6 事件代理(事件委托)

    事件代理利用了 事件冒泡 的特性:子元素的事件会 "冒泡" 到父元素。所以我们可以在父元素上统一处理所有子元素的事件。

    用事件代理之前(低效)

    // 假设有 100 个 <li>,需要给每个都绑定点击事件
    var lis = document.querySelectorAll('li');
    for (var i = 0; i < lis.length; i++) {
    lis[i].addEventListener('click', function() {
    // 处理点击……
    });
    }

    用事件代理之后(高效)

    var ul = document.querySelector('ul');

    ul.addEventListener('click', function(event) {
    if (event.target.tagName.toLowerCase() === 'li') {
    console.log('你点击了:' + event.target.innerHTML);
    // 只绑定了一个事件,却能处理所有 <li> 的点击!
    }
    });

  • 只给父盒子 <ul> 绑定唯一 1 个点击事件;
  • 点击任意一个 <li>,事件冒泡跑到 ul 的监听函数里;
  • event.target = 你实际点到的那个最小标签;
  • event.target.tagName 获取标签名,大写 LI,转小写 li 判断;
  • 确认点的是 li,再执行业务代码。
  • 事件代理的好处:只需要一个事件处理程序、动态添加的子元素也自动有效、节省内存。


    十二、异步编程

    12.1 什么是异步

    先看一段代码,你能猜到输出顺序吗?

    function print() {
    setTimeout(() => {
    console.log("定时器");
    }, 1000);
    console.log("Hello");
    }
    print();

    // 输出顺序:
    // Hello ← 先输出
    // 定时器 ← 1 秒后才输出

            这就是异步:"定时器" 的代码不会阻塞后面的 "Hello",它们同时在进行。理解异步是掌握 JavaScript 的关键一步。

    12.2 定时器:setTimeout

    setTimeout 的意思是 "过一会儿再执行":

    setTimeout(function() {
    console.log("1 秒后执行");
    }, 1000); // 1000 毫秒 = 1 秒

    this 指向问题

    setTimeout 中的 this 默认指向全局对象(浏览器中是 window),而不是定义时的对象:

    var name = "sxt";
    var user = {
    name: "CDSlice",
    getName: function () {
    setTimeout(function() {
    console.log(this.name); // 输出 "sxt"(全局的 name)!
    }, 1000);
    }
    };
    user.getName();

    解决方案:用 var that = this 保存外部 this:

    var name = "sxt";
    var user = {
    name: "CDSlice",
    getName: function () {
    var that = this; // 保存 this
    setTimeout(function() {
    console.log(that.name); // 输出 "CDSlice" ✓
    }, 1000);
    }
    };
    user.getName();

    (ES6 的箭头函数可以更优雅地解决这个问题,忘了就去看第七章)

    取消定时器

    var id = setTimeout(f, 1000);
    clearTimeout(id); // 取消了,f 不会执行

    12.3 定时器:setInterval

    setInterval 的意思是 "每隔一段时间执行一次":

    interval /ˈɪntəvl/ 名词:间隔、间隙、一段时间

    var timer = setInterval(function() {
    console.log("每 1 秒输出一次");
    }, 1000);

    // 取消
    clearInterval(timer);

    实战:用 setInterval 实现渐隐动画

    var div = document.getElementById('someDiv');
    var opacity = 1;
    var fader = setInterval(function() {
    opacity -= 0.05;
    if (opacity > 0) {
    div.style.opacity = opacity;
    } else {
    clearInterval(fader); // 完全透明后停止
    }
    }, 30);

    opacity /əʊˈpæsəti/ 名词:不透明、浑浊度;在 CSS 里直接译作 透明度

    CSS 属性:opacity

    作用:控制整个元素(包括文字、背景、子元素)的整体透明程度 取值范围:0 ~ 1

    • opacity: 1 默认值,完全不透明
    • opacity: 0.5 半透明
    • opacity: 0 完全透明(元素看不见,但依然占位置、可以点击)

    12.4 Promise 对象

    为什么需要 Promise?

            在没有 Promise 的年代,异步操作只能用回调函数一层套一层,代码变成 "回调地狱"(Callback Hell):

    // 回调地狱示意图 —— 读着想吐
    step1(function(result1) {
    step2(result1, function(result2) {
    step3(result2, function(result3) {
    // 越来越深……
    });
    });
    });

    Promise 就是来拯救这种情况的:把异步操作用同步的方式表达出来。

    Promise 基本用法

    Promise 是一个构造函数,用来生成 Promise 实例:

    const promise = new Promise(function(resolve, reject) {
    // 异步操作代码…

    if (/* 操作成功 */) {
    resolve(value); // 成功 → 调用 resolve
    } else {
    reject(error); // 失败 → 调用 reject
    }
    });

    // 通过 .then 获取结果
    promise.then(
    function(value) { /* 成功的处理 */ },
    function(error) { /* 失败的处理 */ }
    );

            以上相当于一套固定模版,第一个函数是用来捕获成功与失败的信息的,通过.then()来进行失败和成功的处理

    通俗理解:

    • Promise 就像一张 "承诺书"(比如外卖订单)。
    • resolve:商家接单了,承诺会送到。
    • reject:商家取消了订单。
    • .then():拿到外卖后该做什么(吃!)。

    实战:用 Promise 封装图片加载

    function loadImage(url) {
    return new Promise(function(resolve, reject) {
    const image = new Image();
    image.onload = function() {
    resolve(image); // 加载成功
    };
    image.onerror = function() {
    reject(new Error('加载失败:' + url)); // 加载失败
    };
    image.src = url;
    });
    }

    // 使用
    loadImage("图片地址")
    .then(function(img) {
    document.body.appendChild(img); // 加载成功,显示图片
    }, function(error) {
    console.log(error); // 加载失败
    });

    实战:用 Promise 封装 Ajax 请求

    AJAX = Asynchronous JavaScript and XML 中文:异步 JavaScript 和 XML 核心意思:在不刷新整个网页的前提下,浏览器偷偷向后端服务器发送请求、获取数据,然后局部更新页面内容。

    这里学完HTTP相关网络知识即可理解了,也可以自行查阅一下

    function getJSON(url) {
    return new Promise(function(resolve, reject) {
    const handler = function() {
    if (this.readyState !== 4) return;
    if (this.status === 200) {
    resolve(this.response); // 请求成功
    } else {
    reject(new Error(this.statusText)); // 请求失败
    }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();
    });
    }

    // 使用
    getJSON("接口地址")
    .then(function(json) {
    console.log(json); // 拿到数据
    }, function(error) {
    console.error('出错了', error);
    });

    12.5 Async 函数

            ES2017 引入的 async/await 进一步简化了异步代码的写法,让异步代码看起来像同步代码。它是 Promise 的语法糖。

    基本语法

    async function asyncPrint(value, ms) {
    await timeout(ms); // 等 timeout 完成再往下走
    console.log(value); // 前面等完了才会执行这行
    }

    function timeout(ms) {
    return new Promise((resolve) => {
    setTimeout(resolve, ms);
    });
    }

    asyncPrint('hello world', 50);

    实战:async + Promise 处理多个 Ajax 请求

    function ajax(url) {
    return new Promise(function(resolve, reject) {
    $.getJSON(url, function(result) {
    resolve(result);
    }, function(error) {
    reject(error);
    });
    });
    }

    async function getInfo() {
    let ids = await ajax("接口1"); // 等接口1返回
    let names = await ajax("接口2?id=" + ids[0]); // 拿到 ids 后再调接口2
    let infos = await ajax("接口3?name=" + names.name); // 拿到 names 后再调接口3
    console.log(infos); // 最终结果
    }

    getInfo();

            三个请求是依次执行的,前一个完成才调用下一个,数据传递非常清晰。如果用传统的回调写法,三层嵌套会让人眼花缭乱。

    12.6 防抖(Debounce)

    问题场景

    滚动条事件触发得太频繁了!滚动一下可能触发 8~9 次:

    function showTop() {
    var scrollTop = document.documentElement.scrollTop;
    console.log('滚动条位置:' + scrollTop);
    }
    window.onscroll = showTop; // 触发频率太高!

    1. 函数 showTop

    • document.documentElement:指代整个 <html> 根标签
    • .scrollTop:只读属性,返回页面顶部被卷上去的高度 比如往下滑 300 像素,scrollTop 就是 300
    • 作用:实时打印滚动距离,常用于:回到顶部按钮显示、滚动导航栏吸顶、滚动动画等

    2. 滚动事件绑定

    • window.onscroll:浏览器自带的页面滚动事件
    • 只要鼠标滚轮滑动、拖动滚动条、手机上下滑动,就会立刻执行 showTop 函数

    防抖的思想

    "等你停下来,我再执行。"

    具体规则:

  • 事件触发后,不立即执行,而是等一段"冷静期"(比如 300ms)。
  • 如果在冷静期内又触发了事件,重新计时。
  • 冷静期过了没有新事件,才真正执行。
  • 效果:短时间多次触发,只执行一次。

    function debounce(fn, delay) {
    let timer = null; // 利用闭包保存定时器
    return function() {
    if (timer) {
    clearTimeout(timer); // 还在冷静期?取消上次的计时
    }
    timer = setTimeout(fn, delay); // 重新开始计时
    };
    }

    // 使用
    function showTop() {
    var scrollTop = document.documentElement.scrollTop;
    console.log('滚动条位置:' + scrollTop);
    }
    window.onscroll = debounce(showTop, 300);

    1. 参数说明

    • fn:需要被防抖保护的业务函数(这里就是 showTop)
    • delay:等待延迟时间,单位毫秒(例子里 300ms)

    2. 闭包 let timer = null

    debounce 执行完会返回内部函数,timer 变量被内层函数一直锁住不会销毁,全程共用同一个定时器标记,这是防抖的关键。

    3. 内部执行逻辑(重点)

  • 每次滚动触发 onscroll → 进入返回的匿名函数
  • 判断 timer 是否有值:有值 = 上一次倒计时还在跑,立刻 clearTimeout 清空之前的定时器
  • 重新新建 setTimeout,约定:停止滚动 300ms 后再运行 fn(showTop)
  • 翻译滚动场景:
    • 手一直在滑滚轮 → 疯狂触发滚动事件,每一次都清空倒计时,永远不执行打印
    • 手松开滚轮、页面静止不动 → 300ms 倒计时走完,执行一次 showTop,打印最终滚动距离

    适用场景

    • 搜索框输入:等待用户停止输入后再发起搜索请求。
    • 窗口 resize:等窗口调整完毕再重新计算布局。

    12.7 节流(Throttle)

    防抖的问题

            防抖好是好,但如果用户一直不停操作(比如一直滚动),函数就永远不会执行。有时候产品经理的要求是:即使用户一直操作,也要每隔一段时间给一次反馈。

    节流的思想

    "就算你一直来,我也有冷却时间,冷却好了才接下一单。"

    就像一个技能有冷却时间(CD):执行一次后,进入冷却期,冷却期内不响应,冷却期过了才能再次执行。

    效果:一段时间内多次触发,按固定频率执行。

    实现代码

    function throttle(fn, delay) {
    let valid = true; // true = 可以工作,false = 冷却中
    return function() {
    if (!valid) {
    return false; // 冷却中,不执行
    }
    valid = false; // 立即进入冷却
    setTimeout(function() {
    fn(); // 冷却结束,执行函数
    valid = true; // 重新开放
    }, delay);
    };
    }

    // 使用
    function showTop() {
    var scrollTop = document.documentElement.scrollTop;
    console.log('滚动条位置:' + scrollTop);
    }
    window.onscroll = throttle(showTop, 300);

    核心逻辑拆解

  • valid 是闭包变量,只会初始化一次,全程共用;
  • 第一次滚动进来:valid=true → 上锁 valid=false,开启 300ms 倒计时;
  • 这 300ms 内不管滚动触发多少次,都会进入 !valid 直接 return,全部拦截;
  • 300ms 到了:执行 showTop,再把 valid 改回 true,放开下一轮
  • 防抖 vs 节流 —— 对比总结

    实际开发选择

  • 搜索框输入实时推荐 → 用节流,间隔一段时间就查一次。
  • 搜索框输入完成后搜索 → 用防抖,输入间隔超过 500ms 才发起搜索。
  • 页面 resize 重新布局 → 用防抖,只需要最后一次变化的结果。
  • 滚动加载更多 → 用节流,固定间隔检查是否到底。

  • 十三、面向对象编程 —— Class

    13.1 传统方式:构造函数和原型

    在 ES6 之前,JavaScript 用构造函数 + 原型来实现类似 "类" 的概念:

    function Point(x, y) {
    this.x = x;
    this.y = y;
    }

    Point.prototype.toString = function () {
    return '(' + this.x + ', ' + this.y + ')';
    };

    var p = new Point(1, 2);
    p.toString(); // "(1, 2)"

    这种方式来自其他语言(Java、C++)的程序员会觉得怪怪的。

    13.2 Class 基本语法

            ES6 引入了 class 关键字,让面向对象编程的写法更接近传统语言。但本质上,class 只是构造函数的 "语法糖"——底层还是原型继承。

    class Point {
    constructor(x, y) { // 构造函数,new 的时候自动调用
    this.x = x;
    this.y = y;
    }

    toString() { // 实例方法
    return '(' + this.x + ', ' + this.y + ')';
    }
    }

    var p = new Point(1, 2);
    p.toString(); // "(1, 2)"

    constructor 方法

            每个 class 都有 constructor,它是类的默认方法,new 的时候自动执行。如果没有显式定义,JavaScript 会自动加一个空的。

    class Point {} // 等价于
    class Point {
    constructor() {}
    }

    类的实例化

    类必须用 new 调用,直接当函数调用会报错:

    var point = Point(2, 3); // 报错!
    var point = new Point(2, 3); // 正确

    类不存在提升

    和普通函数不同,类必须先定义后使用:

    new Foo(); // 报错!ReferenceError
    class Foo {}

    13.3 Class 的属性与方法

    实例属性

    在 constructor 中用 this.属性名 定义:

    class People {
    constructor(name, age) {
    this.name = name; // 实例属性 name
    this.age = age; // 实例属性 age
    }
    }

    var p = new People("iwen", 20);
    console.log(p.name, p.age); // "iwen" 20

    实例方法

    直接在 class 里定义的方法,会被所有实例继承:

    class People {
    say() {
    console.log("Hello");
    }
    }

    var p = new People();
    p.say(); // "Hello"

    静态方法

    前面加 static 关键字的方法,只能通过类本身调用,实例调用不了:

    class Person {
    static classMethod() {
    console.log("我是静态方法");
    }
    }

    Person.classMethod(); // "我是静态方法" ✓

    var p = new Person();
    p.classMethod(); // 报错!p.classMethod is not a function

            什么时候用静态方法? 当这个方法不依赖于具体的实例数据,只和类本身相关时。比如 Math.max() 就是典型的静态方法。


    十四、开发工具与环境

    14.1 Node.js 环境搭建

    Node.js 是什么

    Node.js 诞生于 2009 年,它让 JavaScript 脱离了浏览器,能在服务器上运行。从此 JavaScript 可以写后端代码了。

    安装 Node.js

  • 访问 nodejs.orghttps://nodejs.org/en/
  • 下载 LTS(长期支持)版本
  • 像安装普通软件一样,一路下一步即可
  • 安装完成后,打开命令行,输入以下命令验证:

    node -v
    # 显示版本号,如 v16.14.0,表示安装成功

    14.2 npm 包管理工具

            安装 Node.js 时会自动附带 npm(Node Package Manager)。npm 是世界上最大的开源库生态系统,托管了超过 100 万个免费开源包。

    常用 npm 命令

    npm install 包名 # 安装一个包
    npm install -g 包名 # 全局安装
    npm uninstall 包名 # 卸载

    npm 镜像配置

    由于 npm 服务器在国外,下载速度可能很慢。可以用国内的阿里镜像加速:         镜像可以这么理解,就是你要下载一个仓库里的东西,只不过这个东西在国外所以你是国内的网下载的很慢,然后搬一份一样的仓库来到国内,直接在国内下载

    npm install -g cnpm –registry=https://registry.npmmirror.com

    安装成功后,之后可以用 cnpm 代替 npm,下载速度会快很多。

    镜像地址:npmmirror.com

    14.3 Babel 转码器(了解即可)

    为什么需要 Babel?

    浏览器对新版 JavaScript(ES6+)的支持速度不一致。你可能写了一段很酷的箭头函数,但有些用户的旧浏览器不认识。Babel 就是解决这个问题的:

    Babel 是一个 ES6 转码器,把 ES6 代码转成 ES5 代码,让所有浏览器都能运行。

    可以到查看各浏览器对不同特性的支持情况。caniuse.comhttps://caniuse.com/转码示例:

    // 转码前(ES6 箭头函数)
    input.map(item => item + 1);

    // 转码后(ES5 普通函数)
    input.map(function (item) {
    return item + 1;
    });

    Babel 安装与配置

    步骤一:安装核心包

    npm install –save-dev @babel/core

    步骤二:创建配置文件 .babelrc

    在项目根目录下创建 .babelrc 文件

    步骤三:安装转码规则

    npm install –save-dev @babel/preset-env

    步骤四:配置规则

    命令行转码

    安装命令行工具:

    npm install –save-dev @babel/cli

    用法:

    # 输出到屏幕
    npx babel example.js

    # 输出到文件
    npx babel example.js –out-file compiled.js
    # 或简写
    npx babel example.js -o compiled.js

    # 整个目录转码
    npx babel src –out-dir lib
    # 或简写
    npx babel src -d lib

    附录:快速命令速查表

    Windows 命令行常用命令

    JavaScript 学习路线图

            写在最后:学习编程没有捷径,最好的方法就是 "敲代码"。本教程中的每一个代码示例,都建议你亲手敲一遍,修改参数看看效果,遇到报错就尝试自己解决。JavaScript 的世界很广阔,但有了扎实的基础,你就可以自信地探索更高级的领域——React、Vue、Node.js 后端开发、小程序等等。

    赞(0)
    未经允许不得转载:171主机测评 » 三.前端web开发:100%零基础吃透 JavaScript 全部核心知识(全文共计38000字左右)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址