1234 lines
30 KiB
Markdown
Executable File
1234 lines
30 KiB
Markdown
Executable File
# 1-js
|
||
|
||
JavaScript 的组成:
|
||
|
||
* ECMAScript: 核心, 语言规范
|
||
* DOM: 文档对象模型
|
||
* BOM: 浏览器对象模型
|
||
|
||
## 数据类型
|
||
|
||
* 基本数据类型: string number Boolean null undefined symble bigInt
|
||
* 复杂数据类型: array object function date regexp set map Promise
|
||
|
||
null 和 undefined
|
||
|
||
* null 表示一个值被定义了,定义为"空值"
|
||
* undefined 表示根本不存在定义
|
||
|
||
### 基本数据类型是按值传递
|
||
|
||
> 传递的是===副本===, 不会修改原始数据
|
||
|
||
```js
|
||
function modifyValue(x) {
|
||
x = 10; // 修改副本
|
||
console.log(x); // 10
|
||
}
|
||
|
||
let a = 5;
|
||
modifyValue(a);
|
||
console.log(a); // 5,原始值未改变
|
||
```
|
||
|
||
* 函数中不会修改传递进来的基本类型
|
||
* 需要修改, 要么全局变量
|
||
* 函数返回修改后值, 调用函数重新赋值
|
||
* 用对象/数组包装
|
||
|
||
### 复杂数据类型是按共享传递
|
||
|
||
> 传递的是内存地址的===副本===
|
||
> 如果是修改对象的属性, 会影响原始数据
|
||
> 如果是赋值, 不会影响原始数据
|
||
|
||
* 修改属性
|
||
|
||
```js
|
||
function modifyProperty(obj) {
|
||
obj.value = 10; // 修改对象的属性
|
||
console.log(obj.value); // 10
|
||
}
|
||
|
||
let myObj = { value: 5 };
|
||
modifyProperty(myObj);
|
||
console.log(myObj.value); // 10,原始对象被修改
|
||
|
||
```
|
||
|
||
* 重新赋值
|
||
|
||
```js
|
||
function reassignReference(obj) {
|
||
obj = { value: 10 }; // 重新赋值引用副本
|
||
console.log(obj.value); // 10
|
||
}
|
||
|
||
let myObj = { value: 5 };
|
||
reassignReference(myObj);
|
||
console.log(myObj.value); // 5,原始对象未被修改
|
||
|
||
```
|
||
|
||
## 栈, 堆
|
||
|
||
**栈**(stack)中主要存放一些**基本类型的变量和对象的引用**, 其优势是存取速度比堆要快,但缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性,**后进先出**, 就像盘子
|
||
|
||
**堆**(heap )多用于复杂数据类型(**引用类型**)分配空间,例如数组对象、object 对象;它是运行时动态分配内存的,因此存取速度较慢。就像仓库, 需要索引
|
||
|
||
<img src="https://img.081024.xyz/20230321033721.png" alt="image-20200713144353620" />
|
||
|
||
## 分支语法
|
||
|
||
### if else
|
||
|
||
### switch-case
|
||
|
||
```js
|
||
switch (expression) {
|
||
case value1:
|
||
// 当 expression 的结果与 value1 匹配时,执行此处语句
|
||
[break;]
|
||
case value2:
|
||
// 当 expression 的结果与 value2 匹配时,执行此处语句
|
||
[break;]
|
||
...
|
||
case valueN:
|
||
// 当 expression 的结果与 valueN 匹配时,执行此处语句
|
||
[break;]
|
||
[default:
|
||
// 如果 expression 与上面的 value 值都不匹配,执行此处语句
|
||
[break;]]
|
||
}
|
||
```
|
||
|
||
* break 作用:结束该 switch 语句,所以一般情况下要加上,如果不加上则会发生穿透
|
||
|
||
* 穿透:从上一个 case 代码快执行到下一个 case 代码快
|
||
|
||
* 使用场景: 多中值执行同个代码块, 例如:
|
||
|
||
```js
|
||
case 1:
|
||
case 2:
|
||
console.log("1或2都打印")
|
||
break
|
||
```
|
||
|
||
### 三元表达式
|
||
|
||
```js
|
||
// 如果表达式成立则执行代码1,否则执行代码2
|
||
表达式?代码1:代码2
|
||
```
|
||
|
||
* 一元运算符:只能操作一个值 `++` `--` `!`
|
||
* 二元运算符:操作两个值 `1 + 1` `1 > 0`
|
||
* 三元运算符:操作三个值
|
||
|
||
## 循环语法
|
||
|
||
### for
|
||
|
||
```js
|
||
for(i; i<10; i++){
|
||
|
||
}
|
||
```
|
||
|
||
### while
|
||
|
||
```js
|
||
while(条件 true/false){
|
||
循环体/需要重复执行的代码;
|
||
}
|
||
```
|
||
|
||
### do-while
|
||
|
||
```js
|
||
do{
|
||
循环体
|
||
}while(条件)
|
||
```
|
||
|
||
* do-while 和 while 实现的循环其实是一样的,只有一个不同点:do-while 循环不管怎样先执行一次循环体代码,然后再判断条件
|
||
|
||
## 创建对象的三种方式
|
||
|
||
### 字面量
|
||
|
||
> 直接赋值
|
||
|
||
```js
|
||
const obj = {name:"悟空",height:100,age:5000}
|
||
```
|
||
|
||
* 简单粗暴
|
||
* 不适合创建多个同样类型的对象的场景
|
||
|
||
### 工厂函数
|
||
|
||
> 函数返回
|
||
|
||
```js
|
||
function createPerson(name, age, height) {
|
||
return {
|
||
name: name,
|
||
age: age,
|
||
height: height
|
||
}
|
||
}
|
||
```
|
||
|
||
1. 容易理解
|
||
2. 失去`血缘关系`,无法简单分辨对象的特征
|
||
|
||
### 构造函数
|
||
|
||
> new
|
||
|
||
```markdown
|
||
构造函数的工作原理:
|
||
1. 开辟内存空间(堆)
|
||
2. 产生内部对象:this
|
||
3. 初始化属性(执行构造函数)
|
||
4. 返回对象的内存地址
|
||
```
|
||
|
||
```js
|
||
// 1 声明函数
|
||
function CreateStudent(name, age) {
|
||
// 2 通过 this 赋值
|
||
this.name = name;
|
||
this.age = age;
|
||
}
|
||
|
||
// 3 通过 new 来创建对象
|
||
const obj = new CreateStudent("悟能", 83);
|
||
|
||
console.log(obj);
|
||
```
|
||
|
||
优点:
|
||
|
||
1. 可以方便的创建对象
|
||
2. 拥有血缘关系
|
||
3. 还有后续更多的优势
|
||
|
||
缺点:
|
||
|
||
1. 可能会浪费内存空间:在构造函数中给属性增加方法,会导致每个对象都会保存一份方法
|
||
|
||
解决: 将方法单独存储一份,让对象中的属性指向方法
|
||
|
||
```js
|
||
// 但造成了污染全局变量的问题
|
||
function myLog(){
|
||
console.log(1)
|
||
}
|
||
|
||
function Test(){
|
||
this.log= myLog
|
||
}
|
||
|
||
```
|
||
|
||
构造函数的本质就是函数, 但与普通函数不同:
|
||
|
||
1. 构造函数名字是大驼峰法(**首字母大写**)
|
||
2. 会在函数体中直接使用this(this就代表对象自己)
|
||
3. 构造函数通常不需要给返回值:默认有返回值,是一个对象
|
||
|
||
## 定义函数的三种方式
|
||
|
||
### 声明式
|
||
|
||
> function 函数名 (){}
|
||
|
||
```js
|
||
fn()
|
||
function fn(参数..){
|
||
console.log("这是函数声明")
|
||
return 返回值
|
||
}
|
||
```
|
||
|
||
* ==函数声明可以先调用,再声明==
|
||
|
||
### 表达式
|
||
|
||
> const 函数名=function (){}
|
||
|
||
```js
|
||
const fn = function() {
|
||
console.log("这是函数表达式")
|
||
}
|
||
fn()
|
||
```
|
||
|
||
* ==函数表达式必须先声明,再调用==
|
||
|
||
### 构造函数
|
||
|
||
> new Function ()
|
||
|
||
```js
|
||
// Function是系统内置的一个构造函数
|
||
// 创建函数:new Function(动态参数,都需要使用引号,最后一个参数代表函数体)
|
||
```
|
||
|
||
```js
|
||
const fn1 = new Function("a1", "a2", "alert(a1+a2)")
|
||
fn1(1,2)
|
||
```
|
||
|
||
## 高阶函数
|
||
|
||
### arguments 关键字
|
||
|
||
> 在函数内部使用, 获取所有的实参
|
||
|
||
```js
|
||
fucntion add(){
|
||
let sum = 0
|
||
for (let i = 0; i < arguments.length; i++){
|
||
let value = arguments[i]
|
||
if(isNaN(value)){
|
||
return false
|
||
}
|
||
sum += Number(value)
|
||
}
|
||
return sum
|
||
}
|
||
```
|
||
|
||
## 原型 prototype
|
||
|
||
> 当函数定义完毕,系统就会自动创建原型
|
||
|
||
* 原型本质是一个对象,理解为 `定义构造函数` 的时候, `JavaScript` 自动帮我们添加的
|
||
* 所有构造函数的实例,共享一个原型
|
||
* 原型上一般用来挂载函数
|
||
|
||
### 实例的`__proto__`属性
|
||
|
||
* 实例的 `__proto__` 属性 等于 构造函数的 `prototype`
|
||
* 实例的 `__proto__` 是非标准属性, 只是为了方便我们开发的时候查看数据
|
||
|
||
实际用来获取原型的方法
|
||
|
||
```js
|
||
const obj = {}
|
||
const proto = Object.getPrototypeOf(obj)
|
||
```
|
||
|
||
### 原型链
|
||
|
||
* ==所有的构造函数都是Function的实例==
|
||
* Object的顶端是 null `console.log(Object.prototype.__proto__ === null)`
|
||
* student 实例的 constructor 属性是从它的原型继承来的
|
||

