秋招面试 前端问题总结

小红书面试实战集锦

1、vue的优势

  • 能够进行组件化开发,使得开发效率大大提高。且有许多第三方组件库,可以进行组件化开发。

  • 可以对数据进行双向绑定

  • vue是单页面应用,使得页面局部刷新,不必每次跳转都请求所有数据和dom,提升了用户体验。

2、深拷贝 浅拷贝

首先要明白深拷贝浅拷贝的概念:

深拷贝:深拷贝会层层拷贝,对于不是基本类型的变量,也会递归到基本类型的变量之后在复制。深拷贝后的对象和原来的完全隔离,互不影响,对这个对象的改变不会影响另一个对象。

浅拷贝:浅拷贝会将对象的每个属性进行复制,但是当对象的属性值为引用类型时,实际复制的值是其引用,可以理解为指向其地址,当其值改变,引用的值也会改变。

浅拷贝的实现方式:

  • Object.assign() :将任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象

      var obj1 ={
             name:'jack',
             age:25,
             hobby:{
                 ball:'tennis'
             }
         }
         let obj2 = Object.assign({},obj1)
         obj2.hobby.ball = 'basketball'
         console.log('obj1',obj1.hobby.ball) //basketball
         console.log('obj2',obj2.hobby.ball) //basketball
      
    
    

    原对象属性中不包含引用类型的时候等价于深拷贝,因为不包含引用类型的时候是对属性值的拷贝也就是对基本类的值的复制,也就是值引用,所以对拷贝的对象改变不会影响到原对象,也就等价于深拷贝

    var obj1 ={
             name:'jack',
             age:25,
         }
         let obj2 = Object.assign({},obj1)
         obj2.name = 'rose'
         console.log('obj1',obj1.name) //jack
         console.log('obj2',obj2.name) //rose
    ​

  • 数组的浅拷贝

    1. Array.prototype.slice()

    2. Array.prototype.concat()

    3. 结构赋值

      上述3种都与正常对象相同,对引用类型指向地址,其他都如同深拷贝

       var arr = ['jack',25,{hobby:'tennise'}];
              // let arr1 = arr.slice()
              // let arr1 = arr.concat()
              arr1[2].hobby='rose'
              arr1[0]='rose'
              console.log( arr[2].hobby) //rose
              console.log( arr[0]) //jack
      ​

深拷贝的实现方式:

  • JSON.parse(JSON.stringify())

    原理就是用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈

    缺点:对象之中若是有函数,深拷贝后,函数会消失。

  • 手写深拷贝 递归函数实现

function copy(obj){
        let newobj = null;   //声明一个变量用来储存拷贝之后的内容
        
     //判断数据类型是否是复杂类型,如果是则调用自己,再次循环,如果不是,直接赋值即可,
     //由于null不可以循环但类型又是object,所以这个需要对null进行判断
        if(typeof(obj) == 'object' && obj !== null){ 
        
    //声明一个变量用以储存拷贝出来的值,根据参数的具体数据类型声明不同的类型来储存
            newobj = obj instanceof Array? [] : {};   
            
    //循环obj 中的每一项,如果里面还有复杂数据类型,则直接利用递归再次调用copy函数
            for(var i in obj){  
                newobj[i] = copy(obj[i])
            }
        }else{
            newobj = obj
        }    
        console.log('77',newobj)
      return newobj;    //函数必须有返回值,否则结构为undefined
   }
  • 借助第三方库也能实现深拷贝 如lodash

3、Get与Post的区别

  1. get将参数包含于url中,post通过request body传递参数

  2. get比post更不安全

  3. get传递的参数含有参数限制 而get比post更大

  4. get参数会被完整保留再浏览器历史记录中,而post不会。

  5. get只支持url编码,post支持多种编码方式。

4、css垂直居中的方法总结

  1. 设置line-height:

    父容器高度和子元素line-height一样的数值,再设置text-align:center,内容中的行内元素就会垂直居中。

  2. 设置伪元素::before

    给父元素添加一个伪元素::before,让这个伪元素的div高度为100%,这样其他div就可垂直居中了,但div 本身就是块级元素,而vertical-align是行内元素属性,则需要修改为inline-block

    .parent::before{
        content:'';
        display: inline-block;
        height:100%;
        vertical-align: middle;  
    }
    // 或者不用伪元素 直接给子盒子设置
    .child{
        display:inline-block;
        vertical-align:middle;
    }

  3. 绝对定位法:

    1. 子元素设置absolute,top和left相对父元素的50%,与其搭配的 transformse: translate(-50% , -50%) 表示X轴Y轴方向水平居中。

    2. 子元素设置absolute, 将上下左右的数值都设置为0,同时margin:auto

  4. display:flex法

  5. 其他如:absolute+calc | display:table-cell实现CSS垂直居中

    .child{
        background:red;
        display: table-cell;
        vertical-align: middle;
        text-align: center;
    }
    // 注意 父元素要设置为display:table

