js数据类型检测

  • type of

    机制

  1. 直接在计算机底层基于数据类型的值(二进制)进行检测

    缺点

  2. 检测null时为Object

原因:对象存储在计算机中,都是以000开始的二进制存储,null也是。

  1. 检测普通对象、数组对象、正则对象、日期对象,返回的结果都是”Object”
  • instanceof(检测当前实例属不属于这个类)解决typeof不能检测对象的具体类型

    机制

    1. 只要当前类出现在实例的原型链上,结果都是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

  1. <a name="3zJ2M"></a>
  2. #### 缺点
  3. 1. 基本数据类型检测不出来
  4. 1. 由于我们可以肆意修改原型的指向,所以检测结果不准确。
  5. <a name="ihmSC"></a>
  6. #### 手斯instanceof
  7. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1699388/1616382455304-b98aa90d-96e7-4b7d-9c33-4b6778a194b1.png#height=382&id=ohWBn&margin=%5Bobject%20Object%5D&name=image.png&originHeight=764&originWidth=1470&originalType=binary&ratio=1&size=462774&status=done&style=none&width=735)
  8. - constructor
  9. 用起来比instanceof好,可以检测基本数据类型
  10. ```javascript
  11. let a = []
  12. console.log(a.constructor === Arrary) //true
  13. console.log(a.constructor === RegExp) //false
  14. console.log(a.constructor === Object) //false
  15. let b = 1
  16. console.log(b.constructor === Number) //true
  17. b.prototype.constructor = 'aaaaaa'
  18. console.log(b.constructor === Number) //false

缺点

construct可以随便改,也不准

  • Object.prototype.toString.call([value])

标准检测方法,Object.prototype.toString不是返回一个字符串,返回的当前实例所属类的信息。

  1. let obj= {name: '王某某'}
  2. obj.toString()//'[Object Object]'

检测数据类型的方法

image.png

js中三类循环对比及性能分析

for循环及forEach底层原理

FOR循环是自己控制循环过程
1.基于var声明的时候,for和while性能差不多(不确定循环次数的时候用while)
2.基于let循环的时候,for循环的性能更好,「没有创造全局不释放变量」let有块级上下文
3.重写foreach(比上画性能差)手撕数组迭代方法

  1. Array.prototype.forEach = function forEach(callback, context){
  2. let self = this,
  3. i = 0,
  4. len = self.length;
  5. context = context == null ? window :context
  6. for (; i < len; i++) {
  7. typeof callback === 'function' ? callback.call(context, self[i], i) : null
  8. }
  9. }

for in循环的BUG解决方案

1.迭代所有可枚举属性,「私有(大部分是可枚举的,length是不可枚举的)&公有(出现在类的原型链上的,也有部分是可枚举的)」,按照原型链一级级查找很耗性能
2.问题很多,不能迭代symbol属性、迭代属性会以数字属性优先、共有可枚举的{一般是自定义属性}属性也会进行迭代
(1)遍历的顺序以数字优先
(2)无法遍历symbol属性
(3)可以遍历共有中可枚举的

  1. Object.prototype.fn = function fn(){}
  2. let obj = {
  3. name: 'hahahah',
  4. age: '13',
  5. [Symbol('AA')]: 100,
  6. 0: 200,
  7. 1: 300
  8. }
  9. for(let key in obj) {
  10. if(!obj.hasOwnProperty(key)) break; //解决问题3
  11. console.log(key);
  12. }
  13. //0,1,name,age,fn
  14. let keys = Object.keys(obj)
  15. if(typeof Symbol !== 'undefined') keys = keys.concat(Object.getOwnPropertySymbols(obj))
  16. keys.forEach(key => {
  17. console.log(`属性名:${key},属性值:${obj[key]}`); //Symbol不能以这种方式转换为字符串
  18. }) //解决问题2,不兼容IE678

for of循环底层机制

1.迭代器iterator规范(部分数据结构实现了迭代器规范,有Symbol.iterator)「具备next方法,每次执行返回一个对象,具备value/done属性」数组,类数组,Set,Map「对象没有实现」
2.让对象具备可迭代性并且使用for of循环

  1. let arr = [10, 20, 30]
  2. arr[Symbol.iterator] = function () {
  3. let self = this,
  4. index = 0
  5. return {
  6. next () {
  7. //必须具备next方法,执行一次next方法,拿到结构中的某一项值
  8. //done:false value:每一次获取的值
  9. if (index > self.length - 1) {
  10. return {
  11. done: true,
  12. value: undefined
  13. }
  14. }
  15. return {
  16. done: false,
  17. value: self[index++]
  18. }
  19. }
  20. }
  21. }
  22. // 1.let itor = arr[Symbol.iterator]() 2.itor.next()...
  23. let obj = {
  24. 0: 100,
  25. 1: 200,
  26. 2: 300,
  27. 3: 400,
  28. length: 4
  29. }
  30. obj[Symbol.iterator] = Array.prototype[Symbol.iterator]
  31. console.log(obj[Symbol.iterator]);
  32. for (let val of obj) {
  33. console.log(val);
  34. }
  35. //实现类数组的遍历

谈谈你对this的了解及应用场景

  • this的五种情况分析(this执行主体,谁把它执行的,和在哪创建,在哪执行没有必然关系)
  1. 函数执行,看方法前面是否有点,没有点this是window「严格模式下this是underfined」,有点,点前面是谁this是谁

    1. const fn = function fn() {
    2. console.log(this)
    3. }
    4. let obj = {
    5. name:'Obj',
    6. fn: fn
    7. }
    8. fn() //window
    9. obj.fn() //obj
  2. 给当前元素的某个事件行为绑定方法,当事件行为触发,方法的this是当前元素的本事「排除attachEvent」

    1. document.body.addEventListener('click', function(){
    2. console.log(this);
    3. })//body
  3. 构造函数体中的this是当前类的实例

    1. function Factory () {
    2. this.name = "hahahah"
    3. this.age = "123"
    4. console.log(this)
    5. }
    6. let f = new Factory //Factory
  4. 箭头函数中没有执行主体,所用的this都是其所处上下文的this

    1. let demo = {
    2. fn(){
    3. console.log(this) //demo
    4. setTimeout(function(){
    5. console.log(this)
    6. },1000) //window
    7. setTimeout(()=>{
    8. console.log(this)
    9. },1000)//demo
    10. }
    11. }
    12. demo.fn()
  5. 可以基于Function.prototype伤的call/apply/bind去改变this的指向

    1. function func (x, y) {
    2. console.log(this, x, y)
    3. }
    4. let obj = {
    5. name:'OBJ'
    6. }
    7. //func函数基于__proto__找到Function.prototype.call把call方法执行
    8. //call(context->obj,...params->[10,20])
    9. //在call方法内部「call执行的时候」把func中this改为传入的第一个this改为obj
    10. //并且把params接收的值当作实参传递给func函数,并且让func函数立即执行
    11. func.call(obj,10,20)
    12. func.apply(obj,[10,20])
    13. document.body.addEventListener('click',func.bind(obj,10,20))
    14. //func函数基于__proto__找到Function.prototype.bind把bind方法执行
    15. //和call和apply区别,并没有把func立即执行
    16. //把传递来的值存储起来「闭包存储」
    17. //执行bind会返回一个新的函数 例如:proxy,把proxy绑定给元素的事件,当事件触发执行的是返回的proxy,在proxy内部
    18. //再去把func执行,把this和值都改变为之前存储的那些内容
  6. 手撕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,

    1. 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)) } }

  1. 7. 掌握this好玩的应用:鸭子类型
  2. ```javascript
  3. Arrary.protopype.slice = function slice(){
  4. let self = this,
  5. result = []
  6. for(let i = 0; i < self.length,i++){
  7. result.push(self[i])
  8. }
  9. return result
  10. }
  11. //克隆返回一个新数组,浅拷贝arr.slice()
  12. //像鸭子,我们就说他是鸭子,类数组像数组「结构,一些操作」不允许用数组的方法我们让其用数组的方法「不能直接用」
  13. function func () {
  14. console.log(arguments)
  15. //把arguments变为数组,这样就可以用数组的方法:Arrary.from/[...arguments]/...
  16. let result = []
  17. for(let i = 0; i < arguments.length,i++){
  18. result.push(arguments[i])
  19. }
  20. return Arrary.prototype.slice.call(arguments)
  21. [].forEach.call(arguments,item=>{
  22. console(item)
  23. })
  24. }
  25. //如果让slice执行:[].slice(),Arrary.prototype.slice()并且让this指向arguments,就相当于把arguments变为数组