|
||
|
||
### 使用
|
||
|
||
把属性或者方法挂到构造函数的原型上, 使得所有实例都能共享该属性或方法
|
||
|
||
```js
|
||
function CreateStu(name,age){
|
||
this.name=name
|
||
this.age=age
|
||
}
|
||
|
||
const stu1 = new CreateStu('张三', 18)
|
||
|
||
Object.getPrototypeOf(stu1).say = function(){
|
||
console.log('hello')
|
||
}
|
||
|
||
/**
|
||
//或者
|
||
stu1.__proro__.say = function(){
|
||
console.log('hello')
|
||
}
|
||
|
||
// 或者
|
||
stu1.constructor.prototype.say = function(){
|
||
console.log('hello')
|
||
}
|
||
*/
|
||
|
||
const stu2 = new CreateStu('李四', 20)
|
||
stu2.say()
|
||
```
|
||
|
||
## 作用域
|
||
|
||
> 作用域有三种: 全局作用域global, 局部作用域local, 块级作用域block
|
||
|
||
* 全局作用域: 函数外部的作用域
|
||
* 全局变量: 可以在任意地方使用, 如果页面不关闭,那么变量所占用的内存就不会释放,就会占空间,消耗内存
|
||
* 局部作用域: 函数内部的作用域
|
||
* 局部变量:在函数内部使用
|
||
* 块级作用域: 使用一对大括号包裹的一段代码
|
||
* es6中新增 let const
|
||
* ==var声明的变量会产生变量提升,没有块的概念, 可以跨块访问, 不能跨函数访问==
|
||
* let 和 const定义的变量是有块级作用域的,只能在当前块作用域访问
|
||
|
||
## 函数的四种调用模式与this
|
||
|
||
> 根据函数内部this的指向不同,可以将函数的调用模式分成4种
|
||
|
||
1. 函数调用模式
|
||
2. 方法调用模式
|
||
3. 构造函数调用模式
|
||
4. 上下文调用模式(借用方法模式)
|
||
|
||
### 函数调用模式
|
||
|
||
如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的。此时this指向了window
|
||
|
||
```js
|
||
function fn(){
|
||
console.log(this) // 指向window
|
||
}
|
||
fn()
|
||
```
|
||
|
||
### 方法调用模式
|
||
|
||
当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到当前对象
|
||
|
||
```js
|
||
const obj = {
|
||
sayHi:function(){
|
||
console.log(this) //在方法调用模式中,this指向调用当前方法的对象。
|
||
}
|
||
}
|
||
obj.sayHi()
|
||
```
|
||
|
||
* 简写
|
||
|
||
```js
|
||
const obj = {
|
||
sayHi(){
|
||
console.log(this)
|
||
}
|
||
}
|
||
obj.sayHi()
|
||
```
|
||
|
||
### 构造函数调用模式
|
||
|
||
如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上
|
||
|
||
```js
|
||
function Person(){
|
||
console.log(this)
|
||
}
|
||
Person() //this指向window
|
||
let p = new Person() //this指向Person
|
||
```
|
||
|
||
### 方法借用模式/上下文模式
|
||
|
||
> apply, bind, call, 可以改变this
|
||
|
||
```js
|
||
/** apply 、bind、call 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
|
||
** apply 所有参数都必须放在一个数组里
|
||
** bind 参数用逗号隔开,返回是函数需要执行
|
||
** call 参数用逗号分隔
|
||
|
||
apply call 立刻调用, bind 返回函数可以延迟执行
|
||
**/
|
||
```
|
||
|
||
```js
|
||
const name = '小王', age=17
|
||
const obj = {
|
||
name: '小张',
|
||
objAge: this.age,
|
||
myFun: function(fm, t){
|
||
console.log(this.name + '年龄' + this.age, "来自" + fm + "去往" + t)
|
||
}
|
||
}
|
||
const db = {
|
||
name = '德玛',
|
||
age: 99
|
||
}
|
||
|
||
obj.myFun.apply(db,['成都','上海']); // 德玛 年龄 99 来自 成都去往上海
|
||
obj.myFun.bind(db,'成都','上海')(); // 德玛 年龄 99 来自 成都去往上海, bind返回的是个函数
|
||
obj.myFun.call(db,'成都','上海'); // 德玛 年龄 99 来自 成都去往上海
|
||
```
|
||
|
||
## this
|
||
|
||
* 单独使用,`this` 指向全局对象
|
||
|
||
```js
|
||
console.log(this) // window
|
||
```
|
||
|
||
* 在函数内部,`this` 的指向在函数定义的时候是不能确定的,**只有函数执行的时候才能确定**
|
||
* 谁调用它, 就指向谁
|
||
|
||
```js
|
||
function show(){
|
||
console.log(this) // window
|
||
}
|
||
show()
|
||
```
|
||
|
||
```js
|
||
const a = 18
|
||
const obj = {
|
||
a: 19,
|
||
b: {
|
||
a: 20,
|
||
c: function () {
|
||
console.log(this.a)
|
||
}
|
||
}
|
||
}
|
||
obj.b.c() // 20
|
||
```
|
||
|
||
* 在方法中,`this` 指代该调用方法的对象
|
||
|
||
```js
|
||
const obj ={
|
||
name:"小白",
|
||
say:function(){
|
||
console.log(this)
|
||
}
|
||
}
|
||
obj.say() // {name:"小白",say:f}
|
||
```
|
||
|
||
* 箭头函数: 箭头函数自己没有this, this向上级找
|
||
|
||
```js
|
||
//箭头函数没有funtion关键字, 所以里面没有this关键字
|
||
//浏览器断点调试,可以发现this是undefined
|
||
//箭头函数中非要用this, this向上级找
|
||
let obj = {
|
||
fn1: function () {
|
||
console.log('我是function函数输出的this: ', this)
|
||
},
|
||
fn2: () => console.log('我是箭头函数输出的this: ', this)
|
||
}
|
||
|
||
obj.fn1() //输出obj, function函数中, 谁调用函数, this指的就是谁
|
||
obj.fn2() //输出windows, 箭头函数不会产生this, 向上找, obj是个对象, 再向上就是window
|
||
```
|
||
|
||
* 借调函数不能改变箭头函数里面的this
|
||
* 要改变this, 可以通过改变它的上级
|
||
|
||
## 判断对象属于哪个构造函数
|
||
|
||
> instanceof, constructor, typeof
|
||
|
||
```js
|
||
let arr = [1, 2, 3]
|
||
console.log(arr instanceof Array) //true
|
||
```
|
||
|
||
```js
|
||
console.log(arr.constructor === Array) //true
|
||
//arr没有constructor, 就向上到它的原型找 arr.__proto__.constructor
|
||
```
|
||
|
||
```js
|
||
//typeof 可以找到基础数据类型, 数组,对象, 函数都不准确, 输出object
|
||
console.log(typeof (a)); //number
|
||
console.log(typeof (arr)); //object
|
||
```
|
||
|
||
## 对象的键使用变量
|
||
|
||
```js
|
||
//在键使用中括号的时候, 可以设为变量
|
||
const key = test
|
||
const obj = {[key]: 'a'}
|
||
console.log(obj) // {test: 'a'}
|
||
```
|
||
|
||
## 内置对象的方法
|
||
|
||
### Array
|
||
|
||
```js
|
||
let c = a.concat(b) // 连接两个或多个数组
|
||
|
||
let b = [1, 2, 3].join() // 拼接成字符串
|
||
|
||
a.shift() // 删除第一个元素, 返回第一个元素的值
|
||
|
||
a.unshift('the', 'start') // 往头部插入一个或多个, 返回新的长度
|
||
|
||
a.pop() // 删除最后一个元素, 返回被删除元素的值
|
||
|
||
a.push('the','end') // 往最后插入一个或多个, 返回新的长度
|
||
|
||
a.splice(index, howmany , item1 , ..... , itemX) // 删除或添加元素.
|
||
// 第一个参数是开始的位置
|
||
// 删除的数量, 不写则删除index到结尾, 可以是0
|
||
// 后面的是要插入的元素
|
||
// 如果删除了元素, 则返回的是含有被删除元素的数组
|
||
|
||
let b = a.slice(start, end) // 切片, 截取一部分, 浅拷贝
|
||
|
||
let value = a.find((value, index) => return value > 18) // 返回第一个满足条件的元素, 没有符合的返回undefined
|
||
|
||
let index = a.findIndex((value, index) => return value > 18) // 返回第应该满足条件的元素的下标, 没有符合的返回-1
|
||
|
||
let index = a.indexOf('test', 0) // 从头开始找, 是否能找到该元素, 找到返回第一次出现的下标, 没有返回-1. 第二个参数是开始找的位置, 默认是下标0可以不写
|
||
|
||
let index = a.lastIndexOf('test', a.length - 1) // 从结尾往前找, 是否能找到该元素, 找到返回第一次出现的下标, 没有返回-1. 第二个参数是开始找的位置, 默认是数组的长度减1可以不写
|
||
|
||
let boolean = a.some((value, index) => value > 18) // 满足条件返回true
|
||
|
||
a.sort() // 排序, 小到大
|
||
a.sort((a, b) => a - b) // 小到大
|
||
a.sort((a, b) => b - a) // 大到小
|
||
|
||
a.reverse() // 颠倒顺序
|
||
|
||
// 遍历
|
||
a.forEach((value, index) => {
|
||
console.log(`下标${index}, 值为${value}`)
|
||
})
|
||
|
||
// 过滤
|
||
let b = a.filter((value, index)=>{
|
||
return value > 0
|
||
})
|
||
|
||
// 映射
|
||
let b = a.map((value, index)=>{
|
||
return value + 1
|
||
})
|
||
```
|
||
|
||
### string 方法
|
||
|
||
```js
|
||
let len = str.length()
|
||
|
||
let newStr = str.replace('a', 'b') // 字符替换替换
|
||
|
||
let uStr = str.toUpperCase() // 全大写
|
||
|
||
let lStr = str.toLowerCase() // 全小写
|
||
|
||
let index = str.indexOf('a') // 某个字符首次出现的位置, 没有找到返回 -1
|
||
|
||
let arr = "a,b,c,d,e".split(","); // 转数组, 按逗号分隔
|
||
|
||
str.trim() // 移除两端空格
|
||
|
||
let str2 = str.substring(indexStart [, indexEnd]) // 截取部分
|
||
```
|
||
|
||
### math 方法
|
||
|
||
```js
|
||
Math.ceil(数字) // 向上取整
|
||
Math.floor(数字) // 向下取整
|
||
Math.max(数字1,数字2,...) // 求最大值
|
||
Math.min(数字1,数字2,...) // 求最小值
|
||
Math.random() // 返回 (0,1) 之间的小数
|
||
Math.abs(数字) // 绝对值
|
||
```
|
||
|
||
### regexp 方法
|
||
|
||
```js
|
||
// 创建
|
||
let re = new RegExp("\\w+");
|
||
let re = /\w+/;
|
||
|
||
let str="Is this all there is?";
|
||
let patt1=/is/gi; // 修饰符, g全局, i忽略大小写
|
||
|
||
part1.test(str) // test方法, 满足时返回true
|
||
```
|
||
|
||
## 编码解码
|
||
|
||
```js
|
||
encodeURI(URI) // 对整个 URI 进行编码, 不会编码 URI 中的保留字符(如 :、/、?、&、= 等)
|
||
decodeURI(encoded)
|
||
|
||
|
||
const uri = 'https://example.com/path?name=John Doe';
|
||
const encodedURI = encodeURI(uri);
|
||
console.log(encodedURI); // 输出: https://example.com/path?name=John%20Doe
|
||
```
|
||
|
||
```js
|
||
encodeURIComponent(str) // 对 URI 的组件(如查询参数)进行编码
|
||
decodeURIComponent(encodedURI)
|
||
|
||
const param = 'name=John Doe';
|
||
const encodedParam = encodeURIComponent(param);
|
||
console.log(encodedParam); // 输出: name%3DJohn%20Doe
|
||
```
|
||
|
||
## 闭包
|
||
|
||
> 函数能够记住并访问它被创建时的作用域,即使这个函数在作用域之外执行
|
||
|
||
```js
|
||
function outer() {
|
||
let x = 10; // outer 函数的局部变量
|
||
|
||
function inner() {
|
||
console.log(x); // inner 函数访问 outer 函数的局部变量
|
||
}
|
||
|
||
return inner;
|
||
}
|
||
|
||
const closureFunc = outer(); // outer 函数执行完毕,但 x 仍然被 inner 函数记住
|
||
closureFunc(); // 输出: 10
|
||
```
|
||
|
||
1. **函数**:闭包的核心是一个函数(如 `inner`)。
|
||
2. **作用域**:这个函数能够访问它被创建时的作用域(如 `outer` 函数中的 `x`)。
|
||
3. **记忆**:即使外部函数(如 `outer`)已经执行完毕,闭包仍然可以访问它的作用域。
|
||
|
||
闭包可以用于:
|
||
|
||
* 创建私有变量。
|
||
* 实现函数柯里化(Currying)。
|
||
* 柯里化: 一个函数 f(a, b, c) 转换为 f(a)(b)(c) 的形式
|
||
* 在异步编程中保留上下文。
|
||
|
||
缺点:
|
||
|
||
* 可能带来内存泄漏
|
||
* 额外的内存开销和性能损耗
|
||
|
||
解决:
|
||
|
||
* 及时释放 = null
|
||
|
||
使用:
|
||
|
||
* 防抖
|
||
* 下载任务
|
||
|
||
## reduce
|
||
|
||
会循环当前的数组, 侧重于 "滚雪球"
|
||
|
||
```js
|
||
// 语法
|
||
数组.reduce((val, item)=>{return ...}, 初始值)
|
||
// 把每次 函数体的结果, 返回到val里, 参与下次滚雪球
|
||
// 始始值只在第一次时使用
|
||
|
||
const arr = [1, 2, 3, 4, 5]
|
||
const total = arr.reduce((val, item)=>{return val + item}, 0)
|
||
console.log(total)
|
||
```
|
||
|
||
## DOM
|
||
|
||
### script 标签进阶
|
||
|
||
#### type=importmap 导入映射
|
||
|
||
```js
|
||
<script type="importmap">
|
||
{
|
||
"imports": {
|
||
"shapes": "./shapes/square.js",
|
||
"shapes/square": "./modules/shapes/square.js",
|
||
"https://example.com/shapes/square.js": "./shapes/square.js",
|
||
"https://example.com/shapes/": "/shapes/square/",
|
||
"../shapes/square": "./shapes/square.js"
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
|
||
* **`<script type="importmap">`**:定义了一个 import map,类型为 `importmap`。浏览器会解析这个 JSON 对象,并在后续的模块导入过程中使用这些映射。
|
||
* **`"imports"`**:这是 import map 的主要部分,用于定义模块的映射关系。
|
||
|
||
兼容性: chrome >= 89, firefox >=108, safari >=16.4
|
||
|
||
#### type=module
|
||
|
||
ES Module 格式
|
||
|
||
```html
|
||
<script type="module" src="main.js"></script>
|
||
```
|
||
|
||
```js
|
||
<script type="module">
|
||
import { greet } from './greet.js';
|
||
greet('World');
|
||
</script>
|
||
```
|
||
|
||
* 模块脚本默认是延迟执行的,类似于 `<script defer>`。它们会在 HTML 文档解析完成后按顺序执行,不会阻塞页面渲染。
|
||
* 如果需要动态加载模块,可以使用 import () 函数。它返回一个 Promise,可以在运行时加载模块。
|
||
|
||
```js
|
||
// 动态加载模块
|
||
if (condition) {
|
||
import('./greet.js').then((module) => {
|
||
module.greet('World');
|
||
});
|
||
}
|
||
```
|
||
|
||
### getElementBy与querySelector
|
||
|
||
`getElementBy` 方法返回的结果是 `HTMLCollection`,是动态的, 以后每一次在Javascript函数中使用这个变量的时候都会再去访问一下这个变量对应的html元素, 性能比较好
|
||
|
||
而 `querySelector` 方法返回的结果是 `NodeList`, 选出来的元素及元素数组是静态的,不会随着文档操作而改变, 使用比较方便
|
||
|
||
### 设置带-的样式
|
||
|
||
用 js 设置样式时, 如果样式的属性有 `-`, 则应该转换成大驼峰
|
||
|
||
### attribute 标签的属性
|
||
|
||
> 用 . 语法更加简单, 但无法删除属性名
|
||
|
||
* 获取属性:元素.getAttribute ( "id" )
|
||
* 设置属性:元素.setAttribute('属性名',属性值)
|
||
* 删除属性:元素.removeAttribute('属性名')
|
||
|
||
无法获取的属性:
|
||
|
||
* 行外属性
|
||
* 不属性标签标准属性的自定义属性
|
||
|
||
### 节点和元素
|
||
|
||
> **节点** 是 DOM 树中的基本单位,代表 DOM 中的任何对象, 可以是元素、文本、注释、属性等
|
||
> **元素** 是节点的一种特定类型,表示 HTML 或 XML 文档中的标签
|
||
|
||
* 节点
|
||
|
||
```js
|
||
.childNodes 子节点
|
||
.firstChild 第一个子节点
|
||
.lastChild 最后一个子节点
|
||
.nextSibling 下一个节点
|
||
.previousSibling 上一个节点
|
||
|
||
.parentNode 元素的父节点, 一定是一个元素, 只有元素才有子节点
|
||
```
|
||
|
||
* 元素
|
||
|
||
```js
|
||
.children 子元素, 是动态的 HTMLCollection
|
||
.firstElementChild 第一个子元素
|
||
.lastElementChild 最后一个子元素
|
||
.previousElementSibling 上一个兄弟元素
|
||
.nextElementSibling 下一个兄弟元素
|
||
```
|
||
|
||
#### 增删改
|
||
|
||
| 语法 | 示例 | 描述 |
|
||
| ------------------------ | ---------------------------- | ------------- |
|
||
| document.createElement() | document.createElement('li') | 创建空标签元素 |
|
||
| 父元素.appendChild(子元素) | ul.appendChild(li) | 添加子元素 |
|
||
| 父元素.insertBefore(子元素) | ul.insertBefore(元素A,元素B) | 将元素A插入到元素B的前面 |
|
||
| 父元素.replaceChild(子元素) | ul.replaceChild(元素A,元素B) | 元素A替换元素B |
|
||
| 父元素.removeChild(子元素) | ul.removeChild(li) | 移除子元素 |
|
||
|
||
#### 克隆
|
||
|
||
```js
|
||
元素.cloneNode(布尔)
|
||
false:默认,浅克隆
|
||
true:深克隆
|
||
```
|
||
|
||
### 事件的注册
|
||
|
||
事件的三要数: 事件源, 事件类型, 处理函数
|
||
|
||
```js
|
||
box.onclick = function () {
|
||
alert('单击事件')
|
||
};
|
||
```
|
||
|
||
常见的事件: onclick, dblclick, onmouseover, onmouseout
|
||
|
||
### 事件监听
|
||
|
||
| 特性 | 事件注册(`on` 属性) | 事件监听(`addEventListener`) |
|
||
| ------ | ------------------- | --------------------------- |
|
||
| 绑定方式 | 直接为 `on` 属性赋值 | 使用 `addEventListener` 方法 |
|
||
| 多个处理函数 | 不支持,后赋值的函数会覆盖之前的函数 | 支持,可以为同一个事件绑定多个处理函数 |
|
||
| 事件阶段 | 仅支持冒泡阶段 | 支持捕获和冒泡阶段(通过第三个参数控制) |
|
||
| 移除事件 | 将 `on` 属性赋值为 `null` | 使用 `removeEventListener` 方法 |
|
||
|
||
* 注册事件
|
||
|
||
```js
|
||
element.addEventListerner(type, fn [, option])
|
||
|
||
// 第三个参数如果是布尔值: 默认false, 冒泡阶段进行;true 捕获阶段进行
|
||
// 如果是对象
|
||
// `capture`(布尔值):是否在捕获阶段执行事件处理函数(默认 `false`)。
|
||
// `once`(布尔值):事件处理函数是否只执行一次(默认 `false`)。
|
||
// `passive`(布尔值):是否忽略事件处理函数中的 `preventDefault()`(默认 `false`)。改善滚屏性能
|
||
// `signal`(`AbortSignal`):用于移除事件监听器的信号。
|
||
```
|
||
|
||
冒泡: 从里往外传递, 直到 windows
|
||
|
||
* 当一个元素接收到事件的时候,会把他接收到的事件传给父级,一直传到window (注意这里传递的仅仅是事件并不传递所绑定的事件函数。所以如果父级没有绑定事件函数,就算传递了事件也不会有什么表现但事件确实传递了。)
|
||
* 利用: 事件委托, 在外层监听, 减少数量; 通过e.target拿到触发事件的元素
|
||
|
||
捕获: 从外往里传递
|
||
|
||
* 移除事件
|
||
|
||
```js
|
||
element.removeEventListener()
|
||
```
|
||
|
||
* 手动触发事件
|
||
|
||
```js
|
||
element.dispatchEvent(event)
|
||
```
|
||
|
||
* 阻止事件的默认行为/传播
|
||
|
||
```js
|
||
event.preventDefault(); // 阻止默认行为
|
||
event.stopPropagation(); // 阻止事件传播
|
||
```
|
||
|
||
### offset 获取真实高宽和位置
|
||
|
||
```js
|
||
/* offsetWidth与offsetHeight:获取的是元素的实际宽高 = width + border + padding
|
||
|
||
1.可以获取行内及内嵌的宽高
|
||
2.获取到的值是一个number类型,不带单位
|
||
3.获取的宽高包含border和padding
|
||
4.只能读取,不能设置
|
||
|
||
*/
|
||
|
||
element.offsetWidth
|
||
element.offsetHeight
|
||
```
|
||
|
||
```js
|
||
/* offsetParent:获取最近的定位父元素 (自己定位参照的父元素)
|
||
|
||
1.如果元素自身是固定定位,则定位父级是null
|
||
2.如果元素自身是非固定定位,并且所有的父元素都没有定位,那么他的定位父级是body
|
||
3.body的定位父级是null
|
||
|
||
*/
|
||
element.offsetParent
|
||
```
|
||
|
||
```js
|
||
/*
|
||
offsetLeft: 元素的 border 左边最外一条边 到其 最近定位父元素的 padding 左边一条边 的距离
|
||
offsetTop: 元素的 border 上边最外一条边 到其 最近定位父元素的 padding 上边一条边 的距离
|
||
*/
|
||
|
||
element.offsetLeft
|
||
element.offsetTop
|
||
```
|
||
|
||

|
||
|
||

|
||
|
||

|
||
|
||
### requestAnimationFrame
|
||
|
||
> 1. 屏幕刷新时(通常是60hz)更新动画, 优化性能
|
||
> 2. 运行在后台或者 `<iframe>` 会停止工作
|
||
|
||
```js
|
||
requestAnimationFrame(callback)
|
||
```
|
||
|
||
```js
|
||
// 使用 setTimeout 实现动画
|
||
function animateTimeout() {
|
||
// 动画逻辑
|
||
setTimeout(animateTimeout, 1000 / 60); // 手动设置帧率
|
||
}
|
||
animateTimeout();
|
||
|
||
// 使用 requestAnimationFrame 实现动画
|
||
function animateRAF() {
|
||
// 动画逻辑
|
||
requestAnimationFrame(animateRAF); // 自动优化帧率
|
||
}
|
||
animateRAF();
|
||
|
||
```
|
||
|
||
## BOM
|
||
|
||
### window 窗口对象
|
||
|
||
> 指当前浏览器窗口, js 中的顶级对象
|
||
|
||
* 常用方法
|
||
|
||
```js
|
||
// window可以省略
|
||
window.alert()
|
||
window.open()
|
||
window.close()
|
||
```
|
||
|
||
* 事件
|
||
|
||
```js
|
||
window.onload = fn // 界面上的内容加载完毕触发
|
||
window.onbeforeunload = fn // 界面关闭前触发
|
||
window.onunload = fn // 关闭的那一瞬间触发
|
||
```
|
||
|
||
### location 地址栏
|
||
|
||
> 包含 url 信息
|
||
|
||
* 常用的方法
|
||
|
||
```js
|
||
location.assign('你要打开的新网页的url')
|
||
location.replace('要替换的网页url')
|
||
location.reload()
|
||
```
|
||
|
||
### history 历史记录
|
||
|
||
```js
|
||
history.forward()
|
||
history.backup()
|
||
```
|
||
|
||
### navigator 浏览器信息
|
||
|
||
```js
|
||
console.log ( navigator );//navigator对象
|
||
console.log ( navigator.appVersion ); //当前浏览器版本信息
|
||
console.log ( navigator.platform ); //当前浏览器的硬件平台
|
||
console.log ( navigator.userAgent ); //当前浏览器信息
|
||
```
|
||
|
||
### screen 屏幕信息
|
||
|
||
```js
|
||
screen.width // 屏幕宽度
|
||
screen.height // 屏幕高度
|
||
|
||
screen.availWidth // 可用宽度
|
||
screen.availHeight // 可用高读
|
||
```
|
||
|
||
如果要获取视口的宽高 `window.innerWidth` `window.innerHeight`
|
||
|
||
## web apis
|
||
|
||
### web storage api
|
||
|
||
#### localStorage
|
||
|
||
> 容量通常为 5 mb
|
||
> sessionStorage 也是 5 mb
|
||
> cookies 4 k
|
||
|
||
```js
|
||
localStorage.setItem(key, value)
|
||
localStorage.getItem(key)
|
||
locclStorage.removeItem(key)
|
||
locclStorage.clear()
|
||
```
|
||
|
||
#### sessionStorage
|
||
|
||
* 页面会话结束时被清除
|
||
* 每个标签页都有独立的 `sessionStorage`, 即使他们的 url 相同
|
||
|
||
### indexDB
|
||
|
||
> 空间
|
||
|
||
* 异步操作, 不会阻塞主线程
|
||
* 键值存储, 支持复杂数据类型
|
||
* 事务支持: 事务更新是指将多个数据库操作(如插入、更新、删除)组合成一个逻辑单元,确保这些操作要么全部成功,要么全部失败
|
||
* 大容量:
|
||
* Safari: 1 G
|
||
* firefox: 2 G
|
||
* chrome: 单域名 60% 磁盘空间, 总 80%
|
||
|
||
#### 打开数据库
|
||
|
||
```js
|
||
const request = indexedDB.open("MyDatabase", 1);
|
||
|
||
request.onupgradeneeded = (event) => {
|
||
const db = event.target.result;
|
||
// 创建对象存储空间(类似于表)
|
||
if (!db.objectStoreNames.contains("users")) {
|
||
db.createObjectStore("users", { keyPath: "id" });
|
||
}
|
||
};
|
||
|
||
request.onsuccess = (event) => {
|
||
const db = event.target.result;
|
||
console.log("数据库打开成功");
|
||
};
|
||
|
||
request.onerror = (event) => {
|
||
console.error("数据库打开失败", event.target.error);
|
||
};
|
||
```
|
||
|
||
#### 添加数据
|
||
|
||
* 通过事务向对象存储空间添加数据
|
||
|
||
```js
|
||
const transaction = db.transaction("users", "readwrite");
|
||
const store = transaction.objectStore("users");
|
||
|
||
const user = { id: 1, name: "John", age: 25 };
|
||
const request = store.add(user);
|
||
|
||
request.onsuccess = () => {
|
||
console.log("数据添加成功");
|
||
};
|
||
|
||
request.onerror = (event) => {
|
||
console.error("数据添加失败", event.target.error);
|
||
};
|
||
|
||
```
|
||
|
||
#### 读取数据
|
||
|
||
```js
|
||
const transaction = db.transaction("users", "readonly");
|
||
const store = transaction.objectStore("users");
|
||
|
||
const request = store.get(1); // 根据主键读取数据
|
||
|
||
request.onsuccess = () => {
|
||
const user = request.result;
|
||
console.log("读取数据成功", user);
|
||
};
|
||
|
||
request.onerror = (event) => {
|
||
console.error("读取数据失败", event.target.error);
|
||
};
|
||
|
||
```
|
||
|
||
### 更新数据
|
||
|
||
```js
|
||
const transaction = db.transaction("users", "readwrite");
|
||
const store = transaction.objectStore("users");
|
||
|
||
const user = { id: 1, name: "John", age: 30 }; // 更新 age
|
||
const request = store.put(user);
|
||
|
||
request.onsuccess = () => {
|
||
console.log("数据更新成功");
|
||
};
|
||
|
||
request.onerror = (event) => {
|
||
console.error("数据更新失败", event.target.error);
|
||
};
|
||
```
|
||
|
||
#### 删除数据
|
||
|
||
```js
|
||
const transaction = db.transaction("users", "readwrite");
|
||
const store = transaction.objectStore("users");
|
||
|
||
const request = store.delete(1); // 根据主键删除数据
|
||
|
||
request.onsuccess = () => {
|
||
console.log("数据删除成功");
|
||
};
|
||
|
||
request.onerror = (event) => {
|
||
console.error("数据删除失败", event.target.error);
|
||
};
|
||
```
|
||
|
||
### fetch api
|
||
|
||
### websocket api
|
||
|
||
另建笔记
|
||
|
||
### URLSearchParams
|
||
|
||
https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams
|
||
|
||
URLSearchParams 是处理 URL 上的参数串的,主要处理形如 AAA=BBB&CCC=DDD 这部分内容,看个简单用法:
|
||
|
||
```js
|
||
let searchParam=new URLSearchParams('aaa=bbb&ccc=ddd')
|
||
searchParam.get('aaa');//结果:bbb
|
||
```
|
||
|
||
配合 location 的用法,一般是先取出 location. search:let searchParam=new URLSearchParams (location.search.substr (1))。
|
||
|
||
**主要用法**
|
||
|
||
has, get, getAll
|
||
|
||
```js
|
||
let searchParam=new URLSearchParams('aaa=bbb&ccc=ddd&aaa=kkk')searchParam.has('ccc');//truesearchParam.get('aaa');//bbbsearchParam.getAll('aaa');//[bbb,kkk]
|
||
```
|
||
|
||
toString, append, set, delete
|
||
|
||
```js
|
||
let searchParam=new URLSearchParams('aaa=bbb&ccc=ddd&aaa=kkk')searchParam.delete('aaa');//删除aaa的keysearchParam.toString();//ccc=dddsearchParam.append('eee','fff');//增加eee=fffsearchParam.toString();//ccc=ddd&eee=fffsearchParam.set('ccc','kkk');//设置ccc=kkksearchParam.toString();//ccc=kkk&eee=fff
|
||
```
|
||
|
||
entries, values, keys
|
||
|
||
```js
|
||
let searchParam=new URLSearchParams('aaa=bbb&ccc=ddd');[...searchParam.entries()];//[[aaa,bbb],[ccc,ddd]][...searchParam.values()];//[bbb,ddd][...searchParam.keys()];//[aaa,ccc]
|
||
```
|
||
|
||
## json的格式
|
||
|
||
除了对象格式, 还有数组格式, 布尔值, 数字, 字符串, null
|
||
|
||
```js
|
||
console.log(JSON.parse(JSON.stringify([1,2,3])))
|
||
console.log(JSON.parse(JSON.stringify(123)))
|
||
console.log(JSON.parse(JSON.stringify('abc')))
|
||
console.log(JSON.parse(JSON.stringify(null)))
|
||
```
|