Skip to content

TS

TypeScript 包含了 JavaScript 的所有内容,是 他的的超集。

TS编译

浏览器不能直接运⾏ TypeScript 代码,需要编译为 JavaScript 再交由浏览器解析器执⾏。

命令行编译

第⼀步:创建⼀个 demo.ts ⽂件

ts
const person = {
    name:'李四',
    age:18
}
console.log(`我叫${person.name},我今年${person.age}岁了`)

第二步:全局安装 TypeScript

ts
npm i typescript -g

第三步:使⽤命令编译 .ts ⽂件

ts
tsc demo.ts

自动化编译

第⼀步:创建 TypeScript 编译控制文件

会生成⼀个 tsconfig.json 配置文件,默认编译的 JS 版本是 ES7 ,可以手动调整。

tsc --init

第二步:监视目录中的 .ts 文件变化

ts
tsc --watch  或tsc -w

//当编译出错时不⽣成.js ⽂件
tsc --noEmitOnError --watch

基本语法

==与===

  • ==:比较时会进行类型转换,如果转换后值相等,则返回 true

    ts
    1 == '1'  // true,因为字符串 '1' 被转换成数字 1
    0 == false  // true,因为 false 被转换成数字 0
    null == undefined  // true,因为它们被认为是相等的
  • ===:比较时不进行类型转换,只有类型和值都相同时才返回 true

    ts
    1 === '1'  // false,因为一个是数字,一个是字符串
    0 === false  // false,因为一个是数字,一个是布尔值
    null === undefined  // false,因为它们是不同的类型

声明关键字

var

  • 作用域:函数作用域或全局作用域

    • 在函数内声明变量,则整个函数内任意位置均可访问

    • 在函数外声明变量,那么全局可访问。

  • 变量提升:var声明的变量会被提升到函数或全局作用域的顶部,但不会初始化(undefined)。

  • 重复声明:能在同一作用域内多次声明同一个变量

let、const

  • 作用域:块作用域(即在花括号 {} 内有效)。
  • 变量提升:let、const声明的变量也会被提升,但在声明之前不能访问(暂时性死区)。
  • 重复声明:let、const不能在同一作用域内多次声明同一个变量,且const声明时必须初始化,且不能重新赋值。
ts
// 一、作用域限制
{
    var a = 1;
    let b = 2;
    const c = 5;
}
// 可以访问a
console.log(a);
// 不可以访问b和c
console.log(b);
console.log(c);


// 二、重复声明
// var 可以声明多次
var m = 1
var m = 2
// let 只能声明一次,Identifier 'n' has already been declared
let n = 3
let n = 4

// 三、变量提示
// var 会变量提升,let 不存在变量提升
console.log(x); // undefined
var x = 10;
console.log(y); //ReferenceError: y is not defined
let y = 20;

// 四、const不可变性
// 声明之后不允许改变,且声明时必须初始化,否则会报错
const a = 1;
a = 3; //Uncaught TypeError: Assignment to constant variable.

JavaScript 类型

string、number、boolean、null、undefined、bigint、symbol、object(包含: Array 、 Function 、 Date 、 Error 等...... )

  • 原始类型:如 number 、 string ,它们在内存中占⽤空间少,处理速度快。
  • 包装对象:如 Number 对象、 String 对象,是复杂类型,在内存中占⽤更多空间。
ts
let str1: string
str1 = 'hello'
//报错,不能将包装对象赋值给原始类型
str1 = new String('hello')


//⾃动装箱:JavaScript 在必要时会⾃动将原始类型包装成对象,以便调⽤⽅法或访问属性
// 原始类型字符串
let str = 'hello';
// 当访问str.length时,JavaScript引擎做了以下⼯作:
let size = (function() {
    // 1. ⾃动装箱:创建⼀个临时的String对象包装原始字符串
    let tempStringObject = new String(str);
    // 2. 访问String对象的length属性
    let lengthValue = tempStringObject.length;
    // 3. 销毁临时对象,返回⻓度值
    // (JavaScript引擎⾃动处理对象销毁,开发者⽆感知)
    return lengthValue;
})();

string

js
let s = 'Hello world!';

s.indexOf('o')// 返回字符串出现下标
// 三个方法都支持第二个参数,表示开始搜索的位置
s.startsWith('Hello') // 返回布尔值,是否以什么开头
s.endsWith('!') // 返回布尔值,是否以什么结尾
s.includes('o') // 返回布尔值,是否包含字符串
s.repeat(n) // 返回s重复n次的字符串