5、原型链

原型

①所有引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象 ②所有函数都有一个prototype(原型)属性,属性值是一个普通的对象 ③所有引用类型的__proto__属性指向构造函数的prototype

原型链

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的proto隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的proto中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。

6、与js相比ts好处

Typescript = JavaScript + Type

  • 在开发时就能看见部分语法错误,减少找bug的时间,提高了效率。

  • ts是js的超集,存在类型的脚本语言。 ts补充了接口、枚举等开发大型应用时JS缺失的功能,但在其中也能使用js的语言。

7、ts如何声明类型

  • 声明并指定类型: let a : number

  • 声明并赋值: let a : number = 10;

  • 函数声明: function sum(a:number, b:number) : number

  • 可以使用 “|” 来链接多个类型 : let a : string | number

  • 可以用“&”表示同时符合: let j: { name: string } & { age: number };

8、ts中type和interface区别

在 TS 中,typeinterface相似,都可以给类型命名并通过该名字来引用表示的类型。不过它们之间是存在一些差别的,我们在使用时也需要注意一些特殊场景。

  • type

    type关键字是声明类型别名的关键字,如:

    type AliasName = Type;
    // type:声明类型别名的关键字
    // AliasName:类型别名的名称
    // Type:类型别名关联的具体类型
    // 举例:
    type Shape = { name: string }
    type Circle = Shape & { radius: number }
  • interfacfe

    通过interface可以定义一个接口类型。能够合并众多的类型声明。

    interface Interfacename{
        typeName;
    }
  • 相同点:都可以用来定义 对象 或者 函数 的结构,而严谨的来说,type 是引用,而 interface是定义。

  • 不同点:

    1. interface接口名总是会直接显示在编译器的诊断信息和代码编辑器的智能提示中,而 type 的名字只在特定情况下才会显示出来——只有当类型别名表示数组类型、元组类型以及类或者接口的泛型实例类型时才展示。

      type NumericType = number | bigint;
      
      interface Circle {
        radius: number;
      }
      
      function f(value: NumericType, circle: Circle) {
        const bar: boolean = value;
        //    ~~~
        // 	  Type 'number | bigint' is not assignable to type 'boolean'
        // 		这里没有显示类型别名
        
        const baz: boolean = circle;
        // 	  ~~~
        // 		Type 'Circle' is not assignable to type 'boolean'
      }
      

    2. type 在声明类型别名之后实际上是一个赋值操作,它需要将别名与类型关联起来

9、nextTick能做什么

// nextTick是等待下次DOM更新刷新的工具方法 返回一个promise
// 第一种写法
created() {
  this.$nextTick(() => {
    // dom更新完成后,要做些什么事儿
  })
},
// 第二种写法
async created() {
  await this.$nextTick()
  // dom更新完成后,要做些什么事儿
},
使用nextTick场景一般有两种:

① 想在created中获取DOM(面试也会经常问)(created步骤中尚未开始挂载)

问:在created获取数据,mounted查不到?
解决:1、使用nextTick。2、使用定时器。3、使用监听+nextTick

② 响应数据变化后、或者通过v-if控制的DOM刚挂载,我们要获取更新后的状态 (实际项目中经常用到)

10、箭头函数和普通的区别

  1. 箭头函数没有原型,因此箭头函数本身没有this。 箭头函数的this指向在定义的时候继承自外层第一个普通函数的this(箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象))。 而普通函数本身有this和原型。

  2. 使用new调用箭头函数会报错 无论箭头函数的thsi指向哪里,使用new调用箭头函数都会报错,因为箭头函数没有constructor

  3. 箭头函数不支持new.target

    new.target是ES6新引入的属性,普通函数如果通过new调用,new.target会返回该函数的引用。

  4. 箭头函数不支持重命名函数参数,普通函数的函数参数支持重命名

    function func1(a, a) {
      console.log(a, arguments); // 2 [1,2]
    }
    
    var func2 = (a,a) => {
      console.log(a); // 报错:在此上下文中不允许重复参数名称
    };
    func1(1, 2); func2(1, 2);
    
    
  5. 箭头函数相对于普通函数语法更简洁优雅:

    • 箭头函数都是匿名函数,并且都不用写function

    • 只有一个参数的时候可以省略括号:

      var f = a => a; // 传入a 返回a
      
    • 函数只有一条语句时可以省略{}return

      var f = (a,b,c) => a; // 传入a,b,c 返回a
      

