八股文

2023/5/9

# 1.cookie-session-local

共同点:都是浏览器存储

不同点: 1. cookie由服务器写入, sessionStorage以及localStorage都是由前端写入

2. cookie的生命周期由服务器端写入时就设置好的,localStorage是写入就一直存在,除非手动清除,sessionStorage是由页面关闭时自动清除

3. cookie存储空间大小约4kb, sessionStorage及localStorage空间比较大,大约5M

4. 3者的数据共享都遵循同源原则,sessionStorage还限制必须是同一个页面

5. 前端给后端发送请求时,自动携带cookie, session 及 local都不携带

6. cookie一般存储登录验证信息或者token,localStorage常用于存储不易变动的数据,减轻服务器压力,sessionStorage可以用来监测用户是否是刷新进入页面,如音乐播放器恢复进度条功能

# 2.Js数据类型

# 基本数据类型和引用数据类型

基本数据类型:number,string,boolean,bigint,undefined,null,symbol

引用数据类型:object(普通对象,数组,正则,日期,Math数学函数)

# 3.闭包

1.内层函数引用外层函数中变量,这些变量的集合就是闭包

2.通过作用域链,当前作用域可以访问上级作用域中的变量

3.解决的问题:保存变量,保护变量

4.带来的问题:内存泄露

5.块级作用域

# 4.call、apply、bind

首先,call apply bind三个方法都可以用来改变函数的this指向,具体区别如下:

1、fn.call (newThis,params) call函数的第一个参数是this的新指向,后面依次传入函数fn要用到的参数。会立即执行fn函数。

2、fn.apply (newThis,paramsArr) apply函数的第一个参数是this的新指向,第二个参数是fn要用到的参数数组,会立即执行fn函数。

3、fn.bind (newThis,params) bind函数的第一个参数是this的新指向,后面的参数可以直接传递,也可以按数组的形式传入。 不会立即执行fn函数,且只能改变一次fn函数的指向,后续再用bind更改无效。返回的是已经更改this指向的新fn

手撕:

//call
Function.prototype.mycall = function(target,...args){
	target = target || window;
    const targetKey = symbol();
    target[targetkey] = this;
    const res = target[targetkey].(...args);
    delete target[targetkey]
    return res
}
//apply
Function.prototype.mycall = function(target,args){
	target = target || window;
    const targetKey = symbol();
    target[targetkey] = this;
    const res = target[targetkey].(...args);
    delete target[targetkey]
    return res
}
//bind
Function.prototype.mycall = function(target,...args){
	target = target || window;
    const targetKey = symbol();
    target[targetkey] = this;
    const res = target[targetkey].(...args);
    delete target[targetkey]
    return res
}

# 5.水平垂直居中

注意:父元素需要高度

父元素{
display:flex;
}
子元素{
margin:auto;
}

# 6.margin和padding

margin:外边距,作用于外部对象

padding:内边距,作用于内部对象

# 7.vw和百分比

vw,视图窗口的宽度

百分比,继承于父元素的宽度

# 8.浏览器小字体

{
transform:scale(0.5)//缩放
-webkit-transform:scale(0.5)
}

# 9.es6新特性

# 1.let/const/var

三者都是定义变量

const声明是常量,不可以更改,有块级作用域,没有变量提升,可以重复声明

let声明的是变量,可以更改,有作用域,没有变量提升,不可以重复声明

var声明的变量会提升到作用域的最顶部,可以更改,没有作用域,可以重复声明

# 2.箭头函数

1.箭头函数是匿名函数,没有自己的this,this也不可以改变,call、apply、bind也不可以

2.没有原型prototype,_ _ proto _ _,arguments

3.不能作为构造函数(不能使用new创建对象)

# 3.新增的数组API和对象API

新增数组API:

  1. arr.flat 数组扁平化
  2. Array.from() 将数组、类数组转为数组
  3. Array.of 将数值转为数组
  4. find() 找到符合要求的第一个元素值
  5. findIndex() 找到符合要求的第一个元素的下标
  6. fill() 填充一个数组
  7. includes() 数组是否包含某特定值
  8. Array.isArray() 判断是否是数组

