likes
comments
collection
share

关于js的部分总结

作者站长头像
站长
· 阅读数 14
1,js的垃圾回收机制
1,标记清除:

当变量进入执行环境的时候,比如在函数中声明一个变量,垃圾回收器将其标记为进入环境,当变量离开环境的时候,将其标记为离开环境垃圾回收器在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量,以及被环境中变量引用的变量的标记,在完成这些之后仍然存在的标记就是要删除的变量

2,引用计数:

当声明变量并将这个引用类型赋值给该变量的时候,这个值的引用次数就加1,如果该变量的值变成另外一个,则这个值的引用次数减1,垃圾回收机制在运行的时候通过标记清除的方式将引用次数为0的数据回收

3,垃圾回收机制的优化
  1. 分代式垃圾回收:将v8的堆内存分为新生代和老生代两区域,采用不同的垃圾回收器也就是不同的策略管理垃圾回收新生代的对象为存活时间较短的对象,内存为1-8M,老生代的对象为存活时间较长或者常驻内存的对象
  2. 新生代垃圾回收:通过一个scavenge的算法来进行垃圾回收,在scavenge具体实现中,主要采用的一种复制的方法cheney算法,将堆内存分为两个,一个为使用区,一个为空闲区,将新生成的对象放在使用区,当使用区写满时,就需要执行一次垃圾清理操作,当开始垃圾回收时,新生代垃圾回收器会对使用区的活动对象做标记,标记完成之后将使用区的活动对象复制进空闲区并进行排序,随后进入垃圾清理阶段,即将非活动对象占用的空间清理掉,最后进行角色的互换,将原使用区变成空闲区,把原来的空闲区变成使用区
4. 新生代转老生代的情况:
  1. 当一个对象经过多次复制后依然存在,将其认为是一个生命周期较长的对象,移动到老生代中,采用老生代的垃圾回收策略进行管理
  2. 复制一个对象到空闲区时,空闲区空间占用超过了25%,直接晋升到老生代
  3. 老生代垃圾回收:采用标记清除算法
  4. 全停顿:垃圾回收机制在运行的过程中,会阻塞js脚本的执行,需等待垃圾回收完毕后再恢复脚本执行,这个行为称之为全停顿
  5. 新生代的并行回收:垃圾回收器再主线程上执行的过程中,开启多个辅助线程,同时执行同样的回收工作,在执行垃圾回收的过程中,会启动多个线程来负责新生代中的垃圾清除操作,这些线程同时将对象空间中的数据移动到空闲区域,这个过程中由于数据地址会发生改变,所以还需要同步更新引用这些对象的指针
5,从全停顿式到增量标记,将gc标记的过程分为,很多小步,每执行完一步就让应用逻辑执行一会,交替多次完成 怎么实现:
  1. 三色标记法 三色分为:白色(未被标记的对象),灰色(自身被标记,成员变量未被标记),黑色指自身和成员变量都被标记 最初所有对象都是白色,意味着回收器没有标记它们,从一组对象开始,先将这组对象标记为灰色并推入到标记工作表中,当回收器从标记工作表中弹出对象并访问它的引用对象时,将其自身由灰色转化为黑色,并将自身的下一个引用对象转化为灰色,没有被标记的就会被回收(注意没有灰色标记时,才会进行清理)
  2. 写屏障 一旦有黑色对象引用白色对象,该机制会强制将引用的白色对象改为灰色
  3. 懒性清理 当增量标记完成后,假如当前的可用内存足以让我们快速的执行代码,其实我们就没有必要立即清理内存,可以先将清理过程延迟一下,让js脚本代码先执行,也无需一次性清理完所有非活动对象内存,可以按需逐一进行清理直到所有的非活动对象内存都清理完毕
2,encodeR0和 decodeR0的作用是什么?

encodeURI()用于将URL转换为十六进制编码。而 decodeURI()用于将编码的URL转换回正常URL。

3,new的操作

创建一个空对象 改变this的指向 改变对象的原型链 执行构造函数 返回return值

4,script标签的defer和async的区别
  1. defer,对页面解析的同时下载js文件,但是要等到页面解析完,才会执行js文件,多个defer加载时,会按照他们在页面上的顺序进行加载
  2. async,对页面解析的同时下载js文件,但是一旦下载完成,停止对页面的解析,先执行js文件,不能保证加载顺序