11、匿名函数

  • 什么是匿名函数?

    1、匿名函数,即没有名称的函数
    2、如果单独只写一个匿名函数,此时是不符合语法要求会报错。需要给 匿名函数包裹一个括号,使之成为表达式
    3、被小括号包裹的内容会被js识别为一个函数表达式 
    
  • 匿名函数的格式

    1. 需要执行匿名函数 后面追加括号即可 也就是立即执行函数

      (function () {
            alert('匿名函数执行方式一')
          })();

    2. 小括号将匿名函数以及执行匿名函数的小括号都包裹起来 整体构成一个表达式

       (function () {
            alert('匿名函数执行方式二')
          }());

    3. 匿名函数传参 与其他普通参数的传参方式一样,调用的时候将参数传入即可

       (function (m) {
            alert(m)
          }('这是匿名函数传进来的参数'));

12、有哪几种作用域?

  • 全局作用域

  • 函数作用域/局部作用域

    在函数内定义的变量,就是局部作用域。在局部作用域内,对外封闭。外层作用域无法直接访问内部作用域。

  • 块级作用域

    在其他编程语言中,块状作用域是很熟悉的概念,但是在JavaScript中不被支持,就像上述知识一样,除了全局作用域就是函数作用域,一直没有自己的块状作用域。在 ES6 中已经改变了这个现象,块状作用域得到普及。

  • 动态作用域:如:this

    动态作用域即在执行阶段才确定变量的作用域。JS中,动态作用域需要手动借助 bind、with、eval 等开启,即默认情况下是动态作用域。

13、为何要有这么多请求方式

常见的请求方式有get、post、put、delete

不常见的有:head、connect、options、trace、patch、move、copy、link、unlink、wrapped、lock等

不同的请求方式也有细微的差别,总体来说是为了更规范的代码理解。(restFul风格)

14、闭包的作用

  • 概念:

    一个定义在函数内部的函数,可以读取到其他函数内部变量的函数,本质上,闭包就是一个把函数内部和外部连接起来的桥梁。

  • 作用:

    1. 读取函数内部的变量

    2. 让这些变量的值始终保持在内存中。

  • 主要特点:

    1. 函数套函数:闭包一定有嵌套函数。

    2. 外层函数一定有局部变量。内层一定操纵了这个变量。

    3. 内层函数会使用return返回外部(如果不返回这个内层函数,你就没办法使用这个闭包,返回内层函数的最终的目的就是让外部可以访问到这个闭包)

  • 优缺点:

    1. 优点:不会造成变量污染,可以重复使用。

    2. 缺点:闭包的函数变量都会存在于内存之中,内存消耗巨大。 闭包会在父函数外部,改变父函数内部变量的值。

15、promise状态

promise本身是同步的,而其中的then和catch是异步,且是微任务。

  • promise对象:Js中进行异步编程的新的解决方案(传统的解决方案——回调函数和事件),用于表示一个异步操作的最终完成 (或失败), 及其结果值.。其本身是一个构造函数

    new Promise(function (resolve, reject) {
        ...    
    } /* executor */)
    // executor是带有 resolve 和 reject 两个参数的函数 。

  • 三种状态:promise有三种状态:pending(进行中)、resolved(已完成)、rejected(已失败)

    这三种情况的变化途径只有两种:pending->resolved 或者pending->rejected 并且状态一旦改变,就无法再次改变状态,这也是它名字 promise-承诺 的由来,一个promise对象只能改变一次

  • promise解决了什么问题:

    1. 回调地狱问题

      所谓回调地狱就是指把函数作为参数层层嵌套请求,这样层层嵌套,人们称之为回调地狱,代码阅读性非常差

    2. 代码可读性问题

    3. 信任问题

      回调函数不能保证什么时候去调用回调,以及使用什么方式去调用回调;而Promise一旦被确认成功或失败,就不能再被更改。 Promise成功之后仅调用一次resolve(),不会产生回调多次执行的问题。除非Promise再次调用。所以Promise很好地解决了第三方工具导致的回调多次执行(控制反转)的问题,这个问题也称为信任问题。

