【前端】JavaScript自学笔记

type
Post
status
Published
summary
这是一篇JS的学习笔记。一直觉得拥有一个自己亲手写的网页会是一件很酷的事情,但是一直都没有开始去做这件事,最近终于下定决心从基础开始,一步步学习然后写一个自己的网页。
slug
javascript
date
Aug 18, 2023
tags
JS
JavaScript
前端
category
基础知识
password
icon
URL
Property
Feb 28, 2024 01:06 PM
笔记主要整理自:菜鸟教程

一、注释

单行注释://
多行注释:/* */

二、变量声明

变量声明规范

  • 变量必须以字母开头
  • 变量也能以 $ 和 _ 符号开头(不过我们不推荐这么做)
  • 变量名称对大小写敏感(y 和 Y 是不同的变量)
  • 一个好的编程习惯是,在代码开始处,统一对需要的变量进行声明。
  • 函数内声明的变量是局部变量,只能在函数内部访问使用它,只要函数运行完毕,局部变量就会被删除
  • 函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它,全局变量会在页面关闭后被删

var变量声明

var 在现代 JavaScript 中已经被 let 和 const 所取代。let 和 const 具有块级作用域,更符合现代编程习惯。
var变量声明是最常规的变量声明
var carname; // 声明空变量 carname="Volvo"; // 空变量赋值 var carname="Volvo"; // 声明变量并赋值 var lastname="Doe", age=30, job="carpenter"; // 声明多个变量并赋值,逗号之间可以换行 var x,y,z=1; // 只有z有值,x和y是空变量 var carname="Volvo"; var carname; // 重新声明变量,值不会变 // 声明新变量时,可以使用关键词 "new" 来声明其类型,这样创建的变量为对象,String对象,Number对象…… var carname=new String; var x=new Number; var y=new Boolean; var cars=new Array; var person=new Object; // 使用typeof查看变量数据类型 typeof person // const 声明一个只读的常量,一旦声明,常量的值就不能改变。

let变量声明

let 声明的变量只在 let 命令所在的代码块内(通常是花括号{}内)有效,他的作用域比var声明的变量更小
// 作用域内重新定义变量 var x = 10; // 这里输出 x 为 10 { var x = 2; // 这里输出 x 为 2 } // 这里输出 x 为 2 var x = 10; // 这里输出 x 为 10 { let x = 2; // 这里输出 x 为 2 } // 这里输出 x 为 10 // 循环作用域 var i = 5; for (let i = 0; i < 10; i++) { // 一些代码...,这里i最后为10 } // 这里输出 i 为 5 var i = 5; for (var i = 0; i < 10; i++) { // 一些代码...,这里i最后为10 } // 这里输出 i 为 10

const变量声明

  • 同样是块级作用域
  • 声明时必须进行初始化
  • const 定义的对象或者数组,是可变的
const 定义的常量的值,不可变
// 错误写法 const PI; PI = 3.14159265359; // 正确写法 const PI = 3.14159265359; const car = {type:"Fiat", model:"500", color:"white"}; car = {type:"Volvo", model:"EX60", color:"red"}; // 错误 // 创建常量对象 const car = {type:"Fiat", model:"500", color:"white"}; // 修改属性: car.color = "red"; // 创建常量数组 const cars = ["Saab", "Volvo", "BMW"]; // 修改元素 cars[0] = "Toyota"; // 添加元素 cars.push("Audi");

const 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的

const x = 2; // 合法 { const x = 3; // 合法 } { const x = 4; // 合法 }
 

var和let、const的区别

  • var 在现代 JavaScript 中已经被 let 和 const 所取代。let 和 const 具有块级作用域,更符合现代编程习惯。
  • var 关键字定义的变量可以先使用再声明,let 和const 关键字定义的变量需要先声明再使用
使用 var 关键字声明的全局作用域变量属于 window 对象;使用 let 关键字声明的全局作用域变量不属于 window 对象
  • 在 JavaScript 中, 全局作用域是针对 JavaScript 环境,在 HTML 中, 全局作用域是针对 window 对象,