s.trim()// 消除头尾空格
s.trimStart()// 消除字符串头部的空格
s.trimEnd()// 消除尾部的空格


// 字符串补全长度的功能:如果某个字符串不够指定长度,会在头部或尾部补全。
s.padStart(n, str)
s.padEnd(n, str)

Object

object

可以存储所有非原始类型,如对象、函数、数组等,不包括基本数据类型和null、undefined。

ts
let a:object //a的值可以是任何【⾮原始类型】,包括:对象、函数、数组等

// 不能为基本数据类型
a=1

// 不能为null或undefined
a = null
a = undefined
Object

除了 undefined 和 null 的任何值 ,包括基本数据类型

ts
let b:Object

// 可以为基本数据类型,会调用Object封装
b = 1 
b = true 
b = '你好'

// 以下代码均有警告
b = null
b = undefined
使用
js
// 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的属性值,Symbol属性会被忽略
const obj = { foo: 'd', 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj) // ["b", "c", "a", "d"]


// 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组,Symbol属性会被忽略
let obj = {
  [Symbol()]: 123,
  foo: 'abc',
  1: 'a',
  2: 'b'
}
Object.entries(obj);// [ ['1', 'a'], ['2', 'b'], [ 'foo', 'abc' ] ]


// Object.entries()的逆操作,用于将一个键值对数组转为对象
Object.fromEntries([
  [Symbol(), 'm'],
  ['1', 'a'],
  ['2', 'b'],
  ['foo', 'abc']
])
// {1: 'a', 2: 'b', foo: 'abc', Symbol(): 'm'}

Set

  • size:返回 Set 结构的成员总数

  • add(val):添加某个值,返回 Set 结构本身

  • delete(val):删除某个值,返回一个布尔值,表示删除是否成功

  • has(val):返回一个布尔值,表示该值是否为Set的成员

  • clear():清除所有成员,没有返回值

  • forEach((val, key, set) => {}):使用回调函数遍历每个成员,val和key相同,set是集合本身

  • keys():返回键名的遍历器

  • values():返回键值的遍历器

  • entries():返回键值对的遍历器

js
// set创建,可以传入多个参数
let s1 = new Set([4, 5, 6]);

// 遍历
//for (let item of set.keys()) 
//for (let item of set.values())
for (let item of set.values()){
  console.log(item);
  // red
  // green
  // blue
}
for (let item of set.entries()) {
  console.log(item);
  // ["red", "red"]
  // ["green", "green"]
  // ["blue", "blue"]
}


// 数组去重
let arr = [1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 6, 7];
arr = [...new Set(arr)]; // [1,2,3,4,5,6,7]


// 实现交、并、差
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 交集
let intersect = new Set([...a].filter(item => b.has(item)))

// 并集
let union = new Set([...a, ...b]);

// (a 相对于 b 的)差集
let difference = new Set([...a].filter(item => !b.has(item)))

WeakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别

  • WeakSet 的成员只能是对象,不能是其他类型的值

  • WeakSet 中的对象(非值类型)都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用, 可以用于储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏

  • add(val):添加某个值,返回 Set 结构本身

  • delete(val):删除某个值,返回一个布尔值,表示删除是否成功

  • has(val):返回一个布尔值,表示该值是否为Set的成员

Map

  • size:返回 Map 结构的成员总数
  • set(key,value):该方法设置键名key对应的键值为value,然后返回整个 Map 结构(可连续调用)。如果key已经有值,则键值会被更新,否则就新生成该键
  • get(key):该方法读取key对应的键值,如果找不到key,返回undefined
  • has(key):该方法返回一个布尔值,表示某个键是否在当前 Map 对象之中
  • delete(key):该方法删除某个键,返回true。如果删除失败,返回false
  • clear():该方法清除所有成员,没有返回值
  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回所有成员的遍历器
  • forEach():遍历 Map 的所有成员
ts
// 创建多个变量的map
let map = new Map([
  [1, 55],
  ['good', 123],
  [{}, 555]
])

// 链式调用存入变量
myMap.set(obj, { age: 18 }).set(sym, 456);

// Map 转 数组
let arr = [...map]; // [[1, 55], ['good, 123], [{}, 555]]

// 对象转为Map,可以通过Object.entries()
let obj = { a: 1, b: 2 };
let map = new Map(Object.entries(obj));

WeakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合。它与Map有两个区别

  • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名

  • WeakMap 中的对象(非值类型)都是弱引用,即垃圾回收机制不考虑 WeakMap 对该对象的引用。用于储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏

  • myweakMap.set(key, val):添加键值对,返回 Map 结构本身

  • myweakMap.has(key):返回一个布尔值,表示该值是否为 Map的成员

  • myweakMap.get(key):返回 key 对应的值

  • myweakMap.delete(key):删除 key 那个键值对

Typecript 类型

any、unknown、never、void、tuple、enum,以及两个⽤于⾃定义类型的⽅式:type、 interface

any

表示任意类型

ts

// 可以接受任意类型的变量
let a: any
a = 100
a = '你好'

//可以赋值给任意类型的变量
let c:any
c = 9
let x: string
x = c

//可以调用任意属性
let str2: any
str2.toUpperCase()

unknown

表示未知类型

ts
let a: unknown
// 可以接受任意类型的变量
a = 100
a = false

// 但是不能将类型“unknown”分配给其他类型
let x: string
x = a 

// 不能调用任意属性
let str3: unknown
str3.toUpperCase() 


// 强制分配和调用属性方式
// 第⼀种⽅式:加类型判断
if(typeof a === 'string'){
    x = a
    console.log(x)
}

//第⼆种⽅式:加断⾔
(str3 as string).toUpperCase()

//第三种⽅式:加断⾔
x = <string>a

never

不能有值,包括undefined 、 null

ts
let a: never
// 均无法接受
a = true
a = undefined

// 一般存在于以下两种情况:
// 情况一:TypeScript推断
let a: string
// 给a设置⼀个值
a = 'hello'
if (typeof a === 'string') {
	console.log(a.toUpperCase())
} else {
    // TypeScript会推断出此处的a是never,因为没有任何⼀个值符合此处的逻辑
	console.log(a) 
}

// 限制throwError函数不需要有任何返回值,任何值都不⾏,像undeifned、null都不⾏
function throwError(str: string): never {
	throw new Error('程序异常退出:' + str)
}

void

含义是空,即函数不返回任何值,调⽤者也不应依赖其返回值进行任何操作!

ts
// 函数没有返回时,但会有⼀个隐式返回值undefined,其是void可以接受的⼀种“空”
function logMessage():void{
	//情况一:不return
    
    //情况二:仅return
    return ;
    
    //情况三,return undefined
    return undefined;
}


let result = logMessage()
// 返回undefined时,下述语句可以执行,返回void时,报错:⽆法测试 "void" 类型的表达式的真实性
if(result){ 
}

Tuple

元组可以存储固定数量的元素,并且每个元素的类型是已知的且可以不同。

ts
// 第⼀个元素必须是 number 类型,第二个元素是可选的,如果存在,必须是 boolean 类型。
let arr1: [number,boolean?]
// 第⼀个元素必须是 number 类型,后⾯的元素可以是任意数量的 string 类型
let arr2: [number,...string[]]

// 可以赋值
arr1 = ['hello',123]
arr2 = [100,'hello','world']

enum

数字枚举

其成员的值会自动递增,且数字枚举还具备反向映射的特点。

ts
enum Direction {
    // 默认从0开始递增
    Up,
    Down,
    //也可以指定其值,后续从5开始递增
    Left=5,
    Right
}

// 反向映射
console.log(Direction.Up)// 0
console.log(Direction[0])// 为Direction.Up,即0

// 此⾏代码报错,枚举中的属性是只读的
Direction.Up = 'shang'
字符串枚举
ts
enum Direction {
    Up = "up",
    Down = "down",
    Left = "left",
    Right = "right"
}

// 不支持反向映射
let dir: Direction = Direction.Up;
console.log(dir); // 输出: "up"
常量枚举

使⽤ const 关键字定义,在编译时会被内联(将枚举成员引⽤替换为它们的实际值)。

ts
const enum Directions {
    Up,
    Down,
    Left,
    Right
}
let x = Directions.Up;

//翻译为
"use strict";
let x = 0 /* Directions.Up */;

type

type 可以为任意类型创建别名,同时能更方便进行类型复用和扩展。

基本用法
ts
// 为number取别名为num
type num = number;
let price: num;
price = 100;
联合类型

表示⼀个值可以是几种不同类型之⼀

ts
// 类型可以为number或string
type Status = number | string
// 值仅可为男或女
type Gender = '男' | '⼥'

function printStatus(status: Status,str:Gender) {
		
}
交叉类型

