web进阶笔记

·  阅读 224

数据类型都有哪些

原始值数据类型【值类型、基本数据类型】

  • number 数字
  • string 字符串
  • boolean 布尔
  • null 空对象指针
  • undefined 未定义
  • bigint 大数

对象类型【引用数据类型】

  • 标准普通对象 object
  • 标准特殊对象 Array、RegExp、Data、Math、Error...
  • 非标准特殊对象 Number、String、Boolean...
  • 可调用/执行对象 function

数据检测有哪些方法

typeof

  • typeof返回值永远是字符串。
    • typeof typeof typeof 'nihao'返回结果是'string'
  • typeof bull 返回值是'object'
  • typeof function 返回值'function'
  • typeof n检测一个未被声明的变量,返回值是'undefined'
  • 按照计算机底层的存储的二进制进行检测【效率高】
    • 000开头代表对象
    • 1 开始的代表数字
    • 010 开头代表浮点数(有小数点)
    • 100 开始的代表字符串
    • 110 开始代表布尔
    • 0000000 开头代表null,这就是null检测是object的原因。
    • -2^30 代表undefined

typeof底层处理机制

instanceof

constructor

object.prototype.toString.call

JS底层存储机制:堆(Heap)、栈(Stack)内存

  • 当浏览器去加载渲染页面【开一个页卡】,首先会从计算机内存条中分配出两块内存。(堆栈内存)

栈内存(ECStack执行环境栈)

栈内存作用

  • 供代码执行
    • 最开始,要执行的全局下的代码,先形成一个全局执行上下文(EC(G)),然后进栈执行
  • 存放声明的变量
  • 存放原始值类型
  • 存放全局执行上下文,函数私有上下文,块级私有上下文
    • 全局上下文创建时间:打开页面,执行全局代码就会形成,页面关闭的时候才会释放,或者是按F5刷新,相当于删掉之前的全局上下文,重新生成新的全局上下文。
    • 私有上下文是在代码执行到函数或者块级作用域时生成,这个函数或者块级作用域代码执行完后,就会立即释放。所以函数每执行一次就会生成一个新的私有上下文(也叫活动对象)

堆内存

堆内存作用

  • 存放对象数据类型值
  • 放置浏览器(window)的内置API(全局对象global object)简称GO内存空间,例如setTimeout、alert。

创建变量时发生的事情

  • var 变量 = 值
    • 先创建值
      • 原始值直接存储到栈内存中
      • 对象值需要在堆内存中出,开辟出一块空间来,(16进制地址),在空间中存储对象的键值对,最后把地址放到栈内存中,供变量引入。
    • 声明变量
      • 把声明的变量存储到当前上下文的变量对象中
    • 赋值(定义)操作
      • 让变量和对应的值关联在一起(指针指向过程)
  • 注意
    • 基于var/function声明的变量值不是存放到VO(全局变量对象,这个是在栈内存),而是放在了GO(全局对象,这个是堆内存),就相当于给window设置了对应的属性,比如var a = 13;他会在GO中创建a:13键值和对应的地址,然后将这个地址赋值到VO中的变量a。咱们可以通过window.a拿到a的值。
    • 基于let/const/class声明的变量,是纯正血统的全局变量,存放到VO中,和GO是没有关系的。
    • 没有任何声明的变量,也相当于是给全局变量对象(GO)设置了一个属性。
      var a = 13;
      console.log(a,window.a)// 13 13
      let b = 15;
      console.log(b,window.b)// 15 undefined
      d = 15  //等于window.d
      //注:在全局上下文中,使用一个变量,首先看是否是VO中,如果不是则看GO,如果还没有就未定义错误。
      复制代码

闭包作用域和浏览器垃圾回收机制(GC)

常见的浏览器垃圾回收机制有两种方法

第一种标记清除法(chrome)

  • 开辟内存时(不论是栈内存或者堆内存),会有一个标记,标记自己有没有被引用,浏览器会在自己空闲的时候,会把我们开辟的内存依次去迭代和遍历,如果没有引用浏览器就会自动把它回收。

第二种引用计数法(IE6~8)

  • 引用计数类似与标记清除,它也是进行一个标记,不过是这个地址被引用几次就标记为几,当它的引用计数为0时,浏览器在空闲时就会将它回收。

  • 例如

