wncf's blog wncf's blog
首页
书签
  • JavaScript
  • vue
  • css
  • 收藏正则
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)

wncf

编写代码并热爱生活
首页
书签
  • JavaScript
  • vue
  • css
  • 收藏正则
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)
  • JavaScript总结

    • javascript面试题
      • 数据类型判断
      • js一些技巧
      • 正则表达式
      • 常用的数组方法
      • 对象常用的方法
      • typeScript
      • js常用库
      • 浏览器常用api
      • js常用工具函数
      • 三级目录

    • css总结

    • 其他

    • 前端
    • JavaScript总结
    wncf
    2022-02-09
    目录

    javascript面试题

    # 深浅拷贝

    1. 浅拷贝

      • 浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝

      • 如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址

        浅拷贝的api有

        • Object.assign
        • Array.prototype.slice()
        • Array.prototype.concat()
    2. 深拷贝

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

      • 常见的深拷贝方式有:

        • _.cloneDeep()

        • JQuery.extend()

        • JSON.stringify() 但是这种方式存在弊端,会忽略undefined、symbol和函数

        • 循环递归

          <script>
                  let person = {
                      name: "false",
                      age: "38",
                      course: [
                          "react",
                          "uni-app",
                      ],
                      book: {
                          bname: "如何增发",
                          price: "99"
                      }
                  }
                  function deepClone(oldObj) {
                      // if arry or obj? 创建对象格式的空值
                      let newObj = oldObj instanceof Array ? [] : {}
                      // 遍历对象或者数组 使用 for in
                      for (let key in oldObj) {
                          // 判断当前遍历的是否为对象
                          if (typeof oldObj[key] === 'object') {
                              // 如果是对象递归进行拷贝
                              newObj[key] = deepClone(oldObj[key]);
                          } else {
                              // 否则把属性直接拷贝到新对象
                              newObj[key] = oldObj[key]
                          }
                          // 循环结束后,返回新对象
          
                      }
                      return newObj
                  }
                  let p = deepClone(person)
                  console.log(p);
              </script>
          
    3. 区别

      • 浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象
      • 但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象
    4. 总结

      前提为拷贝类型为引用类型的情况下:

      • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
      • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址

    # 构造函数

    1. 什么是构造函数

      描述同一类型的所有对象的统一结构的函数

      通用new 函数名 来实例化对象的函数叫做构造函数,构造函数名为大写开头

    2. 有什么用

      构造函数是为了对初始化的对象添加属性和方法用的

    3. 场景

      为何使用:

      • 用{}一次只能创建一个对象。 如果想创建多个相同结构的对象时, 代码就会很多重复! ——极其不便于将来的维护。

      只要想反复创建多个相同结构,只是内容不同的对象时,都用构造函数。

    4. 代码演示

       function 类型名(形参1,形参2,形参3){
      	this.属性名=形参1;
      	this.xxx=xxx;
      this.方法名=function(){}
      }
      let 对象名= new 类型名(实参值1,实参值2,...)
      

    # 原型与原型链

    1. 原型:

      1. 什么是原型、

        替所有实例化的对象集中保存共有属性值和方法的父对象

    2. 原型链

      1. 是由多级父对象逐级继承形成的链式结构

        • 保存着: 一个对象可用的所有属性和方法

        • 控制着: 属性和方法的使用顺序:

        就近原则: 先子级后父级

    3. 重写

      • 是什么:在子对象中定义一个和父对象中成员同名的自有成员、
      • 何时:从父对象继承来的个别成员不好用时,就可以在子对象中定义同名成员,来覆盖父对象中的同名成员。

    # this指向问题

    this指向谁是在函数调用时指定的,this保存在函数作用域对象中

    1. 普通函数中的this==>window
      1. 严格模式下的函数this==>undefined
    2. 定时器中的this==>window
    3. 自调用函数中的this==>window
    4. 方法中的this==>.前对象
    5. 构造函数中的this==>实例化后的对象
      1. 原型对象中的this==实例化后的对象 :此处不能为箭头函数,箭头函数this指向外部作用域

    # 改变this指向方法

    1. call

      调用一次,临时替换一次this;可传递多个参数;

      函数.call(要替换this的对象,实参,参数n)
      
      1. 自动调用该函数
      2. 将函数的this替换为该对象
      3. 传递实参
      fun2.call(lilei, ...["miku", "gumi"]) //使用call传递多个参数时,如果为数组,需要使用扩展运算符数组打散
      
    2. apply

      apply的实参值是放在数组中的(必须为数组),自动拆散数组,提供this,也是临时替换

      apply(lilei,['miku','gumi'])
      
    3. bind

      创建函数副本,永久绑定this,

      var 新函数=原函数.bind(替换this的对象,实参1,实参n) 
      

      执行过程:

      1. 创建一个和原函数一模一样的新函数副本
      2. 永久替换新函数种的this为指定对象
      3. 永久替换部分形参变量为固定的实参值
    4. 如何选择(对比与区别)

      1. 只在一次调用函数时,临时替换一次this: call
      2. 既要替换一次this,又要拆散数组再传参: apply
      3. 创建新函数副本,并永久绑定this: bind

    # null与undefined

    • undefined表示 “无”的原始值

      • let a; //已经声明,未赋值
        
      • let obj ={}
        console.log(obj.name) //对象的某个属性不存在
        
      • function fn(a,b){
        console.log(a,b) //函数调用时缺少参数
        }
        fn(1)
        
      • function fn(){
            console.log('hellow')
        }
        console.log(fn()) //函数的默认返回值
        
    • null 表示 “无” 对象 0

      • //手动释放内存
        let obj = {}
        obj = null
        
      • 作为函数的参数(不是对象)

      • 原型链的顶端

    # 闭包

    # 闭包是什么

    函数里面返回一个函数,这个函数又使用了外层函数的变量

    # es6新特性

    # 1. let const 与var

    let

    为啥会出现let代替var?

    • var声明提前,扰乱正常的代码执行顺序

    • var变量覆盖,同名变量会覆盖之前定义的

    • var没有块级作用域,代码块内的变量会超出代码块范围,影响外部的变量

    let的优点:

    • 不会被声明提前-保证程序顺序执行
    • 让程序块,也变成了块级作用域-保证块内的变量,不会影响块外的变量。

    const

    • 用于定义常量,在定义后变量的值/地址不能改变

    • const声明后必须赋值

    • 没有变量提升

    1. 模板字符串

      是什么

      • 模板字符串是 支持换行、动态拼接内容的特殊字符串格式

      ${}中可以放什么

      变量、算术计算、三目、对象属性、创建对象、调用函数、访问数组元素 ——有返回值的合法的js表达式

    # 箭头函数

    与普通函数区别:

    • 箭头函数没有arguments对象,使用reset
    • 几乎所有匿名函数都可用箭头函数简化 箭头函数是对大多数匿名函数的简写

    箭头函数this指向问题

    箭头函数的this指向的是外部函数

    • 对象中的方法能不能使用箭头函数?

      不行,对象中箭头函数的this指向的是外部函数,所以指向window

      解决:直接方法名括号fun(){}

    • 箭头函数this指向外部,还有作用域吗?

      箭头函数只让this,指向外部作用域的this

      箭头函数内的局部变量,依然只能在箭头函数内使用。出了箭头函数不能使用! 所以,箭头函数依然是作用域!只不过影响this而已!不影响局部变量

    • 箭头函数可以使用call apply bind替换this吗

      箭头函数底层相当于.bind()绑定外层this

      1. call不能使用
      2. apply不会报错,但this依旧指向window,数组参数能够传递
      3. bind不会报错,但this依旧指向window,参数能够传递

    # 参数增强

    • 参数默认值

      设置了形参默认值之后,如果没有传递实参,则使用默认值

      function(形参=默认值,形参=默认值...){}

    • 剩余参数

      let 函数名=>(...数组名arr)=>{}

      传进来的参数会自动添加到数组arr中去

      生成的数组是纯正的数组类型,所以使用数组家所有函数

      不确定实参值个数时D:\web-note-DN\web-note\All-专题总结\01demo.html, 都可用”…数组名”代替arguments接住所有或剩余实参值。

    • 展开运算符

      调用函数时:

      函数名(...数组arr)

      将数组拆散多个实参值,依次传给函数的每个形参变量

    • 区别剩余运算符和展示运算符

      • 1). 定义函数时,fun(...arr){}形参列表中的...,表示收集参数
      • 2). 调用函数时,fun(...['miku','gumi'])实参列表中的...,表示拆散传参

    # class

    旧js中,构造函数和原型对象是分开定义的,不符合封装的概念,由此催生出了class

    如何通过class定义类:

    • 用class{}包裹原构造函数和原型对象方法

    • 使用constructor(){}定义构造函数

    • 下面add(){}方法自动成为Fun类下的原型方法

      class Fun {
                  constructor(ename, eage) {
                      this.ename = ename;
                      this.eage = eage;
                  }
                  add() {
                      console.log(this.eage)
                      console.log(this.ename)
                  }
              }
      
      
    • 静态属性

      es6放弃了原型对象中保存共有属性的方式,改为静态属性

      ​ static 共有属性名=属性值

      标有static的静态属性,都是保存在构造函数对象身上。

      如何访问:类型名.共有属性名

    # 新增方法

    # filter
    let arry = [1,2,3,4,5,6,7]
    //current 当前值; index 当前下标; current这个数组
    console.log(arry.filter((current,index,array)=>current>6)) //7
    
    # forEach
    • 没有返回值

    • 不能用break中断

    • 遍历的是value

    let arry = [1,2,3,4,5,6,7]
    arry.forEach((current,index)=>{
    
    })
    
    # map
    • 有返回值(数组) 默认每一项return是undefined

    • 不能break中断

    let arry = [1,2,3,4,5,6,7]
    arry.map((current,index)=>{
        return 'hellow'
    })
    

    # 解构赋值

    1. 数组解构

      var [变量1, 变量2, ...] =数组、

      如果想省略掉数组中某个下标的值,使用逗号省略掉某一位即可

    2. 对象解构

      从一个大的对象中只提取出个别属性值单独使用 var { 属性名1:变量1, 属性名2:变量2,... }=对象

      每当对象中:左边的属性名和:右边的变量名刚好相同时,其实只写一个名字即可!

      var { name,age}=lilei;

    3. 参数解构

      基于函数的参数进行解构

      声明时 function fun({属性名1:参数1,属性名2:参数2})

      调用时 函数名(属性名:实参值,属性名2:实参值2)

    # 如何交换两个变量的值,不声明第三个变量

    let a = 1;
    let b = 2;
    [a,b]=[b,a]
    

    # es6快速去重

    const arry =[2,2,2,3,4,5,6,6,7]
    let item = [...new Set(arry)]
    console.log(item) //[2, 3, 4, 5, 6, 7]
    

    # Promise异步的理解

    const promise = new Promise((resolve,reject)=>{
      console.log(1); //同步队列
      resolve()
      console.log(2) //同步队列
    })
    promise.then(res=>{//先执行之后的js代码 在执行异步执行then
      console.log(3);
    })
    console.log(4);
     //1 2 4 3 
    

    # 递归求1-100的和

    const fun = (num1, num2) => {
      const num = num1 + num2;
      if (num2 + 1 > 100) {
        return num;
      }
      return fun(num, num2 + 1);
    };
    let sum = fun(1, 2);
    console.log(sum); 5050
    

    # 事件

    pc事件名

    事件名 触发方式 备注
    click 单击放手时 pc单击事件
    input 当一个input,select,textarea元素的 value 被修改时,会触发 input 事件。 获取value:e.srcElement.value

    事件对象

    • 自动赋值给在事件处理函数的第一个参数 一般写成e或者event

    事件对象属性

    属性 描述
    target 事件对象 (位于DOM树最上面的元素).
    screenX 点击事件发生时鼠标对应的屏幕x轴坐标
    screenY 点击事件发生时鼠标对应的屏幕y轴坐标.
    clientX 点击事件发生时鼠标对应的浏览器窗口的x轴坐标.
    clientY 点击事件发生时鼠标对应的浏览器窗口的y轴坐标.

    取消默认事件:e.preventDefault();

    禁止事件冒泡:e.stopPropagation();

    移动端事件

    事件名 触发方式 备注
    touchstart 触摸开始(手指放在触摸屏上)
    touchmove 拖动(手指在触摸屏上移动)
    touchend 触摸结束(手指从触摸屏上移开)
    touchenter 移动的手指进入一个dom元素。
    touchleave 移动的手指离开一个dom元素。

    # 流程控制

    1. 条件判断

      if

      switch

    # 数据类型转换语法糖

    数字与字符串互转

    +'12'  //纯数字字符串转为number类型 失败为NaN
    12+ '' //数字转字符串 ‘12’
    

    # 空值判断和常用操作语法糖

    //常量空值判断
    0 NaN undefined '' false //结果均为false
    !!value //false 
    if(!value) return  //为空停止执行下去
    value && fun()  //不为空就执行 fun()
    // 对象值不存在时为undefined 所以以上也能用于判断对象值,但要考虑是否为0或者''时是否会造成程序异常
    res.key|| '' // 如果res对象下key为空值 将自动返回第二个值
    

    # 日期与monet.js

    # 原生js(本机时间可以被更改)

    //返回本机时间(以下均为),未格式化
    new Date() //Tue Jan 04 2022 11:35:25 GMT+0800 (中国标准时间)
    //返回当前天数
    new Date().getDate() // 4
    

    # Promise

    es6新特性

    promise出现前提:回调地狱

    1. 在异步任务的结束调用传入的函数
    2. 调用函数 ,传入函数并等待异步任务执行完调用第二个函数

    如果要先后执行的任务多了!就会形成很深的嵌套结构——>回调地狱。

    极其不优雅,极其不便于维护

    promise三种状态:

    • pending挂起
    • fulfilled成功
    • rejected出错

    promise使用场景:

    今后,只要多个异步任务必须顺序执行时,

    都要用promise技术来代替回调函数方式 当一个任务比较耗费时间时,避免阻塞,就可以使用promise

    Promise 处理始终是异步的,因为所有 promise 行为都会通过内部的 “promise jobs” 队列,也被称为“微任务队列”(ES8 术语)。

    因此,.then/catch/finally 处理程序(handler)总是在当前代码完成后才会被调用。

    如果我们需要确保一段代码在 .then/catch/finally 之后被执行,我们可以将它添加到链式调用的 .then 中。

    如何使用

    function fun() {
                return new Promise((resolve, reject) => {
                    setTimeout(() => { //异步耗时任务
                        console.log("异步任务");
                        if (Math.floor(Math.random() * 10) >= 5) {
                            resolve('成功') //执行成功的回调
                        } else {
                            reject('失败') //执行失败的回调
                        }
                    }, 2000)
                })
            }
            fun().then(res => {
                console.log(res); //成功
            }).catch(err => {
                console.log(err); //失败
            }).finally(()=>{ 
                console.log() //无论成功还是失败都会执行  (用于无论请求是否成功都会停止loding或者按钮加载状态)
            })
    

    每个promise对象都有三个方法then()和cacth(),finally()

    调用resolve()表示任务执行成功,Promise的状态置为fullfiled(成功) new promise()自动调用then()函数执行下一项任务

    调用reject()表示任务执行失败,Promise的状态置为rejected(出错) new Promise()会自动调用.catch()执行错误处理代码

    finally()在prmose任务执行后执行,无论prmose的结果如何都会执行

    # 链式调用

    你可以重复调用.then 方法(catch/finally都可以,但用的少)处理成功后的结果,可以返回新值,接下来的.then()方法会拿到这个新值

    new Promise(function(resolve, reject) {
    
      setTimeout(() => resolve(1), 1000); // (*)
    
    }).then(function(result) { // (**)
    
      alert(result); // 1
      return result * 2;
    
    }).then(function(result) { // (***)
    
      alert(result); // 2
      return result * 2;
    
    }).then(function(result) {
    
      alert(result); // 4
      return result * 2;
    
    });
    

    将多个.then添加到一个promise上,并不是链式调用,以下案例会分别执行

    promise.then(function(result) { alert(result); // 1 return result * 2; });

    promise.then(function(result) { alert(result); // 1 return result * 2; });

    promise.then(function(result) { alert(result); // 1 return result * 2; });

    # Prmose并行运行异步任务

    如何使用Promise处理多个任务,并且任务之间需要前个任务的结果,每个任务还是耗时的?

    案例:使用定时器模拟耗时任务,每个任务都会在前个任务执行完在执行下一个

     function fun() {
                return new Promise((resolve, reject) => {
                    setTimeout(() => { 
                        resolve(2) 
                    }, 2000)
                })
            }
            fun().then(res => {
                console.log(res);  //2
               return  new Promise((resolve,reject)=>{
                   setTimeout(()=>{
                       resolve(res*2)
                   },2000)
               })
            }).then(res=>{
                console.log(res);  //4
                return  new Promise((resolve,reject)=>{
                   setTimeout(()=>{
                       resolve(res*2)
                   },2000)
               })
            }).then(res=>{
                console.log(res);  //8
                return  new Promise((resolve,reject)=>{
                   setTimeout(()=>{
                       resolve(res*2)
                   },2000)
               })
            })
    

    # Promise错误捕获

    1. 将.catch()放置在链的末尾,一旦任意一个Promise被reject,.catch()都会捕获它

    2. Promise中隐式try...catch

      //一旦执行到 throw new Error() Prmoise的状态会直接失败,并将错误信息传递给catch(err=>{...进一步处理})
      new Promise((resolve, reject) => {
        throw new Error("Whoops!");
      }).catch(error=>{...进一步处理]}); // Error: Whoops!
      
    3. 如果在链式调用中 其中一个.then()失败了,将停止继续运行下一个.then,并且将错误传递给最近的.catch()中

    4. 如果没有.catch()捕获错误,将中止异步任务的继续执行,反之,如果在.catch()后还有.then()任务的存在,将继续执行下去

    5. 每个promise链式调用的最后都建议使用.catch()捕获错误

    # Promise Api

    1. prmoise all

      并行执行任务,并且等到所有任务执行完毕后再进行处理

      语法:接受多个prmoise对象,并且需要包含在数组中,会等待所有prmoise执行完毕后,才会Promise的状态才会改变为resolve

      Promise.all的.then参数为每个Promise成功后传递的参数的数组,并且他们的在数组中的位置是不变的,无论任意一项任务执行有多缓慢

      let promise = Promise.all([
                  new Promise(resolve=>{setTimeout(()=>resolve(1),5000)}),
                  new Promise(resolve=>{setTimeout(()=>resolve(2),2000)}),
                  new Promise(resolve=>{setTimeout(()=>resolve(3),2000)}),
              ]).then(res=>{
                  console.log(res); //[1,2,3]
              });
      

      错误机制:如果任意一个 promise 被 reject,由 Promise.all 返回的 promise 就会立即 reject,并且带有的就是这个 error。

    2. Promise.allSettled

      例如,我们想要获取(fetch)多个用户的信息。即使其中一个请求失败,我们仍然对其他的感兴趣。让我们使用 Promise.allSettled:

      let promise = Promise.allSettled([
                  new Promise(resolve=>{setTimeout(()=>resolve(1),5000)}),
                  new Promise((resolve,reject)=>{setTimeout(()=>reject(2),2000)}),
                  new Promise(resolve=>{setTimeout(()=>resolve(3),2000)}),
              ]).then(res=>{
                  console.log(res); //[{},{},{}]
              });
      

      不同的是 allSettled resolve 的res是数组,并且每个都包含了执行promise的结果的对象,无论结果是成功还是失败

      0: {status: 'fulfilled', value: 1}
      1: {status: 'rejected', reason: 2} //失败的结果
      2: {status: 'fulfilled', value: 3}
      
    3. Promise.race

      与prmoise.all类似,但只会等待第一个 settled的Promise并获取结果(或error)

      Promise.race([
        new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
        new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
        new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
      ]).then(alert); // 1
      
    4. Promise.resolve/reject

      当一个函数被期望返回一个 promise 时,这个方法用于兼容性。

      这里的兼容性是指,我们直接从缓存中获取了当前操作的结果 value,但是期望返回的是一个 promise,所以可以使用 Promise.resolve(value) 将 value “封装”进 promise,以满足期望返回一个 promise 的这个需求。

      Promise.resolve(value) 用结果 value 创建一个 resolved 的 promise。

      Promise.reject(error) 用 error 创建一个 rejected 的 promise。

    # async/await

    Async/await 是以更舒适的方式使用 promise 的一种特殊语法,同时它也非常易于理解和使用。

    它可以将一个函数转换成Promise对象,总是返回Prmoise,其值自动被包装成resolved的promise中

    # 微任务和宏任务

    1. 微任务

      案例:

      let promise = Promise.resolve();
      
      promise.then(() => alert("promise done!"));
      
      alert("code finished"); // 这个 alert 先显示
      
      

      微任务包含哪些:Promise的.then/catch/finally处理程序

      微任务队列:

      当同步代码执行完才会执行微任务队列中的代码

      或者,简单地说,当一个 promise 准备就绪时,它的 .then/catch/finally 处理程序(handler)就会被放入队列中:但是它们不会立即被执行。当 JavaScript 引擎执行完当前的代码,它会从队列中获取任务并执行它。

    2. 宏任务

      宏任务包括

      script(整体代码,最优先执行的代码)
      setTimeout
      setInterval
      I/O
      UI交互事件
      postMessage
      MessageChannel
      setImmediate(Node.js 环境)
      
    3. 总结

      宏任务先执行,然后是微任务

    编辑 (opens new window)
    上次更新: 2022/09/08, 08:09:32
    数据类型判断

    数据类型判断→

    最近更新
    01
    腾讯云宝塔自动化部署方案
    03-22
    02
    vscode格式化
    12-11
    03
    github使用问题与解决方案
    09-16
    更多文章>
    Theme by Vdoing | Copyright © 2022-2023 Wncf | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式