允许将多个类型合并为⼀个类型。合并后的类型将拥有所有被合并类型的成员。

ts
// ⾯积
type Area = {
    height: number; //⾼
    width: number; //宽
};

// 地址
type Address = {
    num: number; //楼号
    cell: number; //单元号
    room: string; //房间号
};

// 定义类型House,且House是Area和Address组成的交叉类型,需要传入所有属性
type House = Area & Address;
const house: House = {
    height: 180,
    width: 75,
    num: 6,
    cell: 3,
    room: '702'
};

类型声明

基本类型

ts
let a: string //变量a只能存储字符串
let b: number //变量b只能存储数值


// 参数x必须是数字,参数y也必须是数字,函数返回值也必须是数字,且调用传入参数必须为两个
function demo(x:number,y:number):number{
	return x + y
}

//a的值只能为字符串“你好”,b的值只能为100,不能再为其赋其他值
let a: '你好' 
let b: 100 

//自动类型推断
let d = -99 //TypeScript会推断出变量d的类型是数字
d = false //警告:不能将类型“boolean”分配给类型“number”

对象声明

ts
// 限制person1对象必须有name属性,age为可选属性
let person1: { name: string, age?: number }
// 含义同上,也能⽤分号做分隔
let person2: { name: string; age?: number }
// 含义同上,也能⽤换⾏做分隔
let person3: {
    name: string
    age?: number
}
// 限制person对象必须有name属性,可选age属性但值必须是数字,同时可以有任意数量、任意类型的其他属性
let person: {
    name: string
    age?: number
    [key: string]: any // 索引签名,完全可以不⽤key这个单词,换成其他的也可以
}

// 如下赋值均可以
person1 = {name:'李四',age:18}
person2 = {name:'张三'}
person3 = {name:'王五'}
person4 = {
    name:'张三',
    age:18,
    gender:'男'
}

函数声明

ts
let count: (a: number, b: number) => void

const func: count = (1,2) => {
    // 注意,虽然限制了返回值为void,但是允许返回⾮空值
	return 100; 
};

//是为了确保如下代码成⽴,Array.prototype.push 的返回值是⼀个数字,⽽ Array.prototype.forEach ⽅法期望其回调的返回类型是 void。
const src = [1, 2, 3];
const dst = [0];
src.forEach((el) => dst.push(el));
数组声明
ts
let arr1: string[]
let arr2: Array<string>
arr1 = ['a','b','c']
arr2 = ['hello','world']

类型约束文件

类型声明文件通常以 .d.ts 作为扩展名。它的主要作用是为现有的 JavaScript 代码提供类型信息,使得在使用这些js时可以对其进行类型检查。

js
// 添加类型检查文件后,使用js也会有类型提示

// 现有的js文件:demo.js
export function add(a, b) {
    return a + b;
}
export function mul(a, b) {
    return a * b;
}

// 类型解释文件:demo.d.ts
declare function add(a: number, b: number): number;
declare function mul(a: number, b: number): number;
export { add, mul };

其他

数据解构

数组结构
ts
let arr = [1, 2, 3];
// x,y,z 将与 arr 中的每个位置对应来取值
const [x, y, z] = arr;
对象解构
ts
const person = {
    name: "jack",
    age: 21,
    language: ['java', 'js', 'css']
}

// 解构表达式获取值,将 person 里面每一个属性和左边对应赋值
const {name, age, language} = person;

// 将name属性赋值给personName
const {name: personName, age, language} = person;

链判断

如果读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在。

ts
const firstName = message?.body?.user?.firstName || 'default';

// 等价于
const firstName = (message
                   && message.body
                   && message.body.user
                   && message.body.user.firstName) || 'default';

参数默认值

ts
// 直接给参数写上默认值,没传就会自动使用默认值
function add2(a, b = 1) {
  // 等价于 b = b || 1;
  return a + b;
}

箭头函数

ts
// 只有一个参数可以不需要()
let print = obj => console.log(obj);

//当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
let sum2 = (a, b) => a + b;

// 多行代码需要使用{}扩起
let sum3 = (a, b) => {
    c = a + b;
    return c;
};

模板字符串

ts
let info = `你好,我的名字是:${name},年龄是:${person.age},邮箱是:${person.email}`

指数运算符

js
// 案例一:运算方式
2 ** 2 // 4
2 ** 3 // 8

// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2  // 512

解构赋值

Null判断运算符

类似||,但是只有运算符左侧的值为nullundefined时,才会返回右侧的值

