JS入门扫盲

498 阅读25分钟

js才是真的难

概念

同步和异步

JS从一出生就是一门单线程语言,所有程序都需要排队执行。起初我认为这是极好的,直到有一天,当遇到定时器或者网络请求的时候经常被卡住。我还需要暂停下手头的工作,等待这些卡住的任务完成,以至于体验极差。为了解决这个问题,我将任务分成了同步和异步俩个类型。

同步任务:处理那些不需要等待的任务,例如:let a = 1。
异步任务:处理那些需要等待的任务,例如:setTimeout、网络请求等。

运行了一段时间感觉还可以,但是有一天却接到这样的投诉,promise发起的请求已经完成,但是很久不执行then,等得用户心急如焚

宏任务和微任务

JS的执行机制是自上而下,先去执行同步代码,然后是微任务,然后去渲染dom,最后去执行宏任务
微任务:需要连贯执行:Promise、async、await
宏任务:不需要连贯执行:setTimeout、setInterval、Ajax、DOM事件

Promice 加载图片

作用:解决地狱回调问题

当一个请求需要依赖上一个请求返回的结果的时候,俗称回调地狱。为了解决这种问题,Promise()就诞生了,他可以优雅的解决异步问题。Promice的状态一旦改变之后,就不可逆,并且不可以进行第二次改变。

Promice的三种状态 pending

<body>
    <script type="text/javascript">
    function one() {
        return 'I am one'
    }
    function two() {
        return new Promise ((resolve, reject) => {
            setTimeout(() => {
                resolve('I am two')
            }, 3000);
        })
    }
    function three() {
        return 'I am three'
    }
    async function run() {
        console.log(one())
        console.log(await two())
        console.log(three())
    }
    run()
    </script>
</body>

async和await

前后端数据交互 HTTP 超文本协议