16、async await

  1. async

    async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。

    async function Async(){
    	return "resolved"
    }
    const result = Async()
    console.log(result)
    // 返回一个promise对象 返回的resolved值为Async return的值

  2. awite

    awite也是一个修饰符,但只能放于被async定义的函数内。

    awite修饰的如果是Promise对象,则可以获取到Promise中返回的内容(resolved、rejected),并且取到值后才会继续执行。

    如果修饰的不是Promise对象,则会把这个非Promise的东西当成表达式处理的结果。

    await 相当于 Promise 的 then ,then指的是成功,不指失败

// 使用async/await获取成功的结果

// 定义一个异步函数,3秒后才能获取到值(类似操作数据库)
function getSomeThing(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('获取成功')
        },3000)
    })
}

async function test(){
    let a = await getSomeThing();
    console.log(a)
}
test(); // 3秒后输出:获取成功

17、promise await异同点 理解

  • async和promise的关系

    async/await 是消灭异步回调的终极武器,但和Promise并不排斥,两者相辅相成,执行 async 函数,返回的是 Promsie 对象

  • 不同

    async/await对比promise不需要执行then()处理响应,只需要将结果赋给一个值即可

    async、await看起来更加简洁,使得异步代码看起来像同步代码,只有await的代码执行完毕后才会执行下面的代码,与promise一样,也是非阻塞的;

18、状态码与302 301

  • 200:表示请求成功

  • 301:表示资源被永久转移其他的url。后面的请求会用到这个新的url。

  • 302:资源临时被移动,客户端应继续使用原来的url。

  • 400:客户端语法错误

  • 403:服务器能理解请求,但是拒绝执行。

  • 404:服务器无法根据客户端找到资源。

  • 405:表示客户端请求中的方法被禁止。

  • 500:服务器内部错误,无法完成请求。

  • 502: 作为网络或者代理工作的服务器在尝试执行请求的时候,从远程收到了一个无效的响应。