新增对象API

  1. Object.assign() 将可枚举属性从一个对象复制到另一个对象,浅拷贝,redux中使用
  2. Object.freeze(obj) 冻结对象
  3. Object.keys 返回自身可枚举属性,不包括原型链上的
  4. Object.entries() 返回对象可枚举键值对

# 4.新增新特性

  • let const
  • 块级作用域
  • 箭头函数
  • class
  • 模板字符串
  • 展开运算符
  • 结构赋值
  • 新增的数组API和对象API
  • promise
  • proxy
  • Symbol、bigInt
  • Set、Map/WeapMap

# 10.防抖节流

# 1.防抖

由于触发事件过于频繁,需要最后一次事件触发之后才执行相关的操作,防止多次触发造成性能的损耗。

let inp = document.querySelector("input")
//调用
inp.input = debunce(function(){
    console.log(this.value)
},500)

//封装防抖函数
function debunce(fn,t){
    let timer = null; //设置定时器
    return function(){
    if(timer !== null){
        //判断是否为最后一次定时器
        clearTimeout(timer);
    } 
    timer = setTimeout(()=>{
        fn.call(this) //改变this指向为inp
    },t)
}
}

# 2.节流

由于触发事件过于频繁,需要减少触发次数,防止多次触发造成性能的损耗。

window.onscoll = throttle(function(){
     console.log('处理事件')
},500)

function throttle(fn,t){
    return function(){
		let flag = false
        if(flag){
            setTimeout(()=>{
                fn.call(this)
            },t)
            flag = true
        }
   		 flag = false;
	}
}

# 11.防止button多次点击

  • css设置 pointer-eventsnone
  • 增加变量控制,当变量满足条件时才执行点击事件的后续代码
  • 如果按钮使用 button 标签实现,可以使用 disabled 属性
  • 加遮罩层,比如一个全屏的loading,避免触发按钮的点击事件

# 12.判断设备

  • navigator.userAgent

    if (/Mobi|Android|iPhone/i.test(navigator.userAgent)) {
      // 当前设备是移动设备
    }
    // 另一种写法
    if (
      navigator.userAgent.match(/Mobi/i) ||
      navigator.userAgent.match(/Android/i) ||
      navigator.userAgent.match(/iPhone/i)
    ) {
      // 当前设备是移动设备
    }
    
  • window.screen,window.innerWidth屏幕宽度

  • window.orientation侦测屏幕方向

  • touch触控事件

  • CSS 通过 media query(媒介查询)为网页指定响应式样式

# 13.map、forEach、filter

1.map和filter函数的参数,是完全相同的

array.map(function(currentValue,index,arr), thisValue)

array.filter(function(currentValue,index,arr), thisValue)

  • currentValue:数组元素;
  • index:索引
  • arr:原数组;
  • thisValue:作为该执行回调时使用,传递给函数,用作 "this" 的值

2.forEach的参数

array.forEach(function(currentValue,index,arr))

  • currentValue:数组元素
  • index:索引
  • arr:原数组

3.用途

  1. map方法返回的新数组是原数组的映射,何为映射?就是和原数组的长度相同,数值做相应处理。
  2. filter方法返回的值是过滤原数组后的新数组,和原数组长度不同,数值不变。不改变原数组
  3. forEach()常用于遍历数组,用于调用数组的每一个元素,并将其传递给回调函数。传输函数不需要返回值。

# 14.深拷贝、浅拷贝

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝,如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就 是内存地址。

深拷贝,开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

实现:

  1. 浅拷贝:
  • Object.assign
  • Array.prototype.slice(), Array.prototype.concat()即slice()和concat()
  • 使用拓展运算符实现的复制
  1. 深拷贝:
  • _.cloneDeep()
  • jQuery.extend()
  • JSON.stringify()
  • 手写循环递归

# 15.小程序组件生命周期

  • created --被创建时
  • attached --进入页面节点树时
  • ready-- 视图层布局完成后
  • moved --被移动到节点树另一个位置时执行
  • detached --被从页面节点树移除时执行
  • error-- 组件方法抛出错误时