5,闭包
  1. js的闭包指的是两个嵌套的函数,在内部函数中访问了外部函数作用域的变量,这个被引用的变量,在js的垃圾回收机制清除数据的时候,是不会被清除的
  2. 优点:因为有函数作用域的存在,所以变量在外部的函数作用域中,生成了一个局部的变量,但是因为内部函数的引用使其的作用域进行了扩展,
  3. 注意:这个内部函数可以访问外部函数的变量,这里涉及到一个作用域链的问题
  4. 缺点:js的闭包容易造成内存的泄露
  5. 内存泄露:一般来说,在变量声明的时候会分配一部分的内存,然后在这个变量访问或者使用的时候,这个内存使用,等到内存使用完毕后这个内存就会释放,内存泄露指的是在内存在执行完毕时,并没有被释放的情况
  6. 应用场景:在vue2中对Object.defineProperty的封装,传入一个新值,利用get来进行更新,对于私有变量的封装,对于settimeout的传参
6,null和undefined

1,null表示无的对象,转化为数值时值为0,作为对象原型链的原点 2,undefined表示一个无的原始值,转化为数值时为NaN,变量声明但是没有赋值时,其默认值就为undefined

7,关于数组的一些方法
1,增删改查:
  1. 删除:shift删除数组的第一个元素,返回删除的元素 pop删除数组的最后一个元素,返回删除元素
  2. 增:unshift给数组的前面添加一个或者多个元素,返回值为新数组的长度 push在数组的末尾添加一个或者多个元素,返回值为新数组的长度
  3. 排序的操作 sort对数组进行排序,返回值为排序后的数组 reserve对数组的顺序颠倒,返回值为颠倒后的数组,原数组也会被修改
  4. 特殊方法 splice(起始索引,删除的个数,需要替换的元素) slice(起始值,结束值)返回截取的新数组
2,对数组的遍历
  1. reduce((当前的和,item,index,arr)=>{},起始值)一般用来对数组的求和
  • 利用for来实现reduce
Array.prototype.myReduce = function (callback, context) {
        let arrList = this
        pre = context == undefined ? arrList[0] : context
        let i = context == undefined ? 1 : 0
        for (i; i < arrList.length; i++) {
          pre = callback(pre, arrList[i], i, arrList)
        }
        return pre
      }

      const res = arr.myReduce((a, b) => {
        return a + b
      })
  • 利用reduce来实现数组的去重
let arrReduce = ['t', 't', 'h', 'h', 'k', 'k', 'l', 'o', 'p']
      let arrReduce1 = arrReduce.reduce((pre, item) => {
        if (!pre.includes(item)) {
          pre.push(item)
        }
        return pre
      }, [])
  • 利用reduce来判断元素出现的次数
let list = [
        { id: 1, type: 'human', name: '鹿晗' },
        { id: 2, type: 'robot', name: '伊娃' },
        { id: 3, type: 'animal', name: '豆豆' },
        { id: 4, type: 'human', name: '蔡徐坤' },
        { id: 5, type: 'robot', name: '夏娃' },
        { id: 6, type: 'robot', name: 'hh' },
        { id: 7, type: 'robot', name: 'xx' },
      ]
      const obj = list.reduce((pre, item) => {
        if (pre[item.type]) {
          pre[item.type]++
        } else {
          pre[item.type] = 1
        }
        return pre
      }, {})

2. filter((item,index,arr)=>{},执行上下文(this))对数组的元素进行筛选,返回值为一个满足条件的数组

  • 利用for来实现filter
Array.prototype.myfilter = function (callback, context) {
        const arrList = this
        const arr2 = []
        for (let i = 0; i < arrList.length; i++) {
          if (callback(arrList[i])) {
            arr2.push(arrList[i])
          }
        }
        return arr2
      }
      const arrFilter = arr.myfilter((item, index, arr) => {
        return item > 3
      })

3. every((item,index,arr)=>{},执行上下文(this))测试一个数组内所有元素是否都能通过某个指定函数的测试,返回一个布尔值

  1. findindex((item,index,arr)=>{},this)返回数组中满足提供测试函数的第一个元素的索引,如果没有存在就返回-1

  2. foreach((item,index,arr)=>{},this)对数组进行遍历

  • 利用for循环来实心foreach
   Array.prototype.myforeach = function (callback, con) {
        const length = this.length
        for (let i = 0; i < length; i++) {
          callback.call(con, this[i], i, this)
        }
      }
      let arr = [1, 2, 2, 3, 4, 5, 6]
      arr.myforeach(function (item, index, arr) {
        console.log(item, index, arr, this)
      })

6. join将一个数组的所有元素连接成一个字符串并返回这个字符串

  1. includes判断一个数组是否包含一个指定的值,如果包含则返回true,不包含返回false

  2. map对数组进行遍历,并且返回一个新的数组

  • 利用for来实现map方法
Array.prototype.mymap = function (callback, context) {
        const arrList = this
        const arr1 = []
        for (let i = 0; i < arrList.length; i++) {
          arr1[i] = callback.call(context, arrList[i], i, arrList)
        }
        return arr1
      }
      const arr3 = arr.mymap((item, index, arr) => {
        return ++item
      })