let obj = { name: 1, age: 12}; //此时会创建一个堆内存来存储键值对
obj = null;  //将变量obj重新赋值,刚刚创建的堆内存在浏览器空闲后就会进行释放回收
复制代码

闭包

  • 私有上下文:一般函数(代码块)中的代码执行完,浏览器会自动把私有上下文出栈释放,但是如果,当前上下文中,某个和它关联的内容(一般是一个堆内存)被当前上下文以外的事物占用了,那么这个私有上下文不能出栈释放,这样私有上下文中的私有变量和值也被保存起来了。

  • 他是一种机制,函数执行会产生一个私有上下文(作用域),可以保护里面的私有变量不受外界干扰,防止全局变量污染,我i们把函数执行的这种机制叫做闭包。

闭包的实际应用

  • 闭包解决循环点击按钮绑定点击事件,点击按钮输出当前的索引

文件上传

小文件基于文件流

  1. 基于文件流
    • 直接用element-ui upload的插件
    beforeUpload(file){
        let {type, size} = file
        if(/(png|git|jpeg|jpg)/i.test(type)){
            this.$message('文件格式不正确')
            retrun false
        }
        if(size>200*1024*1024){
            this.$message('文件格式不正确')
            return false
        }
    }
    
    handleSuccess(result){
        //请求成功后返回数据result
    }
    复制代码
  2. 基于base64编码格式
    • 把上传的文件先进行解析(fileReader)
    • 把其转换为base64编码格式
    • 自己基于axios把信息传递给服务器
    function fileParse(file,type==='base64'){
        return new Promise(resolve=>{
            let fileRead = new FileReader();
            if(type=='base64'){
                fileRead.readAsDataURL(file); 
                }else{
                    fileRead.readAsArrayBuffer(file)
                    }
            fileRead.onload = ev=>{
                 resolve(ev.target.result)
            }
        })
    }
    复制代码

大文件切片上传及*断点续传

import SparkMD5 from 'spark-md5'
async changeFile(file){
    if(!file) return
    file = file.raw
    //利用上面的方法解析文件转为buffer数据
    //我们会把文件切片处理;把一个文件分割成为好几个部分(固定数量/固定大小)
    
    //固定数量
    //每一个切片有自己的部分数据和自己名字,例如HASH-1.mp4、HASH-2.mp4、HASH-3.mp4...
    let buffer = await fileParse(file,'buffer'),
    spark = new SparkMD5.ArrayBuffer(),
    hash,
    suffix;//后缀名
    spark.append(buffer)
    hash = spark.end()
    suffix = /\.([0-9a-zA-Z]+)$/i.exec(file.name)[1]
    
    //创建100个切片
    let partList = [],
    partsize = file.size / 100,
    cur = 0
    for(let i = 0; i<100; i++){
        let item = {
            chunk: file.slice(cur,cur + partsize),
            filename: `${hash}_${i}.${suffix}`
        }
        cur += partsize
        partList.push(item)
    }
    this.partList = partList
    this.hash = hash
    this.fileUpload()
}
//上传切片
async function fileUpload(){
//根据100个切片创造100个请求集合
    let requestList = [];
    this.partList.forEach((item,index)=>{
        let fn = ()=>{
        let formData = new FormData();
            formData.append('chunk',item.chunk)
            formData.append('filename',item.filename)
            return axios.post('url',formData,{headers:{"Content-Type":"multipart/form-data"}}).then(res=>{
                result = res.data
                if(result.code==0){
                    this.total += 1; //这个是上传的个数
                    //传完的切片移除掉
                    this.partList.splice(index,1)
                }
            })
        }
        requestList.push(fn)  //100个请求集合
    }) 
    //传递:并发(并行)通过ajax.abort()阻止继续上传/串行基于标识(自定义变量控制不继续发送)
    let i = 0;
    let complete = async ()=>{
        let result = await axios.post('url',{params:{hash:this.hash}})
        result = result.data
        if(result.code ===0){
            this.$message('上传成功')
        }else{
            this.$message('')
        }
    }
    let send =async ()=>{
        if(this.abort) return
        if(i>=requestList.length){
            //都传完了
            complete()
            return
        }
        await requestList[i]()
        i++
        send()
    }
    send()
}
//暂停继续,断点上传
function handleBtn(){
    if(this.btn){
    //断点续传
    this.btn = false//通过true false显示继续 暂停
    this.abort = false
    this.fileUpload()
    }
    //暂停上传
    this.btn = true
    this.abort = true
}
复制代码