19、ajax执行流程

  1. 创建XMLHttpRequest对象实例 : var xmlHttp=new XMLHttpRequest();

  2. 第二步(打开与服务器的连接) : xmlHttp.open("请求方式”,"请求URL","请求是否为异步(默认为false)")

  3. 发送请求

    1. get方式:

      xmlHttp.send(null):如果不给可能会造成部分游览器无法发送!

    2. post方式:

      xmlHttp.send("username="+username.value()):手动设置响应体

  4. 设置回调

 document.querySelector("#btn1").onclick = function () {
            //第一步:创建一个XMLHttpRequet对象
            var xmlhttp = new XMLHttpRequest();
            //第二步:告诉浏览器请求的方式是什么,以及请求发送到那
            xmlhttp.open("get", "student.xml", true);
            //第三步:设置响应服务器端数据处理
            xmlhttp.onreadystatechange = function () {
            }
            //第四步:发送请求
            xmlhttp.send()
        }

20、cookie跨域 同站可以访问cookie吗

scheme/host/port

在sop的限制下,同host,不同端口的服务之间,cookie是可以共享的。

跨站(cross-site)、跨域(cross-origin) ​

  • 以下两个域名就属于跨站,他们具有不同的二级域名pro.com和 dev.com。

    ocs.pro.com ocs.dev.com

  • 以下两个域名就属于同站,具有相同的域名test.com。

    ocs.test.com sts.test.com

当同站时,想要访问跨域,可以通过cookie的SameSite属性设置

(3条消息) 浏览器同源策略问题 - Cookie访问限制朱慧杰的博客-CSDN博客_cookie同源策略

21、map方法的原理

22、ref工作原理

在以前的js中我们都是通过设置id,然后通过document.getelementbyid(hello) 来获取dom树进行操作,方便快捷。 但是在vue框架里面,我们都是通过ref设置来获取参数的。

对于普通的元素比如button而言,他就是一个跟js一样的dom树获取,但是对于组件而言,就可以获取整个组件对象(VC),我们可以通过这种方式获取组件里面的内容进行操作

  • ref实际就是对一个普通值做了一层包装,包装成一个对象,并通过其get和set实现依赖收集和更新,其实现原理类似于computed

  • 其中有一个toRef函数,可以解析proxy对象,对单个属性形成响应式

import { isObject } from "@vue/shared";
import { reactive } from "./reactive";
import { activeEffect, trackEffects, triggerEffects } from "./effect";

export function ref(value){
    return new RefImpl(value);
}


class RefImpl {
    private _value = null;
    private __v_isRef = true;
    private dep = null;
    constructor(public rawValue){
        this._value = toReactive(rawValue) 
    }
    get value(){
        if(activeEffect){
            trackEffects(this.dep ||(this.dep = new Set()))
        }
        return this._value
    }
   set value(newValue){
        if(newValue !== this.rawValue){
            this._value = toReactive(newValue)
            this.rawValue = newValue
            triggerEffects(this.dep)
        }
   }
}


export function toReactive(value){
    return isObject(value) ? reactive(value):value
}

23、路由懒加载原理

  • 概念

    整个网页默认是刚打开就去加载所有页面,路由懒加载就是只加载你当前点击的那个模块。

    按需去加载路由对应的资源,提高首屏加载速度

  • 实现原理

    将路由相关的组件,不再直接导入了,而是改写成异步组件的写法,只有当函数被调用的时候,才去加载对应的组件内容。

24、vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式, 采用集中式存储管理应用的所有组件的状态,解决多组件数据通信

  • vuex本质是一个对象,其中包含了Store类和install方法。

    1. store类含有commit,dispatch这些方法

    2. install方法的作用是将store这个实例挂载到所有的组件上

  • vueX的双向绑定通过new Vue实现 (响应式)

  • 使用vuex的好处:

    数据存储方便,不必层层传递。且存在vuex中的数据都是响应式的。

// 简要示例
// 安装并引入后 在store/index.js 中放置具体的代码,具体如下:
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state(){
    return {
      // 就是公共的数据,所有的组件都可以直接使用
      count: 100
    }
  }
})
export default store
// 随后需要在main.js导入使用 import store from './store'
<!--调用-->
<!--标签中直接使用-->
<p>{{$store.state.属性名}}</p>
// 在逻辑中
this.$store.state.全局数据名称
// 从vuex中导入mapState函数
import { mapState } from "vuex";
// 将当前组件需要的全局数据,映射为当前组件computed属性
computed:{
    ...mapState(["name","age","属性名"])
}
// 在template中使用时直接 {{name + age}}
  • Mutation

    更改vuex的store中状态的唯一方法时提交mutation,每个mutation都有一个字符串的事件类型和一个回调函数。 这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数

    mutations在处理异步操作时,能够引起页面的响应式变化,但是 devtools 无法进行监听。

    updateInfo(state){
      setTimeout(() => {
         state.info.name = 'James'
       }, 1000);
    }

    以下为vuex+mutation的简要使用。

// 使用commit触发
const store = new Vuex.Store({
  state(){
   count:0
  },
    getters:{},
    mutations:{
        addCount(state,num){
            state.count += state.count +num
        }
    }
})
// 调用使用 如为button绑定了增加事件
this.$store.commit("account",10)
//特殊的提交风格
this.$store.commit({
    type:'account',
    count:10
})
// 使用辅助函数
methods:{
...mapMutations(["addcount"]),
btn() {
this.addcount(10)
  }
} 
  • Actions

    如果确实需要进行一些异步操作,比如网络请求,建议在 Actions 中进行处理,这样 devtools 就能够进行跟踪,由 Actions 处理异步操作,具体的函数部分仍交由 Mutations 进行处理。

new Vuex.store({
  // 省略其他...
  actions: {
    // context对象会自动传入,它与store实例具有相同的方法和属性,等同于store
    action的名字: function(context, 载荷) {
      // 1. 发异步请求, 请求数据 如
        setTimeout(() => {
            // 2. commit调用mutation来修改数据
            // 3. context.commit('mutation名', 载荷)
        context.commit('updateInfo',载荷)
      }, 5000);
        // 还可以结合promise
        return new Promise((resolve, reject)=>{
        setTimeout(() => {
          context.commit('updateInfo',载荷);
          resolve('11111')
        }, 1000);
      })
    }
  }
})

调用格式:

1、直接使用:在组件中通过`this.$store.dispatch('actions的名字', 参数)`来调用action
	Promise异步调用

	this.$store
      .dispatch('aUpdateInfo','参数')
      .then(res =>{
        console.log('里面完成了提交');
        console.log(res);
      })

2、还可以基于 Vuex 提供的 mapActions 辅助函数,可以方便的把 Store 中指定的 Action,映射为当前组件的 methods
  • Getters

    类似于计算属性,在数据展示前进行一些变化处理,具有缓存功能,能够提高运行效率

 getters:{
      //  使用时,通过:$store.getters.powerCounter 获取:
    powerCounter(state){
      return state.counter * state.counter
    },
        // 需要参数时,调用时传入参数即可:如:
        // $store.getters.moreAgeStu(18)
    moreAgeStu(state){
      return function(age){
         // 筛选student中>传入的age的项
        return state.students.filter(s => s.age > age)
      }
    }
  }

25、如何避免回流重绘

一、 CSS中避免回流、重绘

1.尽可能在DOM树的最末端改变class 2.避免设置多层内联样式 3.动画效果应用到position属性为absolute或fixed的元素上,脱离文档流 4.避免使用table布局 5.多用visibility:hidden少用display:none 6.使用css3硬件加速,可以让transform、opacity、filters等动画效果不会引起回流重绘 二、 JS操作避免回流、重绘

1.避免使用JS一个样式修改完接着改下一个样式,最好一次性更改CSS样式,或者将样式列表定义为class的名称 2.避免频繁操作DOM,使用文档片段创建一个子树,然后再拷贝到文档中

3.避免循环读取offsetLeft等属性,在循环之前把它们存起来 4.对于复杂动画效果,使用绝对定位让其脱离文档流,否则会引起父元素及后续元素大量的回流

26、document.target和document.currentTaeget区别

其实就相当于e.target和e.currentTarget

  • e.target返回触发事件的元素

  • e.currentTarget返回绑定事件的元素

  <script>
  var ul = document.getElementById("ul");
  ul.onclick = function(event){  //event.currentTarget----绑定事件的对象元素ul
     var tar = event.target;  //触发事件的对象元素
     var tagName = tar.nodeName.toLowerCase();
     console.log("你点击了:"+tagName);
     event.preventDefault();
 }
   </script>
// e.target就是触发事件的标签,触发谁就是谁
// e.currentTarget就是绑定事件的标签,绑定哪个事件输出的就是该事件

27、事件循环机制eventloop

Event Loop即事件循环,是解决javaScript单线程运行阻塞的一种机制。

  • 同步任务和异步任务

    1. 同步任务 即为立即执行的任务, 调用立即得到结果 。 只有前一个任务执行完毕,才能执行后一个任务;

    2. 异步任务 不会立即执行的任务, 调用无法立即得到结果 。

      而异步任务又分为宏任务与微任务。

      img

  • 执行机制

    • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

    • 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

    • 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

    • 主线程不断重复上面的第三步

  • 其他补充

    • 宏任务队列可以有多个,而微任务队列只能有一个

    • 宏任务:

      scriptsetTimeoutsetIntervalsetImmediate(Node.js)I/OpostMessageMessageChannelUI rendering

    • 微任务:

      process.nextTick(Node.js)promise.then()promise.catch()MutationObserver

    • 宏任务微任务执行

      首先在 宏任务 的队列中取出第一个任务,执行完毕后取出 微任务 队列中的所有任务顺序执行;之后再取 宏任务,周而复始,直至两个队列的任务都取完。

28、line-height 如何继承

  • line-height的设置

    一般情况下,浏览器默认的line-height为1.2。可以自定义 line-height 覆盖这个初始值,那么该怎样设置line-height呢?有以下5种方式:

    描述
    normalnormal
    number设置数字,此数字会与当前的字体尺寸相乘来设置行间距,即number为当前font-size的倍数。
    length设置固定的行间距。
    %基于当前字体尺寸的百分比行间距。
    inherit规定应该从父元素继承 line-height 属性的值。
  • line-height的继承

    1. line-height属性值有单位或者百分比的情况 : 继承的值则是换算后的一个具体的px级别的值。例:

      百分比情况:
      假设自身的 font-size 为16px,line-height 设为120%。那么其行高为:16 * 120% = 19.2px。
      即 line-height 是根据自身的 font-size 计算出来的。 即父元素line-height计算后的最终值。
      具体的px:
      假设设置 line-height 为20px,那么该行的该行的行高就是20px,与 font-size 无关,不会随着 font-size 做相应比例的缩放。 这个长度值(20px)会被后代元素继承,除非后代元素设定 line-height 。
      
    2. line-height属性值没有单位的情况 : 浏览器会直接继承这个”因子(数值)”,而非计算后的具体值,此时它的line-height会根据本身的font-size值重新计算得到新的line-height 值