var carName = "Volvo"; // 可以使用 window.carName 访问变量 let carName = "Volvo"; // 不能使用 window.carName 访问变量
使用 var 关键字声明的变量任何地方都可以用var 关键字来重置变量
var x = 2; // x 为 2 var x = 3; // 现在 x 为 3
在相同的作用域或块级作用域中,不能使用 let 和const 关键字来重置 var 关键字声明的变量
var x = 2; // 合法 let x = 3; // 不合法 { var x = 4; // 合法 let x = 5 // 不合法 }
在相同的作用域或块级作用域中,不能使用 let 和const 关键字来重置 let 和const关键字声明的变量
let x = 2; // 合法 let x = 3; // 不合法 { let x = 4; // 合法 let x = 5; // 不合法 }
在相同的作用域或块级作用域中,不能使用 var 关键字来重置 let 和const 关键字声明的变量
let x = 2; // 合法 var x = 3; // 不合法 { let x = 4; // 合法 var x = 5; // 不合法 }
在不同的作用域或不同块级作用域中,let 和const 关键字声明的变量是可以重新声明赋值的
let x = 2; { let x = 3; // 合法 } // 合法, x = 2 let x = 2; { var x = 4; // 合法 } // 合法, x = 4 const x = 2; // 合法 { const x = 3; // 合法 } { const x = 4; // 合法 }

声明提升

  • 函数及变量的声明都将被提升到函数的最顶部。如果变量和函数在调用之后才被声明,那代码在执行时会将声明代码提升到前面先执行
  • 需要注意的是只有变量的声明会被提升,初始化(赋值)不会。这里需要将声明和赋值分开来看
    • 声明:var x
    • 赋值:x = 7
    • <p id="demo"></p> <script> var x = 5; // 初始化 x elem = document.getElementById("demo"); // 查找元素 elem.innerHTML = "x 为:" + x + ",y 为:" + y; // 显示 x 和 y var y = 7; // 初始化 y </script> // p标签最终显示:x 为:5,y 为:undefined // 因为只提升了声明,告诉“执行者”有这个变量;但是没有提升赋值,所以这个变量最终显示未undefined
  • 尽量在每个作用域开始前声明这些变量
  • 严格模式
    • 严格模式通过在脚本或函数的头部添加 use strict; 表达式来声明,
    • 在函数内部声明是局部作用域 (只在函数内使用严格模式)
      • <script> "use strict"; x = 3.14; // 报错 (x 未定义) </script>
      严格模式的限制
      • 不能使用未声明的变量
      • 不允许删除变量、对象、函数
      • 不允许变量重名
      • 不允许使用八进制
      • 不允许使用转义字符
      • 不允许对只读属性赋值
      • 不允许对一个使用getter方法读取的属性进行赋值
      • 不允许删除一个不允许删除的属性
      • 变量名不能使用 "eval” 、"arguments”字符串

三、数据类型

值类型(基本类型)

字符串(String)

var answer="It's alright"; var answer="He is called 'Johnny'"; // 单引号套双引号 var answer='He is called "Johnny"'; // 双引号套单引号 var x = 'It\'s alright'; // 转义字符 var y = "He is called \"Johnny\""; // 转义字符 var character = answer[7]; // 寻址 var sln = answer.length; // 计算字符串长度 var x = "John"; var y = new String("John"); // String对象会拖慢执行速度,并可能产生其他副作用 typeof x // 返回 String typeof y // 返回 Object const str = '9e9e520dcdf04eb1-93e6-cb3fd6a0bc01'; str.replace('-','') // 替换指定字符;只会替换第一个匹配到的内容 str.replaceAll('-','') // 替换指定字符;全局替换 str.replace(/-/g,'') // 替换指定字符;全局替换,区分大小写 str.replace(/-/gi,'') // 替换指定字符;全局替换,不区分大小写 // 模版字符串 // 使用反引号 `` 作为字符串的定界符分隔,支持多行文本无需转义 // 支持占位符 ${} ,可以传入其他变量,函数(调用),表达式等 const name = 'Runoob'; const age = 30; const message = `My name is ${name} and I'm ${age} years old.`; let price = 10; let VAT = 0.25; let total = `Total: ${(price * (1 + VAT)).toFixed(2)}`;

数字(Number)

var x1=34.00;     //使用小数点来写 var x2=34;        //不使用小数点来写 var y=123e5;      // 12300000 var z=123e-5;     // 0.00123

布尔(Boolean)

var x=true; var y=false;

空(Null)

  • null 表示 "什么都没有”
  • 用 typeof 检测 null 返回是object
  • 可以通过将变量的值设置为 null 来清空变量:cars=null;
  • null 用于对象, undefined 用于变量,属性和方法;对象只有被定义才有可能为 null,否则为 undefined

未定义(Undefined):

  • Undefined 这个值表示变量不含有值
  • typeof 一个没有值的变量会返回 undefined
  • 任何变量都可以通过设置值为 undefined 来清空
  • null 和 undefined 的值相等,但类型不等
    • typeof undefined // undefined typeof null // object null === undefined // false 值相等,类型不等 null == undefined // true,值相等,类型不管

Symbol

 

数据类型转换

查看数据类型
  • typeof 操作符
    • typeof "John" // 返回 string typeof 3.14 // 返回 number typeof NaN // 返回 number,NaN 的数据类型是 number typeof false // 返回 boolean typeof [1,2,3,4] // 返回 object,数组(Array)的数据类型是 object typeof {name:'John', age:34} // 返回 object typeof new Date() // 返回 object,日期(Date)的数据类型为 object typeof function () {} // 返回 function typeof myCar // 返回 undefined,未定义变量的数据类型为 undefined typeof null // 返回 object,null 的数据类型是 object
  • constructor构造函数
    • "John".constructor // 返回函数 String() { [native code] } (3.14).constructor // 返回函数 Number() { [native code] } false.constructor // 返回函数 Boolean() { [native code] } [1,2,3,4].constructor // 返回函数 Array() { [native code] } {name:'John', age:34}.constructor // 返回函数 Object() { [native code] } new Date().constructor // 返回函数 Date() { [native code] } function () {}.constructor // 返回函数 Function(){ [native code] }
类型转换方法
  1. 转换为字符串
      • 全局方法String()用于任何类型的数字,字母,变量,表达式,布尔值、日期,将其转换为字符串
      • Number、Boolean、Date的方法 toString() 也是有同样的效果
      String(x) // 将变量 x 转换为字符串并返回 String(123) // 将数字 123 转换为字符串并返回 String(100 + 23) // 将数字表达式转换为字符串并返回 String(false) // 返回 "false" String(true) // 返回 "true" String(new Date()) // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time) // Number的toString()方法 x.toString() (123).toString() (100 + 23).toString() // Boolean的toString()方法 false.toString() // 返回 "false" true.toString() // 返回 "true" // Date的toString()方法 obj = new Date() obj.toString() // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)
  1. 转换为数字
    1. 全局方法Number() 
      • 可以将字符串转换为数字,空字符串转换为 0,其他的字符串会转换为 NaN (不是个数字)
      • 可将布尔值转换为数字
      • 可将日期转换为数字
        • Number("3.14") // 返回 3.14 Number(" ") // 返回 0 Number("") // 返回 0 Number("99 88") // 返回 NaN Number(false) // 返回 0 Number(true) // 返回 1 d = new Date(); Number(d) // 返回 1404568027739 d = new Date(); d.getTime() // 返回 1404568027739
      一元运算符
      // + 可用于将变量转换为数字 var y = "5"; // y 是一个字符串 var x = + y; // x 是一个数字 // 如果变量不能转换,它仍然会是一个数字,但值为 NaN var y = "John"; // y 是一个字符串 var x = + y; // x 是一个数字 (NaN)
  1. 转换为布尔值
      • 见上两个转换
  1. 自动转换类型
    1. 5 + null // 返回 5 null 转换为 0 "5" + null // 返回"5null" null 转换为 "null" "5" + 1 // 返回 "51" 1 转换为 "1" "5" - 1 // 返回 4 "5" 转换为 5
 

引用数据类型(对象类型)

数组(Array)

// 创建数组 var cars=new Array(); cars[0]="Saab"; cars[1]="Volvo"; cars[2]="BMW"; var cars=new Array("Saab","Volvo","BMW"); var cars=["Saab","Volvo","BMW"];
// 数组示例 var arr = [1, 2, 3, 4, 5]; // length 属性:返回数组的长度 arr.length; // 5 // push():将元素添加到数组末尾,并返回新的长度 arr.push(6); // 6 // pop():删除数组的最后一个元素,并返回该元素的值 arr.pop(); // 6 // shift():删除数组的第一个元素,并返回该元素的值 arr.shift(); // 1 // unshift():将元素添加到数组开头,并返回新的长度 arr.unshift(0); // 5 // concat():将两个或多个数组合并成一个新数组 var newArr = arr.concat([6, 7, 8]); // [0, 2, 3, 4, 5, 6, 7, 8] // slice():返回数组的一个片段或子数组,左闭右开,原数组不变 var slicedArr = arr.slice(1, 4); // [2, 3, 4] // indexOf():返回指定元素在数组中首次出现的索引,如果不存在则返回 -1 arr.indexOf(3); // 2 var arr = [1, 2, 3, 4, 5]; // 删除元素:从索引位置 2 开始删除 2 个元素,并返回被删除的元素数组 var removed = arr.splice(2, 2); // 返回 [3, 4];arr 变为 [1, 2, 5] // 添加元素:从索引位置 2 开始删除 0 个元素,并添加元素 'a', 'b' 到这个位置 arr.splice(2, 0, 'a', 'b'); // arr 变为 [ 1, 2, 'a', 'b', 5 ] // 替换元素:从索引位置 1 开始删除 2 个元素,并添加元素 'x', 'y' 到这个位置 var replaced = arr.splice(1, 2, 'x', 'y'); // 返回 [2, 'a'];arr 变为 [ 1, 'x', 'y', 'b', 5 ] // map() 用于遍历数组,并返回一个同等长度的新数组 let arr = [1, 2, 3]; let arr2 = arr.map((e) => e * 2); // filter() 用于过滤数组,返回指定条件的元素 let arr = [1, 2, 3]; let arr2 = arr.filter((e) => e >= 2); // some() 方法用于检查数组中的任意元素是否满足所提供的条件 const hasEvenNumber = numbers.some(num => num % 2 === 0); // 判断数组中是否存在偶数 console.log(hasEvenNumber); // 输出 true,因为数组中包含偶数 // forEach():对数组中的每个元素执行指定的函数 arr.forEach(function(element) { // 对每个元素进行操作 });

对象(Object)

  • 对象由花括号分隔,在括号内部,对象的属性以名称和值对的形式 (name : value) 来定义,属性由逗号分隔
// 创建有三个属性的对象person,属性分别是:firstname、lastname 以及 id var person={firstname:"John", lastname:"Doe", id:5566}; var person = { firstName: "John", // 对象属性1 lastName : "Doe", // 对象属性2 id : 5566, // 对象属性3 fullName : function() // 对象方法1 { return this.firstName + " " + this.lastName; } }; name=person.lastname; // 寻址方式1 name=person["lastname"]; // 寻址方式2 document.getElementById("demo").innerHTML = person.fullName(); // 调用对象方法,需要在方法名后加(),相当于调用一个函数,返回:John Doe document.getElementById("demo").innerHTML = person.fullName; // 调用对象属性,返回方法的字符串:function() { return this.firstName + " " + this.lastName; }
// 对象示例 var obj = { name: 'John', age: 30, city: 'New York' }; // Object.keys():返回对象中所有可枚举属性的名称数组 var keys = Object.keys(obj); // ['name', 'age', 'city'] // Object.values():返回对象中所有可枚举属性的值数组 var values = Object.values(obj); // ['John', 30, 'New York'] // Object.entries():返回对象中所有可枚举属性的键值对数组 var entries = Object.entries(obj); // [['name', 'John'], ['age', 30], ['city', 'New York']] // hasOwnProperty():检查对象是否具有指定的属性 obj.hasOwnProperty('name'); // true // JSON.stringify():将对象转换为 JSON 字符串 var jsonString = JSON.stringify(obj); // '{"name":"John","age":30,"city":"New York"}' // JSON.parse():将 JSON 字符串解析为 JavaScript 对象 var parsedObj = JSON.parse(jsonString); // {name: 'John', age: 30, city: 'New York'}

函数(Function)

  • 函数一般用关键词 function来定义,关键词 function 必须是小写
  • 函数的定义可以在调用代码之后(函数提升)(Python中必须在前)
  • 实参和形参只能通过位置来一一对应
  • 如果函数有返回值,则调用函数的地方将会被函数的返回值替代
  • 如果没有返回值,只是希望结束函数运行,也可以使用return
    • function myFunction(a, b) { return a * b; } // 分号是用来分隔可执行JavaScript语句,由于函数声明不是一个可执行语句,所以不以分号结束。
  • 函数表达式
    • var myFunction2 = function (a, b) {return a * b}; // 实际是一个匿名函数 // 函数存储在变量中,不需要函数名称,通常通过变量名来调用 // 以分号结尾,因为它是一个执行语句
  • 构造函数Function()定义函数
    • var myFunction1 = new Function("a", "b", "return a * b"); // 很多时候,需要避免使用 new 关键字
  • 函数自调用表达式
    • (function () { var x = "Hello!!"; // 我将调用自己 })();
  • 箭头函数
    • 语法
      • // 多个参数 (参数1, 参数2, …, 参数N) => { 函数声明 } (参数1, 参数2, …, 参数N) => 表达式(单一) // 相当于:(参数1, 参数2, …, 参数N) => { return 表达式; } // 单一参数(括号可选) (单一参数) => {函数声明} 单一参数 => {函数声明} // 没有参数(必须有括号) () => {函数声明}
    • 实例
      • const x = (x, y) => x * y; const double = x => x * 2; const add = (a, b) => a + b;
    • 重点
      • 箭头函数是不能提升的,所以需要在使用之前定义
      • 使用 const 比使用 var 更安全,因为函数表达式始终是一个常量
      • 如果函数部分只是一个语句,则可以省略 return 关键字和大括号 {}
      • 箭头函数会默认绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的
  • 闭包
    • 闭包可以让函数捕获并访问其外部函数的作用域中的变量,并且保持外部函数的变量状态,即使外部函数已经执行完毕。
      一般情况当函数执行完之后会有一个return,return完了之后,函数关闭,内部的变量定型,除非再次执行函数,否则不能访问和改变。
      而闭包则是在return的时候抛出了一个遥控器(原函数的一个子函数),即使原函数关闭,还是可以通过运行遥控器访问和修改里面的变量
      var add = (function () { var counter = 0; return function () {return counter += 1;} })(); add(); add(); add(); // 计数器为 3
      在上面例子中,声明一个变量add,初始化值为一个自调用函数,所以最后add的值就是这个自调用函数的返回值
      自调用函数先声明了一个变量counter并赋值为0,然后返回了一个函数
      所以当add被声明完成之后,自调用函数也会执行,并且把匿名函数返回给add,此时add相当于也是一个函数了
      此时counter是自调用函数中的一个变量,并且自调用函数已经执行完成了,正常情况在自调用函数外是不能访问和修改counter变量了
      但是当调用add函数时,还是能够访问自调用函数中的变量counter
       
      闭包的一个实例
      var Counter = (function () { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function () { changeBy(1); }, decrement: function () { changeBy(-1); }, value: function () { return privateCounter; }, }; })(); console.log(Counter.value()); /* logs 0 */ Counter.increment(); Counter.increment(); console.log(Counter.value()); /* logs 2 */ Counter.decrement(); console.log(Counter.value()); /* logs 1 */
 

日期(Date)

正则(RegExp)

用法和案例
  • 语法
    • /正则表达式主体/修饰符(可选)
  • 实例
    • var patt = /runoob/i // i:搜索不区分大小写
正则语法
详见
修饰符
描述
i
执行对大小写不敏感的匹配。
g
执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m
执行多行匹配。
在 JavaScript 中,正则表达式通常用于两个字符串方法 : search() 和 replace()。
search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。
function myFunction() { var str = "Visit Runoob!"; var n = str.search(/Runoob/i); // var n = str.search("Runoob"); // 使用字符串作为参数。字符串参数会转换为正则表达式 document.getElementById("demo").innerHTML = n; }
replace() 方法用于在字符串中用一些字符串替换另一些字符串,或替换一个与正则表达式匹配的子串。
<p id="demo">Visit Microsoft!</p> <script> function myFunction() { var str = document.getElementById("demo").innerHTML; var txt = str.replace(/microsoft/i,"Runoob"); // var txt = str.replace("Microsoft","Runoob"); document.getElementById("demo").innerHTML = txt; } </script>

四、运算符

算术运算符

y=5
运算符
描述
例子
x 运算结果
y 运算结果
在线实例
+
加法
x=y+2
7
5
-
减法
x=y-2
3
5
*
乘法
x=y*2
10
5
/
除法
x=y/2
2.5
5
%
取模(余数)
x=y%2
1
5
++
自增
x=++y
6
6
x=y++
5
6
--
自减
x=--y
4
4
x=y--
5
4
  • 加号+也可以用来拼接字符串
  • 两个数字相加,返回数字相加的和;如果数字与字符串相加,返回字符串

赋值运算符

x=10  y=5
运算符
例子
等同于
运算结果
在线实例
=
x=y
x=5
+=
x+=y
x=x+y
x=15
-=
x-=y
x=x-y
x=5
*=
x*=y
x=x*y
x=50
/=
x/=y
x=x/y
x=2
%=
x%=y
x=x%y
x=0

比较运算符

  • 比较运算符在逻辑语句中使用,以测定变量或值是否相等
  • 一般在条件语句中使用比较运算符对值进行比较
x=5
运算符
描述
比较
返回值
实例
==
等于
x==8
false
x==5
true
===
绝对等于(值和类型均相等
x==="5"
false
x===5
true
!=
不等于
x!=8
true
!==
不绝对等于(值和类型有一个不相等,或两个都不相等)
x!=="5"
true
x!==5
false
>
大于
x>8
false
<
小于
x<8
true
>=
大于或等于
x>=8
false
<=
小于或等于
x<=8
true

逻辑运算符

逻辑运算符用于测定变量或值之间的逻辑
x=6 、 y=3
运算符
描述
例子
&&
and
(x < 10 && y > 1) 为 true
||
or
(x==5 || y==5) 为 false
!
not
!(x==y) 为 true

条件运算符(三元运算符)

  • 语法
    • condition ? expressionIfTrue : expressionIfFalse;
    • condition 是一个表达式,用于表示一个条件,如果这个条件为真(真值),则执行 expressionIfTrue
    • expressionIfTrue 是在条件为真时返回的值或执行的表达式。
    • expressionIfFalse 是在条件为假时返回的值或执行的表达式。
  • 实例
    • const age = 18; const canVote = age >= 18 ? "Can vote" : "Cannot vote"; console.log(canVote); // 输出: "Can vote"

空值合并运算符

const contents = (page.content ?? []) // 如果 page.content 的值不为 null 或 undefined,则将 page.content 的值赋给 contents。否则,将一个空数组 [] 赋给 contents。

五、条件与循环

条件语句

if 语句

  • 只有当指定条件为 true 时,使用该语句来执行代码
  • 语法
    • if (condition) { 当条件为 true 时执行的代码 }
  • 实例
    • if (time<20) { x="Good day"; } if (i==3) break; // if 语句只有一行代码,所以可以省略花括号

if...else 语句

  • 当条件为 true 时执行代码,当条件为 false 时执行其他代码
  • 语法
    • if (condition) { 当条件为 true 时执行的代码 } else { 当条件不为 true 时执行的代码 }
  • 实例
    • if (time<20) { x="Good day"; } else { x="Good evening"; }
 

if...else if....else 语句

  • 使用该语句来选择多个代码块之一来执行
  • 语法
    • if (condition1) { 当条件 1 为 true 时执行的代码 } else if (condition2) { 当条件 2 为 true 时执行的代码 } else { 当条件 1 和 条件 2 都不为 true 时执行的代码 }
  • 实例
    • if (time<10) { document.write("<b>早上好</b>"); } else if (time>=10 && time<20) { document.write("<b>今天好</b>"); } else { document.write("<b>晚上好!</b>"); }

switch 语句

  • 使用该语句来选择多个代码块之一来执行
  • 语法
    • switch(n) { case 1: 执行代码块 1 break; case 2: 执行代码块 2 break; default: 与 case 1 和 case 2 不同时执行的代码 }
  • 实例
    • var d=new Date().getDay(); switch (d) { case 0:x="今天是星期日"; break; // 使用 break 来阻止代码自动地向下一个 case 运行 case 1:x="今天是星期一"; break; case 2:x="今天是星期二"; break; case 3:x="今天是星期三"; break; case 4:x="今天是星期四"; break; case 5:x="今天是星期五"; break; case 6:x="今天是星期六"; break; default: // default 关键词来规定匹配不存在时做的事情 x="期待周末"; }

循环语句

for 循环

  • 代码块执行一定的次数
  • 语法
    • for (语句 1; 语句 2; 语句 3) { 被执行的代码块 } // 语句1 循环体(代码块)开始前执行,通常在这里初始化循环中所用的变量(可以多个),可选用 // 语句2 定义运行循环体(代码块)的条件,返回true则循环再次开始,如果返回false则循环将结束,可选用 // 如果省略语句2,那么必须在循环内提供 break。否则循环就无法停下来 // 语句3 在循环体(代码块)已被执行之后执行,当循环内部有相应的代码时可省略,,可选用
  • 实例
    • for (var i=0; i<5; i++) { x=x + "该数字为 " + i + "<br>"; } for (var i=0,len=cars.length; i<len; i++) // 初始化多个值 var i=2,len=cars.length; for (; i<len; i++) // 省略语句1 var i=0,len=cars.length; for (; i<len; ) // 省略语句1、3 { document.write(cars[i] + "<br>"); i++; }

for/in 循环

  • 遍历对象的属性
  • 实例
    • function myFunction(){ var x; var txt=""; var person={fname:"Bill",lname:"Gates",age:56}; for (x in person){ txt=txt + person[x]; } document.getElementById("demo").innerHTML=txt; }

while 循环

  • 指定的条件为 true 时循环指定的代码块
  • 语法
    • while (条件) { 需要执行的代码 }
  • 实例
    • function myFunction(){ var x="",i=0; while (i<5){ x=x + "该数字为 " + i + "<br>"; i++; } document.getElementById("demo").innerHTML=x; } /* 该数字为 0 该数字为 1 该数字为 2 该数字为 3 该数字为 4 */

do/while  循环

  • 指定的条件为 true 时循环指定的代码块
  • 会在检查条件是否为真之前执行一次代码块,然后如果条件为真的话,就会重复这个循环
  • 语法
    • do { 需要执行的代码 } while (条件);
  • 实例
    • function myFunction(){ var x="",i=0; do{ x=x + "该数字为 " + i + "<br>"; i++; } while (i<5) document.getElementById("demo").innerHTML=x; }

break 和 continue 语句

  • continue 用于跳过循环中的一个迭代,进入下一轮循环。
  • break 语句用于跳出整个循环,继续循环体之后的代码。(switch() 语句中也会用到break)
  • break跳出任何标记的代码块
    • 标记 JavaScript 语句,请在语句之前加上冒号
    • 通过标签引用,break 语句可用于跳出任何 JavaScript 代码块
    • <script> cars=["BMW","Volvo","Saab","Ford"]; list:{ document.write(cars[0] + "<br>"); document.write(cars[1] + "<br>"); document.write(cars[2] + "<br>"); break list; document.write(cars[3] + "<br>"); document.write(cars[4] + "<br>"); document.write(cars[5] + "<br>"); } </script> /* BMW Volvo Saab */

六、其他

代码规范

规范要求

  • 变量和函数为小驼峰法标识, 即除第一个单词之外,其他单词首字母大写( lowerCamelCase
  • 全局变量为大写 (UPPERCASE )
  • 常量 (如 PI) 为大写 (UPPERCASE )
  • 通常运算符 ( = + - * / ) 前后需要添加空格
  • 为了便于阅读每行字符建议小于数 80 个,如果超过了 80 个字符,建议在 运算符或者逗号后换行
  • 通常使用 4 个空格符号来缩进代码块(不推荐使用 TAB 键来缩进,因为不同编辑器 TAB 键的解析不一样)
  • 语句
    • 一条语句通常以分号作为结束符
    • 左花括号前添加一空格,放在第一行的结尾;将右花括号独立放在一行
    • 不要以分号结束一个复杂的声明(条件、循环、函数)
  • 对象
    • 左花括号与类名放在同一行
    • 冒号与属性值间有个空格
    • 字符串使用双引号,数字不需要
    • 最后一个属性-值对后面不要添加逗号
    • 将右花括号独立放在一行,并以分号作为结束符号

常见问题

  • 定义数组元素、对象,最后不能添加逗号(虽然语法没有问题,但是在不同的浏览器可能得到不同的结果)
    • var colors = [5, 6, 7,]; //这样数组的长度可能为3 也可能为4。 var colors = [5, 6, 7]; // 正确的定义方式 websites = {site:"菜鸟教程", url:"www.runoob.com", like:460,} // 错误的定义方式 websites = {site:"菜鸟教程", url:"www.runoob.com", like:460} // 正确的定义方式
  • 不同类型相加
    • 数字 + 数字 = 数字
    • 字符串 - 数字 = 数字
    • 数字 + 字符串 = 字符串
    • 字符串 + 数字 = 字符串
    • 字符串 + 字符串 = 字符串
  • 浮点型数据
    • JavaScript 中的所有数据都是以 64 位浮点型数据(float) 来存储
      • var x = 0.1; var y = 0.2; var z = x + y // z 的结果为 0.30000000000000004 if (z == 0.3) // 返回 false var z = (x * 10 + y * 10) / 10; // z 的结果为 0.3
  • if 语句
    • if (x == 19);{ 中间错误使用分号,导致方法体单独被执行,所以if条件无效
      • <p id="demo"></p> <script> var x = 5; if (x == 19);{ document.getElementById("demo").innerHTML = "Hello"; } </script>
  • return 语句
    • JavaScript 默认是在一行的末尾自动结束,如果是一个不完整的语句,JavaScript 将尝试读取第二行的语句
    • 如果return后面有要返回的内容,则不可以换行,因为return是一个完整的语句
      • function myFunction(a) { var power = 10; return // 相当于在return后加了分号,分号结束,返回 undefined a * power; }

异步编程

JavaScript 是单线程的,在某一时刻内只能执行特定的一个任务,并且会阻塞其它任务执行。异步编程是处理非阻塞操作的一种方式,允许代码在等待耗时操作完成时继续执行,而不会阻塞主线程。异步编程常常与回调函数一起使用,回调函数用于在异步操作完成时执行特定的代码。
JavaScript 在浏览器环境中常用于处理网络请求(例如 AJAX、fetch)、定时器(例如 setTimeout、setInterval)、用户交互(例如点击、键盘输入)、文件读写等,这些操作通常需要一些时间来完成。如果所有操作都是同步的,会导致主线程被阻塞,用户界面不会响应,影响用户体验。因此,异步编程在这些情况下非常重要,它允许在等待耗时操作完成时,主线程可以执行其他任务,从而保持页面的响应性。
可以理解为JavaScript主线程在执行过程中,发出了一些需要耗时很久的请求,但是它不能等到这些请求被响应了之后再执行之后的代码,这时候它就会把这些请求先放到等待队列里去,同时给这些请求写一个回调函数,这样就不用一直盯着这些等待的任务了,在请求被响应之后直接去执行回调函数,完成相应的后续工作。

回调函数

回调函数是一种以参数形式传递给另一个函数的函数;
回调函数通常用于处理异步操作,如网络请求、定时器、事件处理等,以确保在操作完成后执行特定的逻辑。
// 异步请求回调函数 <p>回调函数等待 3 秒后执行。</p> <p id="demo1"></p> <p id="demo2"></p> <script> setTimeout(function () { document.getElementById("demo1").innerHTML="RUNOOB-1!"; // 三秒后子线程执行 }, 3000); document.getElementById("demo2").innerHTML="RUNOOB-2!"; // 主线程先执行 </script>
// 这是一个模拟的异步函数,它接受一个回调函数作为参数 function fetchData(callback) { setTimeout(function () { const data = "这是从服务器获取的数据"; callback(data); // 调用回调函数,并传递数据作为参数 }, 1000); // 模拟异步操作需要的时间 } // 定义一个回调函数,在数据获取后被调用 function displayData(data) { console.log("从服务器获取到的数据:", data); } // 调用 fetchData 函数,并将 displayData 函数作为回调传递 fetchData(displayData); // 调用函数,但是函数内是一个setTimeout函数模拟了异步请求 console.log("等待数据获取中..."); // 最先执行 /* 等待数据获取中... 从服务器获取到的数据: 这是从服务器获取的数据 */
但回调函数嵌套会导致代码难以维护和异常处理,而且会让缩进格式变得非常冗赘,产生所谓的“回调地狱”
setTimeout(function () { console.log("First"); setTimeout(function () { console.log("Second"); setTimeout(function () { console.log("Third"); }, 3000); }, 4000); }, 1000);
为了解决这个问题,ES6(ECMAScript 2015)引入了 Promise 和异步函数(async/await)这两个重要的异步编程机制

Promise

Promise 是一种处理异步操作的封装方式,它提供了更结构化的代码风格,以便管理和处理异步任务的状态(进行中、成功、失败)。Promise 有三种状态:待定(pending)、兑现(fulfilled/resolve)和拒绝(rejected)。一个 Promise 表示一个可能会在未来完成或失败的操作。
新建一个 Promise 对象需要使用 Promise 构造函数,它接受一个函数作为参数(也就是回调函数),该函数是同步的并且会被立即执行,所以我们称之为起始函数。起始函数包含两个参数 resolvereject。起始函数执行成功时,它应该调用 resolve 函数并传递成功的结果给之后的 then。当起始函数执行失败时,它应该调用 reject 函数并传递失败的原因给之后的catch
Promise 构造函数返回一个 Promise 对象,该对象具有以下几个方法:
  • then:用于处理 Promise 成功状态的回调函数。
  • catch:用于处理 Promise 失败状态的回调函数。
  • finally:无论 Promise 是成功还是失败,都会执行的回调函数。
// Promise构造函数定义promise对象,其中的两个参数(resolve, reject)其实是两个函数 const promise = new Promise((resolve, reject) => { // 异步操作 setTimeout(() => { if (Math.random() < 0.5) { resolve('success'); // 符合条件,执行resolve函数并设置一个参数,在之后将参数传递给then方法作为参数 } else { reject('error'); // 不符合条件,执行reject函数并设置一个参数,在之后将参数传递给catch方法作为参数 } }, 1000); }); // 用返回的promise对象调用方法 promise.then(result => { // 这里调用了then方法,参数result的值就是上面resolve函数中的参数 console.log(result); }).catch(error => { // 这里调用了catch方法,参数error的值就是上面reject函数中的参数 console.log(error); }); // 输出 error 或 success
new Promise(function (resolve, reject) { var a = 0; var b = 1; if (b == 0) reject("Divide zero"); else resolve(a / b); }).then(function (value) { console.log("a / b = " + value); }).catch(function (err) { console.log(err); }).finally(function () { console.log("End"); }); // a / b = 0 // End
其他小问题:
  • 在构造Promise对象时,在执行体中必须执行resolve或reject中的一个,否则 Promise 将一直保持在待定(pending)状态,而不会执行 .then() 或 .catch(),也不会触发任何后续的异步操作。
  • 除了 then 块以外,其它两种块虽然也能多次使用,但是最好只安排一个 catch 和 finally 块。(catch 块只会执行第一个)
  • then 块默认会向下顺序执行,return 是不能中断的,可以通过 throw 来跳转至 catch 实现中断
    • new Promise(function (resolve, reject) { console.log(1111); resolve(2222); }).then(function (value) { console.log(value); throw "An error"; // throw跳转至catch,终端程序 }).then(function (value) { console.log(value); return 3333; }).catch(function (err) { console.log(err); }); /* 1111 2222 An error */
  • 当需要多次顺序执行异步操作的时候,适合用 Promise 而不是传统回调函数。例如,如果想通过异步方法先后检测用户名和密码,需要先异步检测用户名,然后再异步检测密码的情况下就很适合 Promise。

异步函数

异步函数是一个更高级的异步编程方式,它建立在 Promise 基础上。
异步函数是一个使用 async 关键字声明的函数,可以包含一个或多个使用 await 关键字等待解决的 Promise。
异步函数中使用 await 指令,其后必须跟着一个 Promise,异步函数会在这个 Promise 运行中暂停,直到其运行结束再继续运行。
异步函数中使用 try-catch 实现处理异常
async function asyncFunc() { try { await new Promise(function (resolve, reject) { throw "Some error"; // 或者 reject("Some error") }); } catch (err) { console.log(err); // 会输出 Some error } } asyncFunc();
如果 Promise 有一个正常的返回值,await 语句也会返回它
async function asyncFunc() { let value = await new Promise( function (resolve, reject) { resolve("Return value"); } ); console.log(value); } asyncFunc();

回调函数、Promise、异步函数比较

  • 回调函数
    • setTimeout(function () { console.log("First"); setTimeout(function () { console.log("Second"); setTimeout(function () { console.log("Third"); }, 3000); }, 4000); }, 1000);
  • Promise
    • function print(delay, message) { return new Promise(function (resolve, reject) { // 返回一个Promise对象 setTimeout(function () { console.log(message); resolve(); }, delay); }); } print(1000, "First").then(function () { // 返回的Promise对象直接调用then方法 return print(4000, "Second"); }).then(function () { print(3000, "Third"); });
  • 异步函数
    • function print(delay, message) { return new Promise(function (resolve, reject) { // 返回一个Promise对象 setTimeout(function () { console.log(message); resolve(); }, delay); }); } async function asyncFunc() { await print(1000, "First"); // 等回调函数运行完之后再继续运行后面的代码 await print(4000, "Second"); await print(3000, "Third"); } asyncFunc();

Ajax

AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
Ajax的一般步骤如下:
1、创建对象
XMLHttpRequest 是 AJAX 的基础,用于在后台与服务器交换数据。在开始之前需要先创建一个 XMLHttpRequest 对象
variable=new XMLHttpRequest(); // IE6, IE5 浏览器只支持ActiveXObject对象 if (window.XMLHttpRequest) { // IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码 xmlhttp=new XMLHttpRequest(); } else { // IE6, IE5 浏览器执行代码 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }
2、发送请求
将请求发送到服务器,需要使用 XMLHttpRequest 对象的 open() 和 send() 方法
方法
描述
xmlhttp.open(method,url,async)
规定请求的类型、URL 以及是否异步处理请求。method:请求的类型;GET 或 POSTurl:文件在服务器上的位置async:true(异步)或 false(同步)
xmlhttp.send(string)
将请求发送到服务器。 • string:仅用于 POST 请求
💡
请求类型GET 和 POST 的区别,该如何选择?
与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用
然而,在以下情况中,请使用 POST 请求:
  • 不愿使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST 没有数据量限制)
  • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
GET请求有时候可能会获取到缓存数据,可以向 URL 添加一个唯一的 ID
xmlhttp.open("GET","/try/ajax/demo_get.php?t=" + Math.random(),true); xmlhttp.send();
可以通过 GET 方法发送信息,请向 URL 添加信息
xmlhttp.open("GET","/try/ajax/demo_get2.php?fname=Henry&lname=Ford",true); xmlhttp.send(); // Henry Ford
 
3、处理请求
当请求被发送到服务器时,服务器需要对请求进行处理,这中间会触发 onreadystatechange 事件
下面是 XMLHttpRequest 对象的三个重要的属性:
属性
描述
onreadystatechange
存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
readyState
存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。 0:请求未初始化(还没有调用 open())。 1:请求已经建立,但是还没有发送(还没有调用 send())。 2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。 3:请求在处理中;通常响应中已有部分数据可用了,但是服务器还没有完成响应的生成。 4:响应已完成;您可以获取并使用服务器的响应了。
status
1xx:信息响应类,表示接收到请求并且继续处理 2xx:处理成功响应类,表示动作被成功接收、理解和接受 3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理 4xx:客户端错误,客户请求包含语法错误或者是不能正确执行 5xx:服务端错误,服务器不能正确执行一个正确的请求
💡
状态码查询
status
100——客户必须继续发出请求 101——客户要求服务器根据请求转换HTTP协议版本 200——交易成功 201——提示知道新文件的URL 202——接受和处理、但处理未完成 203——返回信息不确定或不完整 204——请求收到,但返回信息为空 205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件 206——服务器已经完成了部分用户的GET请求 300——请求的资源可在多处得到 301——删除请求数据 302——在其他地址发现了请求数据 303——建议客户访问其他URL或访问方式 304——客户端已经执行了GET,但文件未变化 305——请求的资源必须从服务器指定的地址得到 306——前一版本HTTP中使用的代码,现行版本中不再使用 307——申明请求的资源临时性删除 400——错误请求,如语法错误 401——请求授权失败 402——保留有效ChargeTo头响应 403——请求不允许 404——没有发现文件、查询或URl 405——用户在Request-Line字段定义的方法不允许 406——根据用户发送的Accept拖,请求资源不可访问 407——类似401,用户必须首先在代理服务器上得到授权 408——客户端没有在用户指定的饿时间内完成请求 409——对当前资源状态,请求不能完成 410——服务器上不再有此资源且无进一步的参考地址 411——服务器拒绝用户定义的Content-Length属性请求 412——一个或多个请求头字段在当前请求中错误 413——请求的资源大于服务器允许的大小 414——请求的资源URL长于服务器允许的长度 415——请求资源不支持请求项目格式 416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If-Range请求头字段 417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求 500——服务器产生内部错误 501——服务器不支持请求的函数 502——服务器暂时不可用,有时是为了防止发生系统过载 503——服务器过载或暂停维修 504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长 505——服务器不支持或拒绝支请求头中指定的HTTP版本
每当 readyState 改变时,就会触发 onreadystatechange 事件。如果一次请求成功,则 readyState == 4 并且 status==200, 此时 onreadystatechange 事件被触发 4 次(0 - 4), 分别是: 0-1、1-2、2-3、3-4
常规的onreadystatechange事件
// xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("myDiv").innerHTML=xmlhttp.responseText; } }
回调函数处理多个Ajax请求
var xmlhttp; function loadXMLDoc(url,cfunc) { if (window.XMLHttpRequest) {// IE7+, Firefox, Chrome, Opera, Safari 代码 xmlhttp=new XMLHttpRequest(); } else {// IE6, IE5 代码 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange=cfunc; xmlhttp.open("GET",url,true); xmlhttp.send(); } function myFunction() { loadXMLDoc("/try/ajax/ajax_info.txt",function() // 回调函数,每次调用可以更改请求地址和任务处理方式 { if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("myDiv").innerHTML=xmlhttp.responseText; } }); }
4、接收响应
接收来自服务器的响应,需要使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性。
responseText 获得字符串形式的响应数据
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
responseXML 获得 XML 形式的响应数据,而且需要作为 XML 对象进行解析
xmlDoc=xmlhttp.responseXML; txt=""; x=xmlDoc.getElementsByTagName("ARTIST"); for (i=0;i<x.length;i++) { txt=txt + x[i].childNodes[0].nodeValue + "<br>"; } document.getElementById("myDiv").innerHTML=txt;
5、不同数据实例
数据可能来自ASP、PHP、XML、数据库、JSON等,其中XML需要特别解析,详情参阅 AJAX XML 实例

DOM

DOM(Document Object Model,文档对象模型)是一种用于表示和操作 HTML、XML 等文档结构的编程接口。它将文档表示为一个树状结构,其中每个节点都表示文档中的一个元素、属性、文本等。JavaScript 可以通过操作 DOM 来修改网页的内容、结构和样式,从而实现动态的交互和页面更新。

DOM 树结构:

DOM 树是由节点组成的层次结构,其中每个节点都代表文档中的一个元素、属性、文本或注释。DOM 树的顶层节点是文档节点(document),然后是根元素节点(如<html>),接着是子元素节点、文本节点等。
notion imagenotion image
 

DOM 操作:

1、定位元素
  • 通过 id 定位 HTML 元素
    • var x=document.getElementById("intro");
  • 通过标签定位 HTML 元素
    • var y=x.getElementsByTagName("p"); // 该方法返回一个 HTMLCollection 对象,类似一个数组(但不能用数组的方法),需要用索引去访问其中的元素 var myCollection = document.getElementsByTagName("p"); myCollection[0] // 索引取值 myCollection.length; // 获取对象内元素的个数
  • 通过类名定位 HTML 元素
    • var z=document.getElementsByClassName("intro");
getElementsByTagName 和 getElementsByClassName 这两个方法查找多个 dom 元素,返回的是 htmlcollection 类型,是伪数组而不是真数组,故不能使用数组的方法。可以使用数组原型配合 slice 方法,利用 call,apply,bind 方法将伪数组转为真数组。
var x=document.getElementById("main"); var y=x.getElementsByTagName("p"); console.log(y)//在控制台我们可以看到原型proto为htmlcollection,是伪数组 //伪数组转为真数组方法1 console.log(Array.prototype.slice.call(y))//在控制台我们可以看到原型proto为Array(0),是真数组 //伪数组转为真数组方法2 console.log(Array.prototype.slice.apply(y))//在控制台我们可以看到原型proto为Array(0),是真数组 //伪数组转为真数组方法3 console.log(Array.prototype.slice.bind(y)())//在控制台我们可以看到原型proto为Array(0),是真数组
2、修改内容
  • 修改HTML输出流(HTML文档中)
    • document.write(Date()); // 绝对不要在文档(DOM)加载完成之后使用 document.write()。这会覆盖该文档。
  • 修改HTML元素内容
    • document.getElementById("p1").innerHTML="新文本!";
  • 修改HTML元素属性
    • // document.getElementById(id).attribute=新属性值 document.getElementById("image").src="landscape.jpg"; // 修改src属性
  • 修改HTML元素样式
    • // document.getElementById(id).style.property=新样式 document.getElementById("p2").style.color="blue"; document.getElementById("p2").style.fontFamily="Arial"; document.getElementById("p2").style.fontSize="larger";
3、响应事件
在事件发生时执行 JavaScript,比如当用户在 HTML 元素上点击时
  • 常见的HTML事件详见这里
  • 常规事件响应
    • <!DOCTYPE html> <html> <head> <script> function changetext(id) { id.innerHTML="Ooops!"; } </script> </head> <body> <h1 onclick="changetext(this)">点击文本!</h1> <!-- 这里的this是指代<h1>这个元素 --> </body> </html>
  • 向元素分配事件
    • document.getElementById("myBtn").onclick=function(){displayDate()};
  • 监听事件
    • 允许向同一个元素添加多个相同或不同类型的监听事件,且不会覆盖已存在的事件
    • // 向指定元素添加事件句柄 element.addEventListener(event, function, useCapture); /* 第一个参数是事件的类型 (如 "click" 或 "mousedown"). 第二个参数是事件触发后调用的函数。 第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。默认值为false, 即冒泡传递 在 冒泡 中,内部元素的事件会先被触发,然后再触发外部元素的事件 在 捕获 中,外部元素的事件会先被触发,然后再触发内部元素的事件 */ // 移除事件的监听 element.removeEventListener("mousemove", myFunction); // 点击监听 document.getElementById("myBtn").addEventListener("click", displayDate); // 重置窗口大小事件监听 window.addEventListener("resize", function(){document.getElementById("demo").innerHTML = sometext;});
4、修改元素
<div id="div1"> <p id="p1">这是一个段落。</p> <p id="p2">这是另外一个段落。</p> </div> <script> var para = document.createElement("p"); <!-- 创建一个p标签 --> var node = document.createTextNode("这是一个新的段落。"); <!-- 创建p标签的内容 --> para.appendChild(node); <!-- 把内容添加到p标签内 --> var element = document.getElementById("div1"); <!-- 定位HTML中已经存在的div标签 --> element.appendChild(para); <!-- 把新建的p标签添加到定位到的div标签内 的最后 --> element.insertBefore(para, child); <!-- 把新建的p标签添加到定位到的div标签内 的最前 --> var child = document.getElementById("p1"); element.removeChild(child); <!-- 移除div标签内的子元素 --> element.replaceChild(para, child); <!-- 替换div标签内的子元素 --> </script>
 
报错记录
1、Module not found: Can't resolve 'dns'
报错信息:
../../../../node_modules/cacheable-lookup/source/index.js:10:2 Module not found: Can't resolve 'dns' https://nextjs.org/docs/messages/module-not-found Import trace for requested module: ../../../../node_modules/got/dist/source/core/index.js ../../../../node_modules/got/dist/source/create.js ../../../../node_modules/got/dist/source/index.js ../../../../node_modules/notion-client/build/index.js ./pages/index.js
报错描述:
代码中的两行注释之后就报错,不注释运行就没问题(不注释,则前一个打印也能运行成功(console.log('这是第一个铁汁!!!!!!!!!!!!!!!',database[0].properties);)注释之后前一个打印也无效)
// const page = await api.getPage('baed2e48-30ad-46b0-aef9-a661e25194b7'); // console.log('这是第一个文章!!!!!!!!!!!!!!!',page);
全部代码:
const api = new NotionAPI(); import Link from "next/link"; import { getDatabase } from "../lib/notion"; // import { getDatabase, getPage, getBlocks } from "../lib/notion"; // import { Fragment } from "react"; import { NotionAPI } from 'notion-client'; import styles from '../styles/Home.module.css'; import { Text } from '../components/text'; // import PostList from "../components/postlist"; export const databaseId = process.env.NEXT_PUBLIC_NOTION_DATABASE_ID // export default function Home({ posts }) { // console.log(posts) // return ( // // <Fragment> // // {posts.map((post) => { // // <PostList items={post} /> // // })} // // </Fragment> // <div> // <h1>what fuck</h1> // </div> // ); // } export default function Home({ posts }) { // console.log(posts) return ( <ol className={styles.posts}> { posts.map((post) => ( <li key={post.id}> <Link href={`/${post.id}`}> {/* Text 需要的参数是 title 这个列表 */} <Text text={post.properties.title.title.slice(-1)} /> {/* {post.properties.title.title.slice(-1)[0]?.plain_text} */} </Link> </li> ))} </ol> ); } export const getStaticProps = async () => { const database = await getDatabase(databaseId); const database_ = database.slice(0,5); console.log('这是第一个铁汁!!!!!!!!!!!!!!!',database[0].properties); // .title.title.slice(-1)[0]?.plain_text // const page = await getPage('baed2e48-30ad-46b0-aef9-a661e25194b7'); // console.log('这是第一个文章!!!!!!!!!!!!!!!',page); // const blocks = await getBlocks('baed2e48-30ad-46b0-aef9-a661e25194b7'); // const blocks_ = await getBlocks(blocks.id); // console.log('这是第一个文章的块!!!!!!!!!!!!!!!',blocks_); // notion-client 库的使用方法 // const page = await api.getPage('baed2e48-30ad-46b0-aef9-a661e25194b7'); // console.log('这是第一个文章!!!!!!!!!!!!!!!',page); return { props: { posts: database_, }, // Next.js will attempt to re-generate the page: // - When a request comes in // - At most once every second revalidate: 5, // In seconds }; };
错误原因和解决方法
  • 错误原因:未知;由notion-client模块引起;
    • ChatGPT找到的原因
      报错信息指出找不到名为 'dns' 的模块。这个错误通常发生在 Node.js 环境中,其中某些模块试图引用浏览器环境的特定模块。
      从代码中可以看到,错误发生在 notion-client 模块的引入处。根据错误消息的导入跟踪,我们可以看到 notion-client 模块内部使用了 got 模块,而 got 模块又使用了 dns 模块。
      通过解除注释这两行代码,你实际上在尝试访问 notion-client 模块中的相关功能。这可能触发了模块的加载和初始化过程,其中涉及到了与报错信息中提到的模块的解决方案相关的模块加载。
      由于你之前没有使用 api.getPage 方法,这两行代码可能触发了一些依赖项的加载和初始化,其中包括了与 dns 模块相关的替代模块。这可能解决了报错信息中的问题,并使你的代码能够正常运行。
  • 解决方法:注释或删除这行代码:import { NotionAPI } from 'notion-client';
对于本文内容有任何疑问, 可与我联系.