js
null ?? 55; 		 // 55
undefined ?? 55; // 55

拓展运算符

取出数组和对象的所有属性

js
// 数组(对象同样可以)
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const mergedArray = [...arr1, ...arr2];
console.log(mergedArray); // 输出: [1, 2, 3, 4, 5, 6]

// 取出数组所有元素给函数
function sum(a, b, c) {
    return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 输出: 6


const person = { name: '李四', age: 18, city: '北京' };
const { name, ...rest } = person;
console.log(name); // 输出: 李四
console.log(rest); // 输出: { age: 18, city: '北京' }

面向对象

基本使用

ts
class Person {
    // 属性声明
    name: string
    age: number
    
    // 构造器
    constructor(name: string, age: number) {
    this.name = name
    this.age = age
    }
    
    // ⽅法
    speak() {
        console.log(`我叫:${this.name},今年${this.age}岁`)
    }
}

// 创建实例
const p1 = new Person('周杰伦', 38)

继承

ts
class Student extends Person {
    // 子类新增属性
    grade: string

    // 构造器,若Student类不需要额外的属性,Student的构造器可以省略
    constructor(name: string, age: number, grade: string) {
        super(name, age)
        this.grade = grade
    }

    // 重写从⽗类继承的⽅法
    override speak() {
        // 内部通过this调用
        console.log(`我是学⽣,我叫:${this.name},今年${this.age}岁,在读${this.grade}年级`,)
    }

	// ⼦类⾃⼰的⽅法
    study() {
        console.log(`${this.name}正在努⼒学习中......`)
    }
}

封装

关键字作用
public可以被:类内部、子类、类外部访问 。
protected可以被:类内部、子类访问。
private可以被:类内部访问。
readonly属性无法修改。
ts
class Person {
	// 按照下方规则使用,默认就是public修饰
    public name: string,
    // 只读属性可以与其他属性一起
    public readonly age:number
    
    // readonly属性仅能在构造器初始化时赋值
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}


// 简化对象创建形式
class Person {
    constructor(public name: string,public readonly age: number) {
    }
}

抽象类

抽象类不能实例化,其意义是可以被继承,抽象类里可以有普通方法、也可以有抽象方法。

ts
abstract class Package {
    constructor(public weight: number) { }
	// 抽象⽅法
    abstract calculate(): number
	// 通⽤⽅法
    printPackage() {
        console.log(`包裹重量为: ${this.weight}kg,运费为: ${this.calculate()}元`);
    }
}

// 实现抽象方法
class StandardPackage extends Package {
    constructor(
        weight: number,
        public unitPrice: number // 每公⽄的固定费率
    ) { super(weight) }
    
	// 实现抽象⽅法
    calculate(): number {
        return this.weight * this.unitPrice;
    }
}

// 创建实例
const s1 = new StandardPackage(10,5)

接口

只能定义格式,不能包含任何实现 !

  • 相同点: interface 和 type 都可以用于定义对象结构,在定义对象结构时两者可以互换。
  • 不同点:
    • interface :更专注于定义对象和类的结构,⽀持继承、合并。
    • type :可以定义类型别名、联合类型、交叉类型,但不⽀持继承和⾃动合并。

定义类结构

ts
// 创建接口
interface PersonInterface {
    name: string
    age: number
    speak(n: number): void
}

// 实现接口
class Person implements PersonInterface {
    constructor(
        public name: string,
        public age: number
    ) { }
	// 实现接⼝中的 speak ⽅法
    speak(n: number): void {
        for (let i = 0; i < n; i++) {
			// 打印出包含名字和年龄的问候语句
            console.log(`你好,我叫${this.name},我的年龄是${this.age}`);
        }
    }
}

// 创建⼀个实例
const p1 = new Person('tom', 18);

定义对象结构

ts
interface UserInterface {
    name: string
    readonly gender: string // 只读属性
    age?: number // 可选属性
    run: (n: number) => void
}
const user: UserInterface = {
    name: "张三",
    gender: '男',
    age: 18,
    run(n) {
        console.log(`奔跑了${n}⽶`)
    }
};

定义函数结构

ts
interface CountInterface {
    (a: number, b: number): number;
}

const count: CountInterface = (x, y) => {
    return x + y
}

接口继承

ts
interface PersonInterface {
    name: string // 姓名
    age: number // 年龄
}
interface StudentInterface extends PersonInterface {
    grade: string // 年级
}
const stu: StudentInterface = {
    name: "张三",
    age: 25,
    grade: '⾼三',
}

接口自动合并

ts
// 接口一
interface PersonInterface {
    name: string
    age: number
}

// 接口二
interface PersonInterface {
    speak(): void
}

// Person类实现PersonInterface,会同时实现接口一和接口二
class Person implements PersonInterface {
    name: string
    age: number
	
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }

    speak() {
        console.log('你好!我是⽼师:', this.name)
    }
}