文件下载

  1. 基于blob
    //如果需要下载json格式的文件
    function(res.e){
        let blob
        if(e==='json'){
            blob = new Blob ([JSON.stringfy(res.data)])
        }else{
            blob = new Blob([res.data])
        }
        let fileName = decodeURI(escape(res.headers['filename']))
        if('dowload' in document.createElement('a')){
            let a = document.createElement('a')
                a.download = fileName
                a.display.style = none
                a.href = URL.createObjectURL(blob)
                document.body.appendChild(a)
                a.click()
                URL.removeObjectURL(a.href)
                document.removeChild(a)
        }else{
                navigator.msSaveBlob(blob)
        }
    }
    复制代码

Promise

Promise使用

  • new Promise(可执行函数)
    • 它等价于new 一个类执行,是同步的,代码运行和new 一个函数一样。
    • new Promise的时候,在Promise内部会将可执行函数立即执行,一般这个函数是放一个异步的编程代码,不是异步的代码也可以。
    • 同时会给这个可执行函数传递两个参数,resolve和reject函数。
  • let p = new Promise(()=>{})
    • p是Promise的一个实例
    • 内置私有属性
      • PromiseState 实例状态 pending准备状态 fulfilled/resolved成功状态 rejected失败状态
      • PromiseValue
    • 公有属性方法 Promise.prototype
      • then
      • catch
      • Symbol(Symbol.toStringTag):'Promise' 这也是object.tostring.prototype.call('p')的原理[object Promise]
  • 在可执行函数中执行resolve或者reject都是为了改变实例的状态
  • 一旦状态改变为fulfilled、resolved或者rejected则不能再改为其他状态
let p1 = new Promise((resolve,reject)=>{
    resolve('ok') // 这是PromiseState就会改为resolved ,PromiseValue的值就成为ok,如果是reject同理
})
复制代码
  • 实例的状态改变可以控制then中两个方法中的一个方法,如果是resolved,触发执行的是第一个回调函数,如果是reject触发的是第二个函数回调。并且会将PromiseValue的值传递给方法

        let p = new Promise((resolve,reject)=>{
           resolve('ok')
        }).then((res)=>{
               console.log(res)// 'ok'
           },()=>{})
    复制代码
  • 当promise的状态由pending改变为resolved或者reject时

    • 首先会将传递进行的onFulfilledCallback和onrejectCallback存储起来【存到一个容器中】
    • 其次再去验证当前的实例状态
    • 验证后通知对应的回调函数执行【注意:但是不是立即执行,.then是异步,得等同步函数执行结束再执行】
  • let p2 = p1.then(res=>{ console.log(111) },rej=>{})

    • p2 不等于p1,p2是p1.then()运行的实例。他还是属于pending状态,如果要改变它的状态需要在.then()的两个回调函数中,使用resolve或者reject
  • 如果我们的onfulfilledCallback和onrejectCallback不传递,则状态会顺延/穿透到下一个同等状态应该执行的函调函数上,内部是其实是自己补充了一些实现效果的默认函数。

    new Promise ((resolve,reject)=>{
        resolve('ok')
    }).then(null,null).then((res)=>{
        console.log('成功',res),
        (reject)=>{
            console.log('失败',reject)
        }
    })
    复制代码
  • catch捕获失败的,原理如下

    Promise.prototype.catch = function(onrejectedCallback) {
        return this.then(null,onrejectedCallback)
    }
    复制代码
  • 结合顺延和catch,真实项目中写法

    new Promise((resolve,reject)=>{
        reject('no')
    }).then(res=>{
        console.log('成功',res)
    }).catch(rej=>{
        console.log('失败',rej)
    })
    复制代码
  • all

    • let aa = Promise.all([promise实例])
      • 要求数组里的每一项都是promise的实例
      • all返回的是一个promise实例,它的成功或者失败取决于数组中每一项的实例是成功还是失败,只要有一个失败,返回值就是失败的。只有都成功aa才是成功的。
      let a = new Promise((resolve)=>{
          resolve('ok')
      })
      let b = new Promise((resolve,reject)=>{
          reject('no')
      })
      Promise.all([a,b]).then(res=>{
          console.log(res,'ok')
      }).catch(rej=>{
          console.log(rej,'rej')
      })
      复制代码
    • 无论谁先知道状态,最后结果的顺序和出阿迪的数组循序保持一致
  • race谁先知道状态就返回AA的成功和失败