29、rem和em区别

  • em:

    是一个相对单位。相对于当前对象内文本的font-size,如果当前文本的字体尺寸没有设置,则相对于浏览器的默认字体尺寸。即1em=16px

    1. em的值不是固定的

    2. em会继承父元素的字体大小

  • rem:

    是一个相对单位。是相对HTML根元素。

    1. rem为元素设定字体大小的时候,是相对于根元素进行计算的。

    2. 当我们改变根元素下的字体大小时,下面的大小都会改变

    3. 通过rem既可以做到只修改根元素就可以成比例的调整所有字体,又可以避免字体大小逐层复合的连锁反应。

  • 区别

    1. rem是相当于根元素计算,而em是相对于当前元素或者父元素的字体大小。

    2. rem不经可以设置字体,还支持设置元素宽高等属性。

    3. em因为相对于父元素换算,层级越高换算越复杂,而rem相当于根元素,换算简单,避免层级关系。

30、BFC

BFC 直译为"块级格式化上下文"。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。bfc的布局规则:

  • 内部的box会在垂直方向一个一个放置,垂直方向距离由margin决定,同一个bfc的两个相邻box的margin会重叠。

  • bfc的区域不会和float box重叠,bfc内部的额元素与外面的互不影响。

  • 计算bfc高度时,浮动元素也参与计算。

bfc的作用:

  • 可以利用bfc避免margin重叠。

  • 利用bfc制作自适应两栏布局。

  • 清除浮动。

如何设置bfc

  • float不为none

  • position的值不为static或relative

  • display的值为:inline-block、table-cell、flex、inline-flex

  • overflow的值不为visible

31、动态侧边栏

32、FormData

FormData的主要用途:

  • 将form表单元素的那么与value进行组合,实现表单数据的序列化,从而减少表单元素的拼接,提高工作效率。

  • 异步上传文件

    注意: 所有的输入元素都需要有name属性,否则无法访问到值。

    <form id="myForm" name="myForm">
      <div>
        <label for="username">Enter name:</label>
        <input type="text" id="username" name="username">
      </div>
      <div>
        <label for="useracc">Enter account number:</label>
        <input type="text" id="useracc" name="useracc">
      </div>
      <div>
        <label for="userfile">Upload file:</label>
        <input type="file" id="userfile" name="userfile">
      </div>
    <input type="submit" value="Submit!">
    </form>
    vr formData = new For

mData($("#myForm")[0]);
var name = formData.get("username"); // 获取名字
var psw = formData.get("useracc"); // 获取账户
// 当然也可以在此基础上,添加其他数据
formData.append("token","kshdfiwi3rh");

相关资料: (3条消息) FormData详解夜光下丶的博客-CSDN博客formdata

33、mvc

MVC(Model View Controller)是软件工程中的一种软件架构模式,它把软件系统分为模型视图控制器三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

M:Model,模型。 JavaBean

  • 完成具体的业务操作,如:查询数据库,封装对象

V:view视图。JSP、HTML等来进行数据展示

C:Controller控制器。 Servlet

34、js设计模式-几种简单的

(3条消息) JS的几种设计模式_前端魔法师的博客-CSDN博客

  1. 单例模式

  2. 组合模式

  3. 观察者模式

  4. 发布/订阅模式

35、koa

koa是基于node.js平台的web开发框架,致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。

36、手写继承