泛型

定义函数、类或接口时,使用类型参数来表示未指定的类型,这些参数在具体使⽤时,才被指定具体的类型。

ts
// 泛型函数(单个泛型)
function logData<T>(data: T): T {
    console.log(data)
    return data
}
logData<number>(100)
logData<string>('hello')

// 泛型函数(多个泛型)
function logData<T, U>(data1: T, data2: U): T | U {
    console.log(data1,data2)
    return Date.now() % 2 ? data1 : data2
}
logData<number, string>(100, 'hello')
logData<string, boolean>('ok', false)



// 泛型接口
interface PersonInterface<T> {
    name: string,
    age: number,
    extraInfo: T
}
let p1: PersonInterface<string>
p1 = { name: '张三', age: 18, extraInfo: '⼀个好⼈' }



// 泛型约束
interface LengthInterface {
	length: number
}
// 约束规则是:传⼊的类型T必须具有 length 属性
function logPerson<T extends LengthInterface>(data: T): void {
	console.log(data.length)
}



// 泛型类
class Person<T> {
    constructor(
        public name: string,
        public age: number,
        public extraInfo: T
    ) { }
    speak() {
        console.log(`我叫${this.name}今年${this.age}岁了`)
        console.log(this.extraInfo)
    }
}

type JobInfo = {
    title: string;
    company: string;
}

const p2 = new Person<JobInfo>("tom", 30, { title: '研发总监', company: '发发发科技公司' });

AXIOS

get

ts
axios.get('/user', {
    // 携带请求参数
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    // 处理成功情况
    console.log(response);
  })
  .catch(function (error) {
    // 处理失败情况
    console.log(error);
  })
  .finally(function () {
    // 总是会执行
  });  

post

ts
axios.post('/user', {
        firstName: 'Fred',
        lastName: 'Flintstone'
    })
    .then(function (response) {
        console.log(response);
    })
    .catch(function (error) {
        console.log(error);
    });

请求响应信息

ts
{
  // `data` 由服务器提供的响应
  data: {},

  // `status` 来自服务器响应的 HTTP 状态码
  status: 200,
  
  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: 'OK',

  // `headers` 是服务器响应头
  // 所有的 header 名称都是小写,而且可以使用方括号语法访问
  // 例如: `response.headers['content-type']`
  headers: {},

  // `config` 是 `axios` 请求的配置信息
  config: {},

  // `request` 是生成此响应的请求
  // 在node.js中它是最后一个ClientRequest实例 (in redirects),
  // 在浏览器中则是 XMLHttpRequest 实例
  request: {}
}

请求配置

创建实例instance后,添加配置信息和拦截器后,后续操作通过实例执行

ts
const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