image.png
image.png

HTTP网络层优化

  • CRP先分析底层原理,围绕底层原理一步步优化

    从输入URL到页面都发现了什么

  1. URL解析
    • 网络传输:TCP/IP(+TCP传输通道,IP主机地址,HTTP传输协议,HTTPS:SSL/TLS)FTP传输大文件
    • 端口号:每一个端口号代表一个项目 http:http:80 https:443 ftp:21
    • 编码:encodeURI/decodeURI

对整个URL编码:处理空格/中文

  • 编码:encodeURIComponent/decodeURIComponnet

    1. 对主要传递参数进行编码
  • escape/unescape:unescape不支持所有浏览器,不应用客户端与服务器

  • URI/URL/URI/URL/URN:区别

image.png

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

image.png
image.png
把服务器返回的标示存到浏览器中

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

image.pngimage.png

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

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

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

image.png
image.png
image.png

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

手撕源码

  1. //数组的浅拷贝
  2. let newArr = [...arr]
  3. newArr = arr.concat([])
  4. newArr = arr.slice()
  5. //对象的浅拷贝
  6. let newObj = {...obj}
  7. newObj = Object.assgin({}, obj)
  8. //循环的方式for in只能遍历可枚举的
  9. newObj = {}
  10. _.each(obj,(value,key)=>{newObj[key] = value})//和第一种方法区别:Symbol不能遍历
  11. //解决for in缺点
  12. let keys = {...Object.keys(obj),...Object.getOwnPropertySymbol(obj)}
  13. //浅拷贝源码
  14. function shallowClone (){
  15. let type = _.toType(obj),//检测数据类型
  16. Ctor = obj.constructor
  17. //对于Symbol BigInt特殊处理,不做处理的话和之前相等
  18. if(/^(symbol|bigint)$/i.test(type)) return Object(obj)
  19. //正则和日期的处理
  20. if(/^(regexp|date)$/i.test(type)) return new Ctor(obj)
  21. //对于错误对象
  22. if(/^error$/i.test(type)) return new Ctor(obj.message)
  23. //对于函数
  24. if(/^function$/i.test(type)){
  25. return function(){
  26. //返回新函数:新函数执行还是把原函数执行,实现和原函数相同的效果
  27. return obj(this,...arguments)
  28. }
  29. }
  30. if(/^(object|arrary)$/i.test(type)){
  31. return type === "arrary" ? [...obj] : {...obj}
  32. //let result = new Ctor()
  33. //_.each(obj,(_,key)=>{result[key] = obj[key]})
  34. let result = new Ctor(),
  35. keys = [...Object.keys(obj),...Object.getOwnPropertySymbol(obj)]
  36. _.each(keys,(_,key)=>{result[key] = obj[key]})
  37. }
  38. //基本数据类型
  39. return obj
  40. }
  41. //深克隆:只要有下一级的,我们就克隆一次
  42. function deepClone(obj,cache = new Set()){
  43. let type = _.toType(obj),//检测数据类型
  44. Ctor = obj.constructor
  45. //不是数组和对象
  46. if(!/^(object|arrary)$/i.test(type)) return shallowClone(obj)
  47. //避免无限套娃
  48. if(cache.has(obj)) return obj
  49. cache.add(obj)
  50. let result = new Ctor(),
  51. keys = [...Object.keys(obj),...Object.getOwnPropertySymbol(obj)]
  52. //再次调用deepClone时候,把cache传递过去,保证每一次递归都是一个cache
  53. _.each(keys,(_,key)=>{result[key] = deepClone(obj[key]),cache})
  54. return result
  55. }
  56. //上述写法会存在栈溢出错误
  57. obj.xxx = {
  58. 0:obj
  59. }

对象的merge合并

  • 两个对象合并的意义,插件组件封装(参数处理),业务需求(两个多个接口数据合并)

image.png
image.png
image.png

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

image.png
image.png