# 16.this

  1. 在函数体中,非显示或隐式地简单调用函数时,在严格模式下,函数内的this会被绑定到undefined上,在非严格模式下会被绑定到window/global上。
  2. 一般使用new方法调用函数时,构造函数内的this会被绑定到新创建的对象上。
  3. 一般通过call、bind、apply方法显式调用函数时,函数体的this会被绑定到指定参数的对象上。
  4. 一般通过上下文对象调用函数时,函数体内的this会被绑定到该对象上。
  5. 在箭头函数中,this的指向是由外层作用域(函数或者全局)来决定的。

# 17.Ajax和Axios的区别?

ajax特点::

​ 1.异步的XML (opens new window)和JavaScript ​ 2.可以运行浏览器 不可以运行在node (opens new window) ​ 3.发送给后台数据需要手动转换 请求头手动设置 ​ 4.后端响应的数据需要自己转换/json格式

*axios (opens new window)特点: 1.基于promise (opens new window)的http库 2.可以调用promise的api (opens new window) 3.axios默认发送就是get请求 发送数据默认格式json 4.axios请求头的数据格式会自动转换

# 18.判断数据类型

  • 1.typeof

    typeof null -- object
    typeof undefined -- undefined
    typeof [] -- object
    typeof Symbol('symbol') -- Symbol
    
  • 2.instanceof:a instanceof b判断的是, a是否为 b实例,即a 原型链上是否存在 b的构造函数。

    //递归实现
    function instanceof(L,f){
        if(typeof L !== 'object'){
            return false
        }
        while(true){
            if(L === null){
                //遍历到顶端
                return false
            }
            if(L.__proto__ === R.prototype){
                return true
            }
            L = L.__proto__
        }
    }
    
  • 3.Object.Prototype.toString().call('xxx')

  • 4.constructor

    对于 undefine 、null, 如果尝试读取其 constructor 属性,则会报错,并且 constructor
    返回的是构造函数本身,一般使用它来判断数据类型的情况并不多见
    

# 19.函数参数传递

• 函数参数为基本数据类型时,函数内复制了一份参数值,任何操作都不会影响原参数的实际值

• 函数参数是引用类型时,当在函数体内修改这个值的某个属性值时,将会对原来的参数进行修改

• 函数参数是引用类型时, 如果直接修改这个值的引用地址,则相当与在函数体内新创建了一个引用, 何操作都不会影响原参数实际值

# 20.Promise

# 1.含义

  • Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。
  • 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

# 2.特点

  • 对象的状态不受外界影响

    Promise对象代表一个异步操作,有三种状态:

    pending(进行中)、fulfilled(已成功)和rejected(已失败)。

    只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果

    Promise对象的状态改变,只有两种可能:

    pending变为fulfilled和从pending变为rejected

    只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

    如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。

    这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

# 3.缺点

  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 当处于pending状态时,无法得知目前进展到哪一个阶段。

# 4.手写

class Promise {
  constructor(executor) {
    // 定义 Promise 的初始状态为 pending
    this.state = "pending";
    // 定义 Promise 完成后的值和拒绝的原因
    this.value = undefined;
    this.reason = undefined;
    // 定义两个回调函数列表,分别用于处理 onFulfilled 和 onRejected 回调函数
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    // 定义 resolve 和 reject 方法
    const resolve = (value) => {
      // 只有当 Promise 状态为 pending 时才可以转为 fulfilled 状态
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        // 执行 onFulfilled 回调函数列表中的所有回调函数
        this.onFulfilledCallbacks.forEach((callback) => callback(value));
      }
    };

    const reject = (reason) => {
      // 只有当 Promise 状态为 pending 时才可以转为 rejected 状态
      if (this.state === "pending") {
        this.state = "rejected";
        this.reason = reason;
        // 执行 onRejected 回调函数列表中的所有回调函数
        this.onRejectedCallbacks.forEach((callback) => callback(reason));
      }
    };