//可用的配置项如下:
{
  // `url` 是用于请求的服务器 URL
  url: '/user',

  // `method` 是创建请求时使用的方法
  method: 'get', // 默认值

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: 'https://some-domain.com/api/',

  // `transformRequest` 允许在向服务器发送前,修改请求数据
  // 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法
  // 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
  // 你可以修改请求头。
  transformRequest: [function (data, headers) {
    // 对发送的 data 进行任意转换处理

    return data;
  }],

  // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
  transformResponse: [function (data) {
    // 对接收的 data 进行任意转换处理
    return data;
  }],

  // 自定义请求头
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` 是与请求一起发送的 URL 参数
  // 必须是一个简单对象或 URLSearchParams 对象
  params: {
    ID: 12345
  },

  // `paramsSerializer`是可选方法,主要用于序列化`params`
  // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data` 是作为请求体被发送的数据
  // 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法
  // 在没有设置 `transformRequest` 时,则必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属: FormData, File, Blob
  // - Node 专属: Stream, Buffer
  data: {
    firstName: 'Fred'
  },
  
  // 发送请求体数据的可选语法
  // 请求方式 post
  // 只有 value 会被发送,key 则不会
  data: 'Country=Brasil&City=Belo Horizonte',

  // `timeout` 指定请求超时的毫秒数。
  // 如果请求时间超过 `timeout` 的值,则请求会被中断
  timeout: 1000, // 默认值是 `0` (永不超时)

  // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials: false, // default

  // `adapter` 允许自定义处理请求,这使测试更加容易。
  // 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。
  adapter: function (config) {
    /* ... */
  },

  // `auth` HTTP Basic Auth
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType` 表示浏览器将要响应的数据类型
  // 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'
  // 浏览器专属:'blob'
  responseType: 'json', // 默认值

  // `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)
  // 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求
  // Note: Ignored for `responseType` of 'stream' or client-side requests
  responseEncoding: 'utf8', // 默认值

  // `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称
  xsrfCookieName: 'XSRF-TOKEN', // 默认值

  // `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称
  xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值

  // `onUploadProgress` 允许为上传处理进度事件
  // 浏览器专属
  onUploadProgress: function (progressEvent) {
    // 处理原生进度事件
  },

  // `onDownloadProgress` 允许为下载处理进度事件
  // 浏览器专属
  onDownloadProgress: function (progressEvent) {
    // 处理原生进度事件
  },

  // `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数
  maxContentLength: 2000,

  // `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数
  maxBodyLength: 2000,

  // `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。
  // 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),
  // 则promise 将会 resolved,否则是 rejected。
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 默认值
  },

  // `maxRedirects` 定义了在node.js中要遵循的最大重定向数。
  // 如果设置为0,则不会进行重定向
  maxRedirects: 5, // 默认值

  // `socketPath` 定义了在node.js中使用的UNIX套接字。
  // e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。
  // 只能指定 `socketPath` 或 `proxy` 。
  // 若都指定,这使用 `socketPath` 。
  socketPath: null, // default

  // `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
  // and https requests, respectively, in node.js. This allows options to be added like
  // `keepAlive` that are not enabled by default.
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // `proxy` 定义了代理服务器的主机名,端口和协议。
  // 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。
  // 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。
  // `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。
  // 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。
  // 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // see https://axios-http.com/zh/docs/cancellation
  cancelToken: new CancelToken(function (cancel) {
  }),

  // `decompress` indicates whether or not the response body should be decompressed 
  // automatically. If set to `true` will also remove the 'content-encoding' header 
  // from the responses objects of all decompressed responses
  // - Node only (XHR cannot turn off decompression)
  decompress: true // 默认值

}

请求拦截

ts
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });

模块化

Common JS模块化

Node.js 默认是⽀持 CommonJS 规范的,但浏览器端不⽀持,所以需要经过编译 。所以主要应用于服务端

导出

  • 每个模块内部的: this 、 exports 、 modules.exports 在初始时,都指向同⼀个空对象
  • ⽆论如何修改导出对象,最终导出的都是 module.exports 的值。
  • exports 是对 module.exports 的初始引⽤,仅为了⽅便给导出象添加属性,所以不能使⽤ exports = value 的形式导出数据,但是可以使⽤ module.exports= xxxx 导出数据。

js
const name = '尚硅⾕'
const slogan = '让天下没有难学的技术!'

function getTel()
{
    return '010-56253825'
}


// 方式一
exports.name = name
exports.slogan = slogan
exports.getTel = getTel

// 方式二
module.exports.name = name
module.exports.slogan = slogan
module.exports.getTel = getTel

// 方式三
module.exports={name,slogan,getTel}

导入

ts
// 直接引⼊模块,通过school.xx使用
const school = require('./school')

// 引⼊同时解构出要⽤的数据
const { name, slogan, getTel } = require('./school')

// 引⼊同时解构+重命名
const {name:stuName,motto,getTel:stuTel} = require('./student')

浏览器支持

第⼀步:全局安装 browserify : npm i browserify -g

第二步:编译

js
// index.js 是源⽂件, build.js 是输出的⽬标⽂件
browserify index.js -o build.js

第三步:页面中引入使用

js
<script type="text/javascript" src="./build.js"></script>

ES6模块化

浏览器与服务端均⽀持该规范 ,主要应用于浏览器端

Node.js 中运⾏ ES6 模块代码有两种⽅式:

  • ⽅式⼀:将 JavaScript ⽂件后缀从 .js 改为 .mjs ,Node 则会⾃动识别 ES6 模块。
  • ⽅式⼆:在 package.json 中设置 type 属性值为 module

页面中引入

html
<!--通过script标签引入,需要指定type=module-->
<script type="module" src="./index.js"></script>