HTTP协议,包括客户端(浏览器)、服务端(服务器)俩个实体。当我们想访问某篇博文时。客户端会发送请求(例如 https://www.bilibili.com)给服务端,服务端收到API会解析他们,并返回客户端相应的数据资源。这些数据资源,可以是html文档、图片、普通文本。

http和https到底有什么区别?

HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

客户端发送HTTP请求到服务端

HTTP请求方式
GET[默认]获取列表 /list
GET获取详情 /details?postId=1
POST增加 /tickets
PUT更新替换 /tickets/12
PATCH修改 /tickets/12
DELETE删除 /tickets/12

cookie、session、jwt、token`记录管理状态

解释一下JS中的原型和原型链

new 实例对象、原型对象、构造函数、

不像java或者c++,js是动态的本身并不提供一个class的实现,即便是在es6中引入了class关键字,但那也只是语法糖,js仍然是基于原型的,当谈到继承时,js具有一种结构那就是对象。每一个实例对象都有一个私有属性称之为__proto__指向它的构造函数的原形对象prototype

继承是指一个类的属性和方法,通过继承的方式让其他类用来使用。并且在使用的时候,还可以修改一定的方法和属性

原型和原型链可以解决javascript中实现继承的问题

class继承

原型

在JS中,每一个函数都自带一个 prototype 属性,我们就把 函数名.prototype 称为原型。也叫“显式原型”。当我们打印 函数名.prototype 时,打印出来的这个对象,称作原型对象

    /**
     * 什么是原型?
     * 在JS中,每一个函数都自带一个 prototype 属性,我们就把 函数名.prototype 称为原型。也叫“显式原型”
     * 当我们打印 函数名.prototype 时,打印出来的这个对象,称作原型对象
     * 
     * 原型对象 默认又有俩个属性:
     * 1.constructor(构造器): 指向构造函数本身
     * 2.__Proto__(隐式原型):指向其上一级的原型
     * 
     * [[prototype]]其实就是__proto__ 因为各大浏览器厂家取名不同
     */

    //  1.构造函数
    function MM(name) {
        this.name = name
    }

    // 2.实例化 - 创建对象
    const mm = new MM('哈妮克孜')

    // 3.打印
    console.log(mm) // 实例化对象mm
    console.log(MM.prototype) // 原型对象
    console.log(MM.prototype.constructor === MM) // true =>  指向函数本身
    console.log(MM.prototype === mm.__proto__) // true => 
    // __proto__和prototype不太一样,
    // __proto__是对象拥有的隐式原型,prototype是函数拥有的显式原型

原型链


闭包

闭包是作用域的一种特殊应用。当我们写一些for循环或者a--,之类的函数时,通常都会在函数顶部声明一个全局变量。但有人说全局变量,这玩意儿占内存不说。任意函数都可以随便调用,会污染环境。于是就有一种新的机制,既能长期保存变量又不会污染全局,这就是闭包。

<script type="text/javascript">
    function fa() {
        var a = 10
        function fb() {
            a-- // 2.内部函数引用外部作用域的变量参数
            console.log('aa', a)
        }
        return fb // 3.返回值是函数
    }
    var fm = fa() // 4.创建一个对象函数,让其长期驻留
    // fm = null // 用完后,我们可以把它释放掉,不用担心内存泄漏

    // 那什么是闭包? 满足以下 4个条件
    /**
     * 1.有函数嵌套
     * 2.内部函数引用外部作用域的变量参数
     * 3.返回值是函数
     * 4.创建一个对象函数,让其长期驻留
     */ 
</script>

图片预加载和懒加载

当页面中有很多图片的时候,图片加载就需要一定的时间,不仅会影响渲染速度,还浪费服务器性能和带宽。而懒加载就是优先加载可视区的内容,其他内容等进入了可视区域再进行加载。我们要实现懒加载需要做到2个步骤,1.如何加载图片,2.如何判断进入可视区域。先说加载图片,我们知道图片都是根据图片标签上的src属性进行加载的,所以在图片进入可视区域前,我们先不给src属性赋值,或者给一个很小的loading图地址,等到图片进入可视区域后再给src附上真正的地址。再来看图片是否进入了可视区域。

方法一.IntersectionObserver

有个方法叫做交叉观察器,intersectionObserver。/ˌɪntəˈsekʃn//əbˈzɜːvə(r)/.音特塞克伸,额波蜇窝,这个api可以自动观察一个元素是否可见或者两个元素是否相交,一般用于实现图片懒加载、内容无限滚动等功能

image.png

代码示例

// 自定义指令 v-lazy
export default {
    mounted(el) {
        console.log('el', el)
        const imgSrc = el.src // 复制图片src地址
        el.src = '' // 默认将src清空

        // 交叉观察者 API
        const lazyLoad = new IntersectionObserver((entries) => {
            // 当元素出现在可视区域, 和离开可视区域被触发
            console.log('entries', entries[0].isIntersecting)
            if (entries[0].isIntersecting) {
                // isIntersecting 为 true 说明进入可视区域
                el.src = imgSrc // 进入可视区域 还原sec地址,实现懒加载
                // 停止观察
                lazyLoad.unobserve()
            }
        });
        lazyLoad.observe(el)
    }

}

方法二. vue-lazyload

安装vue-lazyload插件,在main.js  中全局引入,然后在图片元素src属性名称写成v-lazy即可

深拷贝 和 浅拷贝

假设B复制了A,当修改A时,B也跟着发生了变化,这说明只拷贝了指针,AB实际上还是共用的一份数据,这是浅拷贝。
假设B复制了A,当修改A时,A变,B没有变,那就是深拷贝,复制对象不受原对象的影响,因为不仅拷贝了指针,还拷贝了内存。他们自己有自己的内容,互相独立

浅拷贝

把变量A直接赋值给变量B就是浅拷贝

<script type="text/javascript">
    const obj1 = {
        name: '张三',
        age: 18,
        address: { city: '北京' },
        nobby: [ '台球', '篮球']
    }
    const obj2 = obj1 //  浅拷贝
    obj2.address.city = '上海'

    console.log('obj1', obj1)
    console.log('obj2', obj2)
</script>

image.png

深拷贝

方法一.JSON.parse(JSON.stringify(obj))

js内置的JSON序列化和反序列化方法
缺点: 不能存放函数,时间对象和正则

<script type="text/javascript">
    const obj1 = {
        name: '张三',
        age: 18,
        address: { city: '北京' },
        fn: function name(params) {
            
        },
        nobby: [ '台球', '篮球'],
    }
    // const obj2 = obj1 //  浅拷贝
    const obj2 = deepClone(obj1) // 深拷贝
    obj2.address.city = '上海'

    console.log('obj1', obj1)
    console.log('obj2', obj2)
    // 方法一:JSON序列化
    function deepClone(params) {
        return JSON.parse(JSON.stringify(params))
    }
</script>

image.png

方法二.递归

缺点: 没有考虑循环引用

<script type="text/javascript">
    const obj1 = {
        name: '张三',
        age: 18,
        address: { city: '北京' },
        fn: function name(obj) {
            
        },
        nobby: [ '台球', '篮球'],
    }
    // const obj2 = obj1 //  浅拷贝
    const obj2 = deepClone(obj1) // 深拷贝

    obj2.age = 20
    obj2.address.city = '上海'

    console.log('obj1', obj1)
    console.log('obj2', obj2)
    // 方法二:递归
    function deepClone(obj) {
        if (typeof(obj) !== 'object' || typeof(obj) === null) {
            // 如果他不是一个对象,或者压根儿就是一个null。我们也没有必要处理它了
            return obj
        }
        // instanceof 判断基本数据类型的方法
        let res = obj instanceof Array ? [] : {}
        // for in
        for (const key in obj) {
            if (obj.hasOwnProperty(key)) {
                res[key] = deepClone(obj[key])
            }
        }
        return res
    }
</script>

image.png

方法三.lodash.cloneDeep,推荐工作中使用

防抖和节流

区别共同点区别应用场景
防抖 debounce在事件频繁被触发时只执行最后一次input搜索框
节流 throttle减少事件执行次数有规律的执行拖拽、scroll滚动条

防抖

// 输入框内容变化时的回调
function inputSearch_onChange(params) {
  // 获取输入框 value
  debounce(function () {
    console.log('打印', params.target.value)
    // ...执行 Axios 调取数据
  })
}
// 防抖
let timer = null
function debounce(fn) {
  if (timer) clearTimeout(timer) // 规定时间内若定时器存在则清除
  timer = setTimeout(() => {
    fn()
  }, 1000);
}

节流

function inputSearch_onSearch(params) {
  
  throttle(function () {
    console.log('点击搜索按钮', params)
  })
}
// 节流
let flag = true
function throttle(fn) {
  if(flag) {
    setTimeout(() => {
      console.log('触发点击')
      fn() // 调用接口
      flag = true // 在定时器执行后 移除if阻断
    }, 1000);
  } 
  flag = false // 在执行一次后 if阻断定时器继续执行
}

请描述event loop的机制

堆和栈

for in 和 for of区别

区别
for..in遍历得到key可枚举的数据:数组、字符串、对象
for..of遍历得到value可迭代的数据:数组、字符串、Set、Map

代码示例

<script type="text/javascript">
    /**
     * 数组
     */
    const arr = [10, 20, 30]
    for (const key in arr) {
        // 遍历得到 key
        console.log('数组[for..in]', key) // 0,1,2
    }
    for (const val of arr) {
        // 遍历得到 value
        console.log('数组[for..of]', val) // 10,20,30
    }
    /**
     * 对象
     */
    const obj = {
        name: '张三',
        age: 18,
    }
    for (const key in obj) {
        console.log('对象[for..in]', key) // name, age
    }
    for (const val of obj) {
        console.log('对象[for..of]', val) // 报错
    }
    /**
     * Set
     */
    const set = new Set([10, 20, 30])
    for (const key in set) {
        console.log('Set[for..in]', key) // 无
    }
    for (const val of set) {
        console.log('Set[for..of]', val) // 10, 20, 30
    }
    /**
     * Map
     */
    const map = new Map([
        ['a', 10],
        ['b', 10],
        ['c', 10]
    ]);
    for (const key in map) {
        console.log('Map[for..in]', key) // 无
    }
    for (const val of map) {
        console.log('Map[for..of]', val) // ['a', 10], ['b', 10], ['c', 10]
    }
</script>

map foreach

image.png

image.png

JS基础知识

数据类型

基本数据类型:number、string、boolean、null、undefined
引用数据类型:数组(Array)、对象(Object)、函数(Function)

// 引用数据类型: 函数(Function)
let kiki = function name(params) {
    alert('kk99')
}
kiki() // 弹窗 kk99
name()// 报错 TypeError: name is not a function

作用域

var a = 10
console.log('AA:', a) // AA: 10

console.log('BB:', b) // AA: undefined
var b = 10
/**
 * 执行顺序,由于变量提升发生了改变
 * var b
 * console.log('BB:', b)
 * b = 10  // b声明向上提升。b=10赋值留在远处
 */
console.log('CC:', c) // 报错 ReferenceError: c is not defined
/**
 * 由于没有声明 c
 * 报错,导致后续代码都不会在执行
 */ 

JS的三种声明方式,let、const、var的区别

(1)块级作用域: 块作用域由 { }包括,let和const具有块级作用域,var不存在块级作用域。var声明的变量为全局变量。块级作用域解决了ES5中的两个问题:

  • 内层变量可能覆盖外层变量
  • 用来计数的循环变量泄露为全局变量

(4)重复声明: var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const和let不允许重复声明变量。

(6)初始值设置: 在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。

区别varletconst
是否有块级作用域×✔️✔️
能否重复声明变量✔️××
是否必须设置初始值××✔️

for 循环机制

for (let i = 0; i < 5; i++) {
    // 循环体
}
/**
 * 执行的顺序如下:
 * 第一步 : i=0  初始化值
 * 第二步 : i<5 进行条件判断,如果为真,则继续执行
 * 第三步 : 执行循环体的内容
 * 第四步 : i++ 变量i自增
 * 第五步 : 回到第二步,条件判断为真,则执行循环体内容,再到i++一直循环,
 * 直到第二步的判断条件为假,则退出该循环
 */
for(条件1;条件2;条件3){  
     循环体4  
} 
**执行的循环: 1243 243 243 ....直到条件2为假则退出循环**

JS数组方法

push()

push() 删除数组中的最后一个,在尾部追加,类似于压栈,原数组会变。

const arr = [1, 2, 3]
arr.push(8)
console.log(arr) // [1, 2, 3, 8]

JS字符串方法

replace()替换

replace() 替换字符串的某元素,并返回替换后的字符串

var str = "《帝国崛起:民族战线》"
str.replace(/民族战线/, '12')  // '《帝国崛起:12》'

split()截取

把字符串分割为字符串数组

var time = "2023-02-28T11:25:00.075Z"
time.split('T')[0] // '2023-02-28' 
time.split('T')[1] // '11:25:00.075Z'

toString() 和 split() 数组字符串转化

image.png

代码题

获取数组最大值

方法一:使用for遍历数组,设置一个变量存储最大值

let array = [1, 2 ,-3, 'kk', '67', true, null]
let list = []
array.forEach(element => {
    // typeof() 判断数据类型
    if (typeof(element) === "number") {
        list.push(element)
    }
});
console.log('list', list) // [1, 2, -3]

let mun = list[0]
list.forEach(item => {
    if (mun < item) {
        mun = item
    } 
});
console.log('mun', mun) // 2

方法二:使用Math.max(...arr)函数,其中...是拓展运算符

let array = [1, 2 ,-3, 'kk', '67', true, null]
let list = []
array.forEach(element => {
    // typeof() 判断数据类型
    if (typeof(element) === "number") {
        list.push(element)
    }
});
console.log('list', list) // [1, 2, -3]
console.log('max', Math.max(...list)) // 2

方法三:sort()将数组元素进行排序

数组名.sort(function(a,b){return a-b}); 从小到大排列 数组名.sort(function(a,b){return b-a}); 从大到小排列

let array = [1, 4, 2, 3]
array.sort((n1, n2) => {
    return n2 - n1
})
console.log('array', array[0]) // 4

60秒倒计时,0停止

let count = 60
let timer = setInterval(() => {
    count--
    console.log(count)
    if (count === 0) {
        alert('停止')
        clearInterval(timer)
        // count = 60
    }
}, 10);

for循环中含setTimeout打印

var a = 10
function A() {
    console.log('1', a) // undefined
    var a = 20
    console.log('2', a) // 20
    for (var a = 1; a < 5; a++) {
        setTimeout(() => {
            console.log('3', a) // 4个 5。setTimeout为异步最后执行
        }, 10);  
    }
}
A()
console.log('4', a) // 10

image.png

答案解析

for (var i = 0; i < 10; i++){
    setTimeout(() => {
        console.log(i);
    }, 1000)
}

image.png setTimeout是异步执行,10ms后往任务队列里面添加一个任务,只有主线上的全部执行完,才会执行任务队列里的任务,当主线执行完成后,i是10,所以此时再去执行任务队列里的任务时,i全部是10了。 对于打印10次是:每一次for循环的时候,settimeout都执行一次,但是里面的函数没有被执行,而是被放到了任务队列里面,等待执行,for循环了4次,就放了4次,当主线程执行完成后,才进入任务队列里面执行。

for (let i = 0; i < 10; i++){
  setTimeout(() => {
    console.log(i);
  }, 1000)
}

image.png 因为for循环头部的let不仅将i绑定到for循环快中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值重新被赋值。setTimeout里面的function()属于一个新的域,通过 var 定义的变量是无法传入到这个函数执行域中的,通过使用 let 来声明块变量,这时候变量就能作用于这个块,所以 function就能使用 i 这个变量了;这个匿名函数的参数作用域 和 for参数的作用域 不一样,是利用了这一点来完成的。这个匿名函数的作用域有点类似类的属性,是可以被内层方法使用的。

数组去重

1.new Set(数组)

Set是一系列无序、没有重复值的数据集合,传入一个需要去重的数组,Set会自动删除重复的元素

再将Set转数组返回。此方法效率高,代码清晰,缺点是存在兼容性问题

let arr = [1, 2, 3, 4, 1, 2, 3, 4]
console.log('数组去重', [...new Set(arr)]) // [1, 2, 3, 4]

2.对象属性(indexof)

利用对象属性key排除重复项

遍历数组,每次判断新数组中是否存在该属性,不存在就存储在新数组中

并把数组元素作为key,最后返回新数组

这个方法的优点是效率高,缺点是使用了额外空间

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];

var newArr = [];
arr.forEach((key,index)=>{
    if(newArr.indexOf(key) === -1){
        // 如果newArr 没有当前这项就会显示 -1.并且就把数丢进newArr数组里
        newArr.push(key)
  }        
})
console.log(newArr) // [1, '1', 17, true, false, 'true', 'a', {}, {}] 

去重数组中重复的对象

      let arr = [
        { id: 0, name: "张三" },
        { id: 1, name: "李四" },
        { id: 2, name: "王五" },
        { id: 3, name: "赵六" },
        { id: 1, name: "孙七" },
        { id: 2, name: "周八" },
        { id: 2, name: "吴九" },
        { id: 3, name: "郑十" },
      ];

      const removeDuplicateObj = (arr) => {
        let newArr = []
        let obj = {};
        for (var i = 0; i < arr.length; i++) {
          if (!obj[arr[i].id]) {
            newArr.push(arr[i]);
            obj[arr[i].id] = true;
          }
        }
        return newArr
      };

      console.log(removeDuplicateObj(arr));

ts。

vue TypeScript中引用js文件异常

修改tsconfig.json中"strict"的值为false,禁用严格模式。

image.png


前后端数据交互 HTTP 协议

# B站链接、什么是http超文本协议?

HTTP协议,包括客户端(浏览器)、服务端(服务器)俩个实体。当我们想访问某片博文时。客户端会发送请求(例如 https://www.bilibili.com)给服务端,服务端收到API会解析他们,并返回客户端相应的数据资源。这些数据资源,可以是html文档、图片、普通文本。

URL结构

URL由以下几部分组成:资源类型、存放资源的主机域名、资源文件名。
Protocol 协议 [资源类型] [ˈprəʊtəkɒl] 坡儒特扣
Host 主机 [存放资源的主机域名] [həust]
Path 路径 [资源文件名] [pɑːθ]
Query 查询参数 [ˈkwɪəri]

image.png

客户端发送HTTP请求到服务端

image.png

HTTP请求方式
GET[默认]获取列表 /list
GET获取详情 /details?postId=1
POST增加 /tickets
PUT更新替换 /tickets/12
PATCH修改 /tickets/12
DELETE删除 /tickets/12

服务端返回响应到客户端

![image.png](p9-juejin.byteimg.com/tos-cn-i- k3u1fbpfcp/42d4f636215f442b9653764a02cdb3b7~tplv-k3u1fbpfcp-watermark.image)

响应状态码含义描述
100-199一般信息 100 Continue
200-299成功响应 201 Created
300-399重定向 301 Moved Permanently
400-499客户端错误 404 Not Found
500-599服务端错误 500 Internal Server Error

http和https到底有什么区别?

# B站链接、# http和https到底有什么区别?
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

HTTP无状态,需要用cookie、session、jwt、token记录管理状态

HTTP是无状态的,也就是说服务端是不认识客户端的。每次HTTP发出的请求,都会认为是从全新的客户端发出来的。然而,在许多应用场景中,我们需要保持用户登录的状态或记录用户购物车中的商品。由于HTTP是无状态协议,所以必须引入一些技术来记录管理状态,例如Cookiesession

授权认证登录之 Cookie、Session、Token、JWT 详解

了解Cookie

  • cookie 存储在客户端: cookie 是服务器发送到用户浏览器,并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时,被携带并发送到服务器上。因此,服务端脚本就可以读、写存储在客户端的cookie的值。
  • cookie 是不可跨域的: 每个 cookie 都会绑定单一的域名(绑定域名下的子域都是有效的),无法在别的域名下获取使用,同域名不同端口也是允许共享使用的。

什么是 Session(会话)

  • session 是记录服务器与浏览器会话状态的过程。这个过程是连续的,也可以时断时续的。
  • session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中

session 认证流程:

  • 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session
  • 请求返回时将此 Session 的唯一标识 SessionID 返回给浏览器
  • 浏览器接收到服务器返回的 SessionID 后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
  • 当用户第二次访问服务器的时候,请求会自动把此域名下的 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。

根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。

Cookie 和 Session 的区别

  • 安全性:  Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
  • 存取值的类型不同:Cookie 只支持存字符串数据,Session 可以存任意数据类型。
  • 有效期不同:  Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
  • 存储大小不同:  单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。

什么是 Token(令牌)

  • 简单 token 的组成:  Token是用户身份的验证方式。由uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)

  • token 的身份验证流程: image.png

  1. 客户端使用用户名跟密码,请求登录
  2. 服务端收到请求,验证成功后,服务端会生成一个 token 并把这个 token 发送给客户端
  3. 并将Token存到 Redis数据库(数据结构服务器)中,设置过期时间30分钟
  4. 客户端每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里
  5. 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据

Token 和 Session 的区别

  • Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
  • Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重复攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。
  • 如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。

什么是 JWT

git命令行提交

// 查看当前分支
git branch

// 暂存
git stash

// 创建25分支,并切换到25分支
git checkout -b 25

// 拉去master 主分支 代码
git pull origin upstream

// 放开之前的暂存
git stash pop

// 选择自己要提交的文件

// 提交到本地
git commit -m"#25 标签管理"

// 提交到git
git push origin 25

/**
 * 合并分支 (如把25分支合并到matser主分支,则先要进入master主分支)
 */

git checkout 25 // 切换分支

git pull origin 25 // 下拉全部代码

git checkout master // 切换回主分支

git merge 25 // git merge 分支名【将25分支合并到主分支】

hooks的用法

慕课网1块钱的教程

redux数据管理

ES6语法有哪些

JS的三种声明方式,let、const、var的区别

(1)块级作用域: 块作用域由 { }包括,let和const具有块级作用域,var不存在块级作用域。var声明的变量为全局变量。块级作用域解决了ES5中的两个问题:

  • 内层变量可能覆盖外层变量
  • 用来计数的循环变量泄露为全局变量

(4)重复声明: var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const和let不允许重复声明变量。

(6)初始值设置: 在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。

区别varletconst
是否有块级作用域×✔️✔️
能否重复声明变量✔️××
是否必须设置初始值××✔️

ES6常用语法总结

JS基础入门

JS引用位置

image.png

JSON.parse()

JSON.parse() 把一个JSON字符串,转换为JSON对象
JSON.stringify() 把一个JSON对象,转换为JSON字符串
在接收服务器数据时一般是字符串 我们可以使用 JSON.parse() 方法 将数据转换为 Json对象。

JSON.parse('{"A": "27"}')
// {A: '27'}
JSON.stringify({"B": "18"})
// '{"B":"18"}'

image.png

js深拷贝和浅拷贝的区别

浅拷贝就像是单位大家写日报,每个人都可以修改同一个文档的内容,你修改了别人也能看到。
深拷贝就像别人给你复制了一份ex表格,你俩个各自改各自的。

并发、并行、异步、同步

异步、同步是俩种不同的编程模型。 同步代表必须要等到前一个任务执行结束,才能触发下一个任务。同步中并没有并发和并行的概念 异步代表不同的任务之间并不会相互等待,也就是说你在运行任务A时,也可以运行任务B。一个典型实现异步的方式就是多线程 异步编程指我们在执行一个长时间任务时,程序不需要等待,而是继续执行后续的代码,直到这些任务完成后在通知你。这种编程模式避免了程序的堵塞,大大得提高了cpu的效率。
async``await语法糖

正则表达式

input 支持英文字母和数字,并且开头第一个只能是字母

<Form.Item {...layout} label="英文名称" name="ENname" rules={[{ required: true, validator: this.EName, message: '请填写英文字母(支持,0-9a-zA-Z_)' }]}>
    <Input placeholder="请输入英文名称" onChange={e => this.inputValue_ENname(e)} />
</Form.Item>
// 自定义校验 英文表名
EName = (_, value) => {
    const enPattern = /^[a-zA-Z][0-9a-zA-Z_]*$/ // 求只能输入英文字母的正则表达式

    if (enPattern.test(value)) {
        return Promise.resolve();
    }

    return Promise.reject('请填写以英文字母开头的表名(支持,0-9a-zA-Z_)');
}

驼峰命名法

let studentInfo = {};

// =>项目中常见的有特殊含义的端词组
add / insert / create  新增/插入/创建
del / remove / update  删除/移除/修改
select / query / get  选择/查询/获取
info 信息

JS中的数据类型

typeof 判断数据类型

  • 基本数据类型(值类型 / 原始值)

    • 数字 number
    • 字符串 string
    • 布尔 boolean
    • 空对象指针 null
    • 未定义 undefined
    • ES6新增的唯一值类型 symbol
  • 引用数据类型

    • 对象数据类型 object

      • 普通对象 {}
      • 数组对象 []
      • 正则对象 /^$/
      • 日期对象 new Date
      • 数学函数对象 Math
      • ...
    • 函数数据类型 function

// number数字类型
	let n = 10;
	n = 10.5;
	n = -10;
	n = 0;
	n = NaN; //=>NaN:not a number 非有效数字
	n = Infinity; //=>正/负无穷大  -Infinity   [ɪnˈfɪnəti]

// string字符串:基于单引号、双引号、反引号(TAB上面的撇)包起来的都是字符串
	let str = '';
	str = '19';
	str = "好好学习";
	str = `我是ES6中新增的模板字符串,有助于字符串的拼接`;
	str = '[object Object]';

// boolean布尔:true / false
	let boo = true;
	boo = false;

// 空
	let nu = null;
	nu = undefined;
	let un; //=>默认值就是undefined

// Symbol:每一个Symbol()都是一个唯一值
	let x = Symbol('珠峰');
	let y = Symbol('珠峰');
	console.log(x == y); //=>false

// object普通对象:大括号包起来,里面有零到多组属性名和属性值(键值对),这些属性名和属性值可以描述当前对象的特征(键:值,多组键值对用逗号分隔)
	let obj = {
		name: '好好学习',
		age: 10,
		teachers: 30
	};

// Array数组对象:中括号包起来,逗号分隔数组中每一项的值(每一项的值可以是任意类型)
	let arr = [10, '字符串', true, null];

// RegExp正则对象:两个斜杠包起来一大堆你看不懂的符号就是正则 O(∩_∩)O哈哈~
	let reg = /$[+-]?(\d|([0-9]\d+))(\.\d+)?^/;

// function函数
	function func(x, y) {
		let total = x + y;
		return total;
	}
	
// ES6中的 Arrow Function 箭头函数
	let fn = () => {

	};

Math 称为数学函数[取整、随机数]

// 向上取整,有小数就加1
Math.ceil(5/2)  // 3

// 向下取整,丢弃小数部分
Math.floor(5/2)  // 2

// 四舍五入
Math.round(5/2)  // 3

// 取余
5%2  // 1

// 随机数 (随机生成0 ~1之间的数)
Math.random() 
// 获取随机生成 0 到 9之间的整数
Math.floor(Math.random()*10)

JS常用方法

if条件 和 三元表达式等同写法

for循环

# Javascript循环删除数组中元素的几种方法

map。filter

forEach

var arr = [12345]
arr.forEach(function (item) {
    if (item === 3) {
        return;
    }
    console.log(item)
})
// 1
// 2
// 4
// 5

字面量,变量

6种方式给 JavaScript 数组添加元素

### slice()截取
截取数组中的部分元素

```js
// [公式] 从start位置开始,截取到end位置,end取不到
  Arr.slice(start,end)
// [例子]
  Arr = ['A', 'B', 'C', 'D', 'E']
  Arr.slice(2, 4) // ["C", "D"]

filter()筛选

创建一个新的数组,筛选新数组中符合条件的所有元素

// 返回true的留下,false的舍弃
  const Arr = [{ name: '曹操' },{ name: '刘备' },{ name: '宋江' }]
  const kiki = Arr.filter(item => item.name === '宋江')
  console.log('kiki', kiki[0]) // {name: "宋江"}

image.png

splice()

用于添加,删除,替换数组中的元素,改变原始数组。

// [公式] index起始位置,howmany删除元素的数量,item1添加到数组的新元素
array.splice(index, howmany, item1 ,....., itemX)

// [例子]-添加
const Hero = ["曹操", "曹昂", "曹丕"]
Hero.splice(2, 0, "孙权", "孙策")
console.log(Hero) // ["曹操", "曹昂", "孙权", "孙策", "曹丕"]
// [例子]-删除
const Hero = ["曹操", "曹昂", "曹丕"]
Hero.splice(1, 1)
console.log(Hero) // ["曹操", "曹丕"]
// [例子]-替换
const Hero = ["曹操", "曹昂", "曹丕"]
Hero.splice(0, 1, "曹阿瞒")
console.log(Hero) // ["曹阿瞒", "曹昂", "曹丕"]

forEach() 遍历

循环遍历里面的每一个元素

const heroDesc = [
    {name: "曹操", age: 53},
    {name: "刘备", age: 53},
    {name: "孙权", age: 23}
]
heroDesc.forEach((item, index, arr) => {
    const ele = arr[index]; 
    if(ele.age > 30){
        ele.boo = true
    } 
})
console.log(heroDesc)
// [{name: "曹操", age: 53, boo: true}, {name: "刘备", age: 53, boo: true}, {name: "孙权", age: 23}]

push()

map()、delete

map()用法1

image.png

const heroDesc = [ 
    {name: "曹操", age: 53}, 
    {name: "刘备", age: 53}, 
    {name: "孙权", age: 23} 
]
heroDesc.map(item => { 
    return item.name 
})
// 打印结果 ['曹操', '刘备', '孙权']

map()用法2 4541629886622_.pic.jpg

4551629886628_.pic_hd.jpg

split() 方法

"2:3:4:5".split(":")	//将返回["2", "3", "4", "5"]
"|a|b|c".split("|")	//将返回["", "a", "b", "c"]
var str="How are you doing today?"
document.write(str.split(" ",3))
// How,are,you

findIndex() 数组减去另一数组

www.jianshu.com/p/6ac483fa4…
介绍一下上面用到的数组实例中的find()findIndex()includes()

const arr1 = ['', '100', '120', '125', '125', '130', '130'];
const arr2 = ['', '120', '125', '125', '130'];
const arr3 = [];
arr1.forEach((a)=>{
    let c = arr2.findIndex(b =>a === b);
    if (c > -1) delete arr2[c];
    else arr3.push(a);
});
console.log(arr3)  //['100', '130']

遇到的问题

js禁用鼠标右键菜单

window.oncontextmenu = function(e){
    //取消默认的浏览器自带右键 很重要!!
    e.preventDefault();
}

// react 写法
<div onContextMenu={this.tableRight} />
// 禁止右键菜单事件
tableRight = (e) => {
    e.preventDefault()
    return false
}

js 获取URL地址附带参数 获得请求链接参数

function getParamString(name) {
    var paramUrl = window.location.search.substr(1);
    var paramStrs = paramUrl.split('&');
    var params = {};
    for(var index = 0; index < paramStrs.length; index++) {
        params[paramStrs[index].split('=')[0]] = decodeURI(paramStrs[index].split('=')[1]);
    }
    return params[name];
}
// ————————————————
// 例如URL地址是这样的
http://127.0.0.1:8020/AjaxTester/test.html?age=3&key=xiaoming
// js使用如下
alert(getParamString("key")); //提示xiaoming

JS去掉字符串前后所有空格

image.png

const uiui = "     ty787 97 99 lkjh . "
uiui.replace(/(^\s*)|(\s*$)/g, "")
// 打印结果:'ty787 97 99 lkjh .'

将字符串转成小写字母

image.png

var str = 'FeiNiaoMy.com';
console.log(str.toLowerCase());
// 打印结果:feiniaomy.com

js保留2位小数

toFixed函数来做四舍五入,函数参数里的2就是保留二位小数。

image.png

<el-progress :percentage="(item.num / DataSelectTop_TotalNum * 100).toFixed(2)" />