数据类型都有哪些
原始值数据类型【值类型、基本数据类型】
- 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们把函数执行的这种机制叫做闭包。
闭包的实际应用
- 闭包解决循环点击按钮绑定点击事件,点击按钮输出当前的索引
文件上传
小文件基于文件流
- 基于文件流
- 直接用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 } 复制代码
- 基于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
}
复制代码
文件下载
- 基于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') }) 复制代码
- 无论谁先知道状态,最后结果的顺序和出阿迪的数组循序保持一致
- let aa = Promise.all([promise实例])
-
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 复制代码