导出

ts
// 分别导出
export let name = {str: '尚硅⾕'}
export const slogan = '让天下没有难学的技术!'
export function getTel() {
    return '010-56253825'
}


// 统⼀导出
const name = {str:'尚硅⾕'}
const slogan = '让天下没有难学的技术!'
function getTel (){
    return '010-56253825'
}
export {name,slogan,getTel}


// 默认导出,导出的是一个对象
const name = '张三'
const motto = '⾛⾃⼰的路,让别⼈五路可⾛!'
function getTel (){
    return '13877889900'
}
export default {name,motto,getTel}

导入

ts
// 全部导入
import * as school from './school.js

// 命名导入,对应分别导出、统⼀导出
import { name,slogan,getTel } from './school.js
import { name as myName,slogan,getTel } from './school.js' // as重命名

// 默认导入,对应默认导出。默认导出的名字可以修改,其是一个对象,通过这个对象调用导出属性。
import student from './student.js' 



// 命名导⼊与默认导⼊混合使⽤,且默认导⼊的内容必须放在前⽅
export const name = {str:'尚硅⾕'}
export const slogan = '让天下没有难学的技术!'
function getTel (){
	return '010-56253825'
}
export default getTel

import getTel,{name,slogan} from './school.js


// 动态导入
// 允许在运⾏时按需加载模块,在需要导入模块时再执行下方代码即可,返回值是⼀个 Promise。
const school = await import('./school.js');

// 仅运行js文件,不导入数据
import './mock.js'

实际使用

防抖

在事件被触发的 n 秒后再执行,如果这 n 秒内又被触发,则重新计时。

  • 按钮提交场景:防止多次提交按钮,只执行最后提交的一次
  • 输入场景:搜索联想词功能类似
js
/**
 * 防抖函数,可立即执行,可传参,但无返回值
 * @param {Function} fn 需要防抖的函数
 * @param {Number} delay 防抖的时间
 * @param {Boolean} immediate 是否立即执行
 * @returns 
 */
const debounce = (fn, delay = 800, immediate = false) => {
	let timer = null;
  return function(...args) {
  	if(timer !== null) clearTimeout(timer);
    if(immediate && timer === null) {
    	fn.apply(this, args);
      timer = setTimeout(()=> timer = null, delay);
      return;
    }

    timer = setTimeout(()=>{
    	fn.apply(this, args);
      clearTimeout(timer);
      timer = null;
    }, delay)
  }
}

节流

在规定单位时间内,只能触发一次函数。如果多次触发则只生效一次。

js
/**
 * 节流函数
 * @param {Function} fn 需要节流的函数
 * @param {Number} delay 节流的时间
 * @returns
 */
const throttle = (fn, delay) => {
  let previous = 0;
  return function (...args) {
    const now = Date.now();
    if (now - previous > delay) {
      previous = now;
      return fn.apply(this, args);
    }
  }
}

深拷贝

js
/**
 * 数据类型:
 * 1. 可以直接返回的:
 * Number、String、Boolean、undefined、Symbol、BigInt、Function
 * null(需要特殊判断)
 *
 * 2. 不可以直接返回的:
 * Object、Array
 * Object 的特殊细分类型:Date、RegExp
 *
 * 3. 避免循环引用
 */
const deepClone = (obj, hash = new WeakMap()) => {
  if (obj === null) return obj
  if (obj instanceof Date) return new Date(obj)
  if (obj instanceof RegExp) return new RegExp(obj)
  if (typeof obj !== 'object') return obj

  if (hash.has(obj)) return hash.get(obj)

  const target = Array.isArray(obj) ? [] : {}
  hash.set(obj, target)

  Reflect.ownKeys(obj).forEach(key => {
    target[key] = deepClone(obj[key], hash)
  })

  return target
}

有趣代码

可编辑网页

浏览器地址栏输入下方链接,可以作为记事本使用。解码后内容为:<html contenteditable></html>

data:text/html;base64,PGh0bWwgY29udGVudGVkaXRhYmxlPmVkaXQgaGVyZTwvaHRtbD4=

浏览器控制台直接执行以下js代码,将可以编辑网页元素

document.body.contenteditable=true

边框显示

js
[].forEach.call($$("*"),function(a){
a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16)
})

无限debug

js
setInterval(()=>{(a=>{try{((''+a/a).length!==1?1:1)&&Function('debugger')();a++&&arguments.callee(a)}catch{}})(0)},4000);