promise输出案例

new Promise((resolve)=>{
    console.log('promise1')
    resolve()
}).then(()=>{
    console.log('then11')
    new Promise((resolve)=>{
        console.log('promise2')
        resolve()
    }).then(()=>{
        console.log('then21')
    }).then(()=>{
        console.log('then22')
    })
}).then(()=>{
    console.log('then12')
})// promise1 then11 promise2 then21 then12 then22
复制代码
  • 解析上面代码
    • new promise是同步,所以先输出promise1
    • resolve修改状态为成功,故它第一个then11为微任务1,立刻放入EventQueue(微任务)
    • 下面第二个then12,它的状态取决于then11,但then11还没有执行完,故放到webAPI中监听等待。
    • 执行微任务1(then11),同步输除then11,promise2
    • resolve状态成功,立即生成微任务3,then21,
    • then22,它的状态取决于then21,但是then21还没有执行,故放入webAPI
    • 执行代码,输入then21,webAPI中微任务二先存,故先执行,输出then12 最后到then22

async await ES7

  • async是函数的修饰符,控制函数返回Promise实例
    async function fn(){
        return 10
    }
    console.log(fn())  //返回promise实例,成功状态,值为10
    复制代码
    • 如果函数内部执行报错,则返回的promise实例,状态是失败

async 中加try 和 catch

  • 如果函数内部做了异常捕获,则还是成功状态
    let a = async function fn() {
        try {
          console.log(b);
        } catch (e) {
          console.log(e);  //捕获错误
        }
        return 10;
    };
    console.log(a()); //返回状态是成功的,值是10
    复制代码
  • await是异步微任务,await后面的代码爱咋地咋地,但是下面的代码会暂停执行,把他们当作一个任务,设置在EventQueue微任务队列中。
  • await后面一般都是跟Promise实例,如果await后面跟的不是promise实例,浏览器也会把其变为Promise实例,如果await后面的实例是失败状态,则代码将会结束执行。如下
    async function fn(){
        await 1 //等价于 await Promise.resolve(1)
        await Promise.reject(1)
        console.log(1111) //  不会输出1111
    }
    复制代码

关于this

事件绑定

  • DOM0; xxx.onxxx = function(){}
  • DOM2; xxx.addEventListener('onxxx',function(){})
    • 给当前元素的某个事件行为绑定方法【此时是创建方法,方法没有执行】,当事件行为触发,浏览器会把绑定的函数执行,此时函数中的this是当前元素对象本身。

函数执行

  • 正常的普通函数执行,看函数前是否有,有前面是谁this就是谁,没有,this就是window,严格模式下undefined。'use strict'
  • 匿名函数
    • 函数表达式 var fn = function(){console.log(this)} //window
    • 自执行函数 (function(x){console.log(this)})(10) //window
    • 回调函数 function(callback){callback()} //一般是window,但是可以通过call改变。
  • this指向试题
        var x = 3,
        obj = {
            x: 5,
        };
    obj.fn = (function () {
        this.x *= ++x; //window  x = 12
        return function (y) {
            console.log(this)
            this.x *= ++x + y;  //obj.x = 95
            console.log(x);  //x 13
        };
    })();
    var fn = obj.fn;
    obj.fn(6);
    fn(4); //234
    console.log(obj.x, x); //95 234
    
    //首先代码从上到下执行,创建x, obj, obj.fn,自执行函数执行,然后将return这个函数赋值给fn
    // obj.fn(6)执行,相当于this是obj,执行的函数是返回值
    // fn(4)执行,相当于它的this是window
    复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改