js数据类型检测
原因:对象存储在计算机中,都是以000开始的二进制存储,null也是。
- 检测普通对象、数组对象、正则对象、日期对象,返回的结果都是”Object”
- instanceof(检测当前实例属不属于这个类)解决typeof不能检测对象的具体类型
机制
只要当前类出现在实例的原型链上,结果都是true
缺点
```javascript let arr = [] congsole.log(arr instanceof Array) //true congsole.log(arr instanceof RegExp)//false console.log(arr instanceof Object) //true判断不出对象是否为普通对象
function Fn() { this.x = 10; } Fn.prototypoe = Object.create(Arrary.prototype) let f = new Fn; console.log(f instanceof Arrary) //true
console.log(1 instanceof Number) //false
<a name="3zJ2M"></a>#### 缺点1. 基本数据类型检测不出来1. 由于我们可以肆意修改原型的指向,所以检测结果不准确。<a name="ihmSC"></a>#### 手斯instanceof- constructor用起来比instanceof好,可以检测基本数据类型```javascriptlet a = []console.log(a.constructor === Arrary) //trueconsole.log(a.constructor === RegExp) //falseconsole.log(a.constructor === Object) //falselet b = 1console.log(b.constructor === Number) //trueb.prototype.constructor = 'aaaaaa'console.log(b.constructor === Number) //false
缺点
construct可以随便改,也不准
- Object.prototype.toString.call([value])
标准检测方法,Object.prototype.toString不是返回一个字符串,返回的当前实例所属类的信息。
let obj= {name: '王某某'}obj.toString()//'[Object Object]'
检测数据类型的方法
js中三类循环对比及性能分析
for循环及forEach底层原理
FOR循环是自己控制循环过程
1.基于var声明的时候,for和while性能差不多(不确定循环次数的时候用while)
2.基于let循环的时候,for循环的性能更好,「没有创造全局不释放变量」let有块级上下文
3.重写foreach(比上画性能差)手撕数组迭代方法
Array.prototype.forEach = function forEach(callback, context){let self = this,i = 0,len = self.length;context = context == null ? window :contextfor (; i < len; i++) {typeof callback === 'function' ? callback.call(context, self[i], i) : null}}
for in循环的BUG解决方案
1.迭代所有可枚举属性,「私有(大部分是可枚举的,length是不可枚举的)&公有(出现在类的原型链上的,也有部分是可枚举的)」,按照原型链一级级查找很耗性能
2.问题很多,不能迭代symbol属性、迭代属性会以数字属性优先、共有可枚举的{一般是自定义属性}属性也会进行迭代
(1)遍历的顺序以数字优先
(2)无法遍历symbol属性
(3)可以遍历共有中可枚举的
Object.prototype.fn = function fn(){}let obj = {name: 'hahahah',age: '13',[Symbol('AA')]: 100,0: 200,1: 300}for(let key in obj) {if(!obj.hasOwnProperty(key)) break; //解决问题3console.log(key);}//0,1,name,age,fnlet keys = Object.keys(obj)if(typeof Symbol !== 'undefined') keys = keys.concat(Object.getOwnPropertySymbols(obj))keys.forEach(key => {console.log(`属性名:${key},属性值:${obj[key]}`); //Symbol不能以这种方式转换为字符串}) //解决问题2,不兼容IE678
for of循环底层机制
1.迭代器iterator规范(部分数据结构实现了迭代器规范,有Symbol.iterator)「具备next方法,每次执行返回一个对象,具备value/done属性」数组,类数组,Set,Map「对象没有实现」
2.让对象具备可迭代性并且使用for of循环
let arr = [10, 20, 30]arr[Symbol.iterator] = function () {let self = this,index = 0return {next () {//必须具备next方法,执行一次next方法,拿到结构中的某一项值//done:false value:每一次获取的值if (index > self.length - 1) {return {done: true,value: undefined}}return {done: false,value: self[index++]}}}}// 1.let itor = arr[Symbol.iterator]() 2.itor.next()...let obj = {0: 100,1: 200,2: 300,3: 400,length: 4}obj[Symbol.iterator] = Array.prototype[Symbol.iterator]console.log(obj[Symbol.iterator]);for (let val of obj) {console.log(val);}//实现类数组的遍历
谈谈你对this的了解及应用场景
- this的五种情况分析(this执行主体,谁把它执行的,和在哪创建,在哪执行没有必然关系)
函数执行,看方法前面是否有点,没有点this是window「严格模式下this是underfined」,有点,点前面是谁this是谁
const fn = function fn() {console.log(this)}let obj = {name:'Obj',fn: fn}fn() //windowobj.fn() //obj
给当前元素的某个事件行为绑定方法,当事件行为触发,方法的this是当前元素的本事「排除attachEvent」
document.body.addEventListener('click', function(){console.log(this);})//body
构造函数体中的this是当前类的实例
function Factory () {this.name = "hahahah"this.age = "123"console.log(this)}let f = new Factory //Factory
箭头函数中没有执行主体,所用的this都是其所处上下文的this
let demo = {fn(){console.log(this) //demosetTimeout(function(){console.log(this)},1000) //windowsetTimeout(()=>{console.log(this)},1000)//demo}}demo.fn()
可以基于Function.prototype伤的call/apply/bind去改变this的指向
function func (x, y) {console.log(this, x, y)}let obj = {name:'OBJ'}//func函数基于__proto__找到Function.prototype.call把call方法执行//call(context->obj,...params->[10,20])//在call方法内部「call执行的时候」把func中this改为传入的第一个this改为obj//并且把params接收的值当作实参传递给func函数,并且让func函数立即执行func.call(obj,10,20)func.apply(obj,[10,20])document.body.addEventListener('click',func.bind(obj,10,20))//func函数基于__proto__找到Function.prototype.bind把bind方法执行//和call和apply区别,并没有把func立即执行//把传递来的值存储起来「闭包存储」//执行bind会返回一个新的函数 例如:proxy,把proxy绑定给元素的事件,当事件触发执行的是返回的proxy,在proxy内部//再去把func执行,把this和值都改变为之前存储的那些内容
手撕call/bind源码 ```javascript //原理:就是利用“点”定this机制,context.xxx = self “obj.xxx=func”=>obj.xxx() Function.prototype.call = function call (context,…params) { //this=>func context=>obj …params=>[10, 20] let self = this,
key = Symbol('KEY'),
result context == null ? context = window : null !/^(Object|function)$/i.test(typeof context) ? context = Object(context) : null context[key] = self result = contextkey delete context[key] }
Function.prototype.bind = function bind (context,…params) { //this=>func context=>obj …params=>[10, 20] let self = this return function proxy(…args){ //把func执行并且改变this即可,args是执行proxy的时候可能传递的值 self.apply(context, params.concat(args)) } }
7. 掌握this好玩的应用:鸭子类型```javascriptArrary.protopype.slice = function slice(){let self = this,result = []for(let i = 0; i < self.length,i++){result.push(self[i])}return result}//克隆返回一个新数组,浅拷贝arr.slice()//像鸭子,我们就说他是鸭子,类数组像数组「结构,一些操作」不允许用数组的方法我们让其用数组的方法「不能直接用」function func () {console.log(arguments)//把arguments变为数组,这样就可以用数组的方法:Arrary.from/[...arguments]/...let result = []for(let i = 0; i < arguments.length,i++){result.push(arguments[i])}return Arrary.prototype.slice.call(arguments)[].forEach.call(arguments,item=>{console(item)})}//如果让slice执行:[].slice(),Arrary.prototype.slice()并且让this指向arguments,就相当于把arguments变为数组
HTTP网络层优化
- URL解析
- 网络传输:TCP/IP(+TCP传输通道,IP主机地址,HTTP传输协议,HTTPS:SSL/TLS)FTP传输大文件
- 端口号:每一个端口号代表一个项目 http:http:80 https:443 ftp:21
- 编码:encodeURI/decodeURI
对整个URL编码:处理空格/中文
编码:encodeURIComponent/decodeURIComponnet
对主要传递参数进行编码
escape/unescape:unescape不支持所有浏览器,不应用客户端与服务器
- URI/URL/URI/URL/URN:区别

- 缓存检测:产品优化性能重点
- 先检测检测是否存在强缓存,有未失效,走强缓存,没有强缓存或失效走协商缓存,有或没有
- 缓存位置:内存缓存(加载js时候,ECstack,栈内存,堆内存,浏览器关闭内存释放),硬盘缓存
- html页面一般不做强缓存


把服务器返回的标示存到浏览器中
- 强缓存问题:如果服务器文件更新了,但是本地还没有更新就拿不到最新信息了
- html不做强缓存:每一次html的请求做正常的http请求
- 服务器更新资源后,让资源名称和之前的不一样,这样页面导入新的资源(webpack hashname)
- 当文件更新后,我们在html导入时候,设置一个后缀(时间戳)
- 协商缓存


- 协商缓存和强缓存的区别
- 协商缓存总会和服务器协商,所以一定要发HTTP请求
- 如果没有协商缓存的时候,向服务器发送请求(没有传递任何标识)
- 服务器收到请求
- last-Modified:资源文件最后更新的时间
- ETag:记录的是一个标识,根据资源文件更新生成的,每一次更新都会重新生成ETag
- 客户端拿到信息后进行渲染,把信息和和标识缓存到本地
- 第二次发请求,把标识传到服务器,服务器根据标识判断文件是否更新
- 强缓存和协商缓存只针对于静态资源文件,不经常更新的
- 数据缓存

把最新的数据缓存到本地
5.DNS(域名解析)解析,也是有缓存的,如果之前解析过本地会有缓存(不一定)


- 减少DNS请求(一个页面尽可能少用不同域名:资源都放在相同的服务器上:项目不会这么干,会把不同资源不同服务器上)
- web服务器,数据服务器,图片服务器(根据每个服务器特点选择服务器,资源合理利用,高可用高并发)
- http并发,同一个源同时可以发送4~7个http请求
- 建立连接通道







keep-live:保证TCP通道建立完成后,可以不关闭http1.0需要手动设置
http1.0和2.0的区别:

手撕源码
//数组的浅拷贝let newArr = [...arr]newArr = arr.concat([])newArr = arr.slice()//对象的浅拷贝let newObj = {...obj}newObj = Object.assgin({}, obj)//循环的方式for in只能遍历可枚举的newObj = {}_.each(obj,(value,key)=>{newObj[key] = value})//和第一种方法区别:Symbol不能遍历//解决for in缺点let keys = {...Object.keys(obj),...Object.getOwnPropertySymbol(obj)}//浅拷贝源码function shallowClone (){let type = _.toType(obj),//检测数据类型Ctor = obj.constructor//对于Symbol BigInt特殊处理,不做处理的话和之前相等if(/^(symbol|bigint)$/i.test(type)) return Object(obj)//正则和日期的处理if(/^(regexp|date)$/i.test(type)) return new Ctor(obj)//对于错误对象if(/^error$/i.test(type)) return new Ctor(obj.message)//对于函数if(/^function$/i.test(type)){return function(){//返回新函数:新函数执行还是把原函数执行,实现和原函数相同的效果return obj(this,...arguments)}}if(/^(object|arrary)$/i.test(type)){return type === "arrary" ? [...obj] : {...obj}//let result = new Ctor()//_.each(obj,(_,key)=>{result[key] = obj[key]})let result = new Ctor(),keys = [...Object.keys(obj),...Object.getOwnPropertySymbol(obj)]_.each(keys,(_,key)=>{result[key] = obj[key]})}//基本数据类型return obj}//深克隆:只要有下一级的,我们就克隆一次function deepClone(obj,cache = new Set()){let type = _.toType(obj),//检测数据类型Ctor = obj.constructor//不是数组和对象if(!/^(object|arrary)$/i.test(type)) return shallowClone(obj)//避免无限套娃if(cache.has(obj)) return objcache.add(obj)let result = new Ctor(),keys = [...Object.keys(obj),...Object.getOwnPropertySymbol(obj)]//再次调用deepClone时候,把cache传递过去,保证每一次递归都是一个cache_.each(keys,(_,key)=>{result[key] = deepClone(obj[key]),cache})return result}//上述写法会存在栈溢出错误obj.xxx = {0:obj}
对象的merge合并
- 两个对象合并的意义,插件组件封装(参数处理),业务需求(两个多个接口数据合并)



//实现两个对象的合并,基于浅比较实现的对象合并let options = Object.assgin(options,params)//params替换optionsfunction merge(){}