    try {
      // 在 Promise 构造函数中执行 executor 函数,并将 resolve 和 reject 方法作为参数传递给它
      executor(resolve, reject);
    } catch (error) {
      // 如果执行 executor 函数时发生错误,则将 Promise 状态转为 rejected 状态,并将错误作为原因
      reject(error);
    }
  }

  // 定义 then 方法
  then(onFulfilled, onRejected) {
    // 创建一个新的 Promise 对象
    const newPromise = new Promise((resolve, reject) => {
      // 定义 onFulfilledCallback 回调函数
      const onFulfilledCallback = (value) => {
        try {
          // 如果 onFulfilled 回调函数存在,则执行它
          const result = onFulfilled(value);
          // 如果 onFulfilled 回调函数返回的是 Promise 对象,则等待该 Promise 对象完成后再继续执行
          if (result instanceof Promise) {
            result.then(resolve, reject);
          } else {
            // 否则,将该值作为新的 Promise 对象的值,并将新的 Promise 对象状态转为 fulfilled
            resolve(result);
          }
        } catch (error) {
          // 如果执行 onFulfilled 回调函数时发生错误,则将新的 Promise 对象状态转为 rejected,并将错误作为原因
          reject(error);
        }
      };

      // 定义 onRejectedCallback 回调函数
      const onRejectedCallback = (reason) => {
        try {
          // 如果 onRejected 回调函数存在,则执行它
          const result = onRejected(reason);
          // 如果 onRejected 回调函数返回的是 Promise 对象,则等待该 Promise 对象完成后再继续执行
          if (result instanceof Promise) {
            result.then(resolve, reject);
          } else {
            // 否则,将该值作为新的 Promise 对象的值,并将新的 Promise 对象状态转为 fulfilled
            reject(result);
          }
        }

# 21.数组方法

# 1.reduce简介

语法:

arr.reduce(callback(accumulator, currentValue[, currentIndex[, array]])[, initialValue]);

其中callback可以接受四个参数:

  • accumulator:累加器,它存储了上一次回调函数的返回值,或者是 initialValue 的值(如果指定了的话)。
  • currentValue:当前数组元素的值。
  • currentIndex:当前数组元素的索引
  • array:原数组
  • initialValue:指定的初始值,如果不指定 initialValue,那么累加器的初始值将会是数组中的第一个元素。

# 2.reduce技巧

//1.计算元素出现的次数
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const fruitsCount = fruits.reduce((accumulator,currentValue)=>{
    accumulator[currentValue] = (accumulator[currentValue] || 0) +1;
    //通过 accumulator[currentValue] 来获取对象中指定键名对应的值。
    //如果这个键名不存在,则使用逻辑或运算符 || 来设置默认值为 0。
    //然后将这个值加 1,表示出现次数加 1。
    //最后将更新后的值重新赋值给对象的属性 accumulator[currentValue],实现累加统计的效果。
    return accumulator;
},{})

//2.数组扁平化
const array = [[1,2],[2,3],[4,5]];
const flatArray = array.reduce((accumulator,currentValue)=>{
    accumulator = [...accumulator,...currentValue]
    return accumulator;
},[])

//3.条件分组(根据age)
const people = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 35 },
  { name: 'David', age: 25 },
  { name: 'Emily', age: 30 }
];
const groupedPeople  = people.reduce((accumulator,currentValue)=>{
    const key = currentValue.age
    if(!accumulator[key]){
        accumulator[key] = []
    }
    accumulator[key].push(currentValue) 
    return accumulator
},{})

//4.数组合并
const keys = ['name', 'age', 'gender'];
const values = ['Alice', 25, 'female'];
const person = keys.reduce((accumulator, currentValue, index) => {
    accumulator[currentValue] = values[index];
    return accumulator;
}, {});

# 3.reduce手写

function myReduce(array, callback, initialValue) {
  // 如果提供了初始值,则将它赋值给累加器;否则,将第一个元素赋值给累加器
  let accumulator = initialValue === undefined ? undefined : initialValue;

  // 遍历数组中的每个元素
  for (let i = 0; i < array.length; i++) {
    // 如果累加器有值,则将它与当前元素一起传递给回调函数计算,并将结果赋值给累加器
    if (accumulator !== undefined) {
      accumulator = callback(accumulator, array[i], i, array);
    } else {
      // 如果累加器没有值,则将当前元素赋值给累加器
      accumulator = array[i];
    }
  }

  // 返回最终的累加器值
  return accumulator;
}