在js中,继承的方法有以下几种:

  • 构造函数继承

    function Parent() {
        this.name = 'parent';
    }
    function Child(age) {
        Parent.call(this);
        this.age = age;
    }
    var c = new Child(12);
    console.log(c.name); //输出parent

    其原理是在child构造函数中利用call改变了this的指向,使得Child对象增加了name属性,完成了继承。

    缺点: 父函数原型链上的东西不能被继承

  • 原型链继承

    function Parent() {
        this.name = 'parent';
    }
    Parent.prototype.say = function () {
        console.log('say');
    };
    function Child(age) {
        this.age = age;
    }
    Child.prototype = new Parent();
    var c = new Child(12);
    console.log(c.name); //输出parent
    c.say() //输出say

    原型链继承是直接让Child构造函数的prototype直接指向Parent对象,这样Parent的东西Child对象可以直接从它的原型链上找到。

    缺点: 当创建多个实例时,如果不同实例可能互相存在影响

  • 构造函数+原型链继承组合

    function Parent() {
        this.name = 'parent';
        this.arr = [1,2,3,4]
    }
    Parent.prototype.say = function () {
        console.log('say');
    };
    function Child(age) {
        Parent.call(this); 
        this.age = age;
    }
    Child.prototype = new Parent();
    // 优化写法:寄生式组合继承
    // Child.prototype = Object.create(Parent.prototype);
    // Child.prototype.constructor = Child;
    var c1 = new Child(12);
    var c2 = new Child(12);
    console.log(c1.arr); //[1,2,3,4]
    console.log(c2.arr);//[1,2,3,4]
    c1.arr.push(5);
    console.log(c1.arr); //[1,2,3,4,5]
    console.log(c2.arr); //[1,2,3,4]

    这种继承在实例创建时就有了Parent里的属性,这样就不会去Parent寻找属性,修改的属性就是子类中自己的属性,就不会产生各个实例的影响,同时也继承了Parent原型链上的东西。

    注意:只要使用了原型链继承的都存在constructor丢失的问题

37、手写节流防抖

  1. 节流:在设定的规定时间内,函数只触发一次。后面的触发需要等待冷却时间结束。可以理解为等待冷却时间。

     // fn:要被节流的函数  delay:间隔时间
        function throttle(fn, delay) {
            let lastTime = 0; // 记录上一次的函数触发时间
     
            return function() {
                // 记录本次的函数触发时间
                let nowTime = Date.now(); 
                if (nowTime - lastTime > delay) {
                    return fn.apply(this); //修正this指向
                    // apply是绑定this到指定函数或类,也可以说把函数或者类的方法和属性给到当前作用域
                    lastTime = nowTime;
                }
            }
        }
        document.onscroll = throttle(function() {
            console.log('throttle函数执行了' + Date.now());
        }, 600)

  2. 防抖:在规定时间内,每次触发重新计时,只让函数最后一次的触发生效,可以理解为连续回城只有最后一次成功。

     function debounce(fn, delay) {
            var timer = null;
            return function() {
                clearTimeout(timer);
                timer = setTimeout(function() {
                    fn.apply(this);
                    // apply是绑定this到指定函数或类,也可以说把函数或者类的方法和属性给到当前作用域
                }, delay);
            }
        }
        document.onscroll = debounce(function() {
            console.log('debounce函数执行了' + Date.now());
        }, 1200)

38、手写一个函数 判断某个字符串中出现次数最多的字符

  • 核心算法:

    先利用 charAt()遍历这个字符串,把字符串里的每个字符都存放在一个变量j里面;然后把每个字符都存储给对象, 如果对象没有该属性,就为1,如果存在了就 +1;这样就能把每一个字符出现的次数都找出来 最后再遍历对象,得到最大值和该字符。

<script>
var str = "abcoefoxyozzopp";
var obj={} //创建一个没有属性的对象
for(i=0;i<str.length;i++)
{
    var j=str.charAt(i); 
    // j得到的是  第一次是j=a,第二次循环j=b,遍历字符串的每一个字符
    //这里要注意调用对象属性
    //object['属性名']这是一种调用方式 
    // object.属性名 这是第二种调用方式
  if(obj[j]) {
  // 因为j=str.charat(i); 
  // j返回的值是带引号的值,故调用属性直接用obj[j]
  // 如果有obj.j属性名的话 就+1次数 即字符出现的次数
    obj[j]++;
} else
	obj[j]=1;//如果没有obj.j这个属性名的话,就赋值1给obj.j这个属性
}
console.log(obj);
        // 2. 遍历对象
        var max = 0;
        var ch = '';
        for (var key in obj) {
            // key 得到是 属性名
            // obj[key] 得到的是属性值
            if (obj[key] > max) {
                max = obj[key];
                ch = key;
            }
        }
        console.log('最多的字符是' + ch+'出现的次数为'+max+'次');
    </script>

39、echarts大屏适配如何实现

大屏适配解决方案 - 掘金 (juejin.cn)

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页
评论

打赏作者

怒写一堆屎

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值