异步编程的进化史
  1. 回调函数
  2. promise实现
  • 首先promise的出现解决了地域回调的问题,promise的出现使得异步代码不用再向之前一样一直回调
  • promise的构造函数接受一个函数作为参数,传入两个参数:resolve,reject代表异步操作执行成功后的回调函数和异步操作执行失败的回调函数,可以支持链式的调用
  • promise的基础,状态的变化:只可以从pending转化为resolved或者从pending转化为rejected
  • then,接受两个参数,一个是成功的回调,一个是失败的回调,成功的回调是必选的,当promise的状态从pending转化为resolved执行成功的回调函数,返回一个新的promise对象
  • catch,用来捕获与处理错误,返回一个失败的promise对象
  • resolve,返回一个成功或者失败promise的对象
  • reject,返回一个状态为已拒绝的 Promise 对象,并将给定的失败信息传递给对应的处理函数。
  • all,参数是一个关于promise的数组,只有所有的promise成功才会成功,有一个失败就会失败
  • race,参数同样是关于promise的数组,第一个执行完毕的promise就是最终的结果,返回一个新的promise
  • allSettled,参数是一个关于promise的数组,等待所有的promise执行完毕,返回一个promise对象,这个promise返回一个对象数组,这个数组包含每一个promise的结果
  • any,接受一个promise对象的集合,当其中一个promise成功,就返回那个成功的promise的值
  • finally,为promise添加一个回调函数,无论promise的数值如何都会执行
  1. Generator函数
  • 概念:异步编程的解决方案,Generator函数是一个状态机,还是一个遍历器对象生成函数,返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态
  • 形式上:两个特征,一是,function关键词与函数名之间要有一个星号,二是,函数内部使用yield表达式,定义不同的内部状态
  • 生成器函数调用,该函数并不执行,返回的也不是函数的运行结果,而是一个指向内部状态的指针对象(遍历器),生成器函数是分步执行的,yield表示暂停执行(yield之后的内容不会立马执行,要等到next的调用),遍历器next表示恢复执行,注意这个yield表达式出现在别的表达式之中,必须放在圆括号里面
  • 协程是指多个线程(单线程下,多个函数)可以并行执行,但是只有一个线程或者函数处在执行的状态,其他线程或者函数都处于暂停态,线程或者函数之间可以交换执行权,也就是说线程执行到一半的时候,可以停止,将执行权交给另一个线程,等到稍后收回执行权的时候,再恢复执行
  • 协程与普通线程之间的区别,普通线程是抢先式的,那个线程优先获取资源,必须由运行的环境来决定,但是协程之间是合作式的,执行权由协程自己分配
  • 回调next方法的参数,一般来说yield表达式是没有参数的,但是next参数可以带一个参数,这个参数可以被当做上一个yield表达式的返回值
  1. async await
  • async函数是一个Generator的语法糖,对Generator函数的修改是,将*替换成了async,将yield替换成了await,但是Generator函数是需要执行器的,async的执行器是 asyncReadFile();,返回值为一个promise对象 * async函数内部return语句返回的值,会成为then方法回调函数的参数,除了抛出错误或者return的情况下,一直都是await的函数先执行,然后等到await的函数执行完成了,才会执行.then的回调函数
  • 实现原理:Generator 函数和自动执行器,包装在一个函数里,然后有一个自动的执行器,来执行Generator函数
同源策略
  1. 要解释同源策略,首先要知道什么是同源,同源是指在协议相同,域名相同,端口相同,这三者全部相同才是同源
  2. 而同源策略是指,浏览器最核心也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略
  3. 解决跨域的问题:
  • cors,主要是后端通过配置请求头,服务端设置 Access-Control-Allow-Origin 就可以开启 CORS,配置这个属性的域名,如果是通配符,就表示所有的请求都可以接受
  • 前端配置反向代理,在proxy中配置代理服务器,这个代理服务器是在本地的,所以跟发的请求不存在跨域问题,服务器与服务器之间同样也是不存在的
  • Jsonp,一种很早之前,社区提出来解决跨域的问题,只能解决get请求,由于script标签的src并不受同源策略的影响,在前端声明函数,在src请求中将函数发往后端,后端调用函数来传递数据
判断数据类型
  1. typeof,可以用来判断基本数据类型,注意null和NaN的判断
  2. obj instanceof Type,判断obj是不是Type类的实例,type的原型对象是否是obj原型链上的某个对象,只能判断引用数据类型
  3. constructor ,每一个实例对象都可以通过constructor来访问他的构造函数
  4. Object.prototype.toString.call(参数)
对this的理解
  1. 普通函数,this默认指向window
  2. 对象里面的函数,this指向对象
  3. 构造函数里面的this指向,指向实例对象
  4. call(this,参数,参数),apply(this,[]),bind(this,参数,参数)这个不会立即调用而是返回一个新的函数,