js相关面经

严格模式

严格模式对 Javascript 的语法和行为,都做了一些改变。

  1. 变量规定

① 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var 命令声明,然后再使用。

② 严禁删除已经声明变量。例如,delete x; 语法是错误的。

  1. 严格模式下 this 指向问题

① 以前在全局作用域函数中的 this 指向 window 对象。

② 严格模式下全局作用域中函数中的 this 是 undefined。

③ 以前构造函数时不加 new也可以 调用,当普通函数,this 指向全局对象

④ 严格模式下,如果 构造函数不加new调用, this 指向的是undefined
如果给他赋值则 会报错

⑤ new 实例化的构造函数指向创建的对象实例。

⑥ 定时器 this 还是指向 window 。

⑦ 事件、对象还是指向调用者。

  1. 函数变化

① 函数不能有重名的参数。

② 函数必须声明在顶层.新版本的 JavaScript 会引入“块级作用域”( ES6 中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。

this

this的指向:this不是固定不变的,是根据调用的上下文(执行时环境)改变而改变。

  1. 单独使用,指向window
  2. 方法中,表示方法所属的对象,即哪个对象调用它就指向哪个对象
  3. 函数中,默认表示window,有调用者指向调用者
  4. 在事件中,this 表示与事件绑定的元素的元素。
  5. new实例化对象,构造函数中的this指向实例对象
  6. 箭头函数没有自己的this,函数内的this指向函数定义时作用域对象(所处的对象)
undefined和null
  1. undefined不是关键字,而null是关键字;
  2. 转换为布尔值时,都是false,所以不能用==来判断它们两个是否相等
  3. 使用Number对它们进行类型转换,undefined—>NaN,null—>0
let、const、var
  1. 作用域
  2. const的值原则上不可改变,但是它存的是地址!所以碰到对象、数组需要修改值的时候是可以的。
  3. 变量提升和暂时性死区
  4. var可以重复声明变量
var a=4;
var a;
log(a);//a=4;
//a是undefined所以不受影响,是同一个东西
var a=4;
var a=5;
log(a);//a=5
//这是赋值了。
toString()
数据类型toString方法
1(即纯数字).toString()报错
false.toString()“false”
“foo”.toString()“foo”
undefined.toString()报错
null.toString()报错
(String、Number、Boolean、Array、Function、Date、RegExp返回构造函数
Error、Promise、Object、Math).toString(),返回构造函数
Object.prototype.toString()[object Object]
Object.prototype.toString.call(String)[object String]
Object.prototype.toString.call(null)
Object.prototype.toString.call(Undefined)
[object Null]
[object Undefined]
数值范围

JS中Number是双精度浮点型

JS的64位

第一位:用来表示正负 0表示正 1表示负
第2~第12位 (共11位)用来表示指数部分(决定大小)
第13~第64位(共52位) 用来表示小数部分(决定精度)

浮点数计算

0.1+0.2 == 0.3

解决方法:
  1. 用Number.epsilon和a-b的abs比较,可能会不兼容设置为最小值
  2. 化为整数计算再初以放大倍数得到结果
Array方法

判断数组:
Array.isArray、instanceOf、Array.prototype.isPrototypeOf(arr)、Object.getPrototypeOf(arr)==Array.prototype

转换方法:
toString、valueOf

栈/队列:
push返回值是当前数组元素的个数、pop、unshift、shift

排序:
sort、reverse

操作方法:
slice(index)返回xin’shu’zu、splice(index,0/1,…参数),关于splice如果只有两个参数就表示从index开始删除j个元素返回被删除的数组,他会修改数组噢

位置方法:
indexOf、lastIndexOf

迭代方法:
filter返回新数组,every全true返会true,forEach无返回值,map返回执行了操作的新数组,some任一项true就true

迭代的方法参数都是函数function(item,index,array)

归并方法:
reduce,reduceRight

归并方法的参数是函数function(prev,cur,index,array),prev是上一次的返回值,初值是arr[0],cur是当前索引下的值

连接
concat方法,参数是各数组或者参数集?返回值是新数组;join

ES6新增:

Array.from(类似数组或iterable,如map)

Array.of(传参),生成数组

find,findIndex参数都是函数,一个返回满足条件的第一个值,一个返回索引

RegExp类型

g:全局 i:表示不区分大小写 m:多行

\是转义符,碰到[]\.+() {} ?等想要它以字符的形式出现就得转义

/at/ 匹配第一个有at的,/at/g 匹配第一个有at的,/.at/ 匹配第一个以at结尾的三个字符的组合(.表示占位,.在前面后面有差别),/[bc]at/ 匹配第一个的cat或者bat

字符串方法

根据索引查找:
charAt、charCodeAt返回ascii码、str[i]

操作方法:
slice、substring、substr

传入一个正参数:都表示截取从该字符到结尾;负参数:slice和substr参数取-n+length,而substring置0

eg:“abcde”

不传参数-----abcde

传一个正参数-------从index到结束

传一个负参数-------sunstring校0,其他的都通过+length化为正整数在计算

传入两个参数:slice表示截取[i,j),substring同但是遇到负数转为0并把参数范围校正,substr是指从i开始的j个元素,j<0时置0

传入两个参数

对于slice:[a,b)的值,如果其中有负数,化为正数,如果a>b,返回空字串

对于substring:[a,b)的值,如果有有负数化为0,如果a>b,变为[b,a)

对于substr:因为第二个参数是指长度,如果是负数置0

找索引:
indexOf、lastIndexOf都只返回第一个和最后一个

trim
方法返回新串,删除前缀后缀的空格

大小写转换:
toLocaleUpperCase(),toLocaleLowerCase()

匹配:
startsWith()

模式匹配方法:

  1. match方法返回数组内容分别是字符串、该字符串出现的索引、input以及groups。如果是g模式,则直接返回满足条件的字符串组成的数组
  2. search方法返回满足条件的第一个索引
  3. replace(“a”,“b”);//用b替换a,可以用模式匹配!不改变字符串

localeCompare根据字符串在字典中的顺序返回1,0,-1

fromCharCode参数是一组ascii码值,用他们凑成一个字符串并返回

apply、call和bind

参数问题,obj填当前类!

否则,填对象

apply和call无差别,作用:改变this指向,扩充作用域。传参有分别,apply传数组或arguments参数,call必须写明所有的参数值

bind生成一个实例对象,参数作为this传入,函数需要调用。

typeof和instanceof

typeof的返回值:number、string、undefined、boolean、function、object

  • typeof对于原始类型来说,除了null都可以显示正确类型
  • typeof对于对象来说,除了函数都会显示object

instanceof 检测的是原型

如果需要判断一个值是否为null,最直接就是与null比较

value === null;  //true or false
执行上下文

全局上下文(唯一)、函数环境和Eval环境

每个函数都有执行上下文,当JavaScript程序执行不同的函数时会进入不同的执行上下文,一个执行上下文可能触发另外的执行上下文,比如一个函数调用另一个函数,故会产生上下文堆栈。栈底的元素始终是全局执行上下文,栈顶的元素始终是正在执行的函数的执行上下文。

作用域链

当前执行上下文的变量/活动对象+函数内部的特殊的scope属性

作用域链是一个对象列表,是内部上下文所有变量对象(包括父变量对象)的列表。解析标识符(变量名称、函数声明、形参等)时,会从作用域链中当前活动变量对象中查找,如果没有找到则会向作用域链的上一层查找,直到找到为止。

变量对象
作用域链和原型链

某执行上下文环境中的变量和函数声明。函数执行上下文中变量对象又叫活动对象(activitive Object,简称AO),活动对象包含arguments,传进去的形参,以及函数内部自定义的变量和函数声明。

当这些函数或者对象拥有原型时,原型链和作用域链的查找顺序是:在当前执行上下文中会先查找该执行上下文的活动变量,然后查找其原型链_proto__(如果有原型链),如果原型链上没有,再查找[[Scope]]中活动对象父作用域链,深入每一个作用域链的原型链。

闭包

由于作用域链的存在,作用域内部能够访问外部的变量(作用域链作用),函数处于全局作用域中,所以函数内部能够访问全局变量。闭包是在作用域外部能够访问作用域内部的变量,或者说,可以在函数外部访问函数内部的变量,换一种更广泛的说法就是可以 凡是访问函数内部的变量的都叫闭包。至于他的作用和缺点都是因为JS回收机制导致的,因为JS会在变量使用过后将其清理,但是闭包调用了函数的变量,所以函数的变量将会一直保存下来,又因为变量处于函数作用域中,变量的存储环境比较干净,不会被污染,所以闭包经常用来保存一个需要持久保存的变量。函数内部能访问到外部上级作用域的变量是因为作用域链的存在。从函数外部能访问函数内部的变量就是闭包。

优点:

1、保护函数内部的变量安全,实现封装
2、可以把这个变量当作缓存来使用
弊端:无法销毁变量,使用不当,容易造成内存泄漏

浅拷贝和深拷贝

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

垃圾回收

js有自己的一套垃圾回收机制(Garbage Collection)。JavaScript的解释器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用的内存释放掉了

标记清除

​ 当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了,因为环境中的变量已经无法访问到这些变量了,然后垃圾回收器相会这些带有标记的变量机器所占空间。

引用计数

引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。

注意:
当存在循环引用的时候,引用计数会导致问题。要解除变量的引用,即及时解除不再使用的全局对象、全局对象属性以及循环引用变量的引用,设为null。

v8垃圾回收

V8采用了一种代回收的策略,将内存分为两个生代:新生代(new generation)老生代(old generation)。新生代中的对象为存活时间较短的对象,老生代中的对象为存活时间较长或常驻内存的对象,分别对新老生代采用不同的垃圾回收算法来提高效率,对象最开始都会先被分配到新生代(如果新生代内存空间不够,直接分配到老生代),新生代中的对象会在满足某些条件后,被移动到老生代,这个过程也叫晋升。

新生代垃圾回收算法

Scavenge算法中的Cheney算法:内存一分为二,一个处于使用状态From,另一个处于闲置状态To,当分配对象时,先是在From空间中进行分配。当开始进行垃圾回收算法时,会检查From空间中的存活对象,这些存活对象将会被复制到To空间中(复制完成后会进行紧缩),而非活跃对象占用的空间将会被释放,用到了两个指针,从根对象用到的对象开始扫描,看他们用到了哪些对象,复制,最后活动对象都在To中,From中则是所有对象,直接释放。完成复制后,From空间和To空间的角色发生对换。

对象的晋升

当一个对象经过多次新生代的清理依旧幸存,这说明它的生存周期较长,也就会被移动到老生代,这称为对象的晋升。具体移动的标准有两种:

  1. 对象从From空间复制到To空间时,会检查它的内存地址来判断这个对象是否已经经历过一个新生代的清理,如果是,则复制到老生代中,否则复制到To空间中。
  2. 对象从From空间复制到To空间时,如果To空间已经被使用了超过25%,那么这个对象直接被复制到老生代。
老生代垃圾回收算法

保存的对象大多数是生存周期很长甚至是常驻内存的对象,老生代占用的内存比较多。垃圾回收策略采用Mark-SweepMark-Compact相结合。V8主要使用Mark-Sweep,在空间不足以对从新生代中晋升过来的对象进行分配时,才使用Mark-Compact。

Mark-Sweep

标记清除分为标记和清除两个阶段。

在标记阶段需要遍历堆中的所有对象,并标记那些活着的对象,然后进入清除阶段。在清除阶段中,只清除没有被标记的对象。由于标记清除只清除死亡对象,而死亡对象在老生代中占用的比例很小,所以效率较高。

标记清除有一个问题就是进行一次标记清楚后,内存空间往往是不连续的,会出现很多的内存碎片。如果后续需要分配一个需要内存空间较多的对象时,如果所有的内存碎片都不够用,将会使得V8无法完成这次分配,提前触发垃圾回收。

Mark-Compact

标记整理正是为了解决标记清除所带来的内存碎片的问题。标记整理在标记清除的基础进行修改,将其的清除阶段变为紧缩极端。在整理的过程中,将活着的对象向内存区的一段移动,移动完成后直接清理掉边界外的内存。紧缩过程涉及对象的移动,所以效率并不是太好,但是能保证不会生成内存碎片。

js中数组 内存分配

数组分为慢数组和快数组,慢数组索引线性连续存储,慢数组使用HashTable存储。

数组的 elements 被分为许多类型,其每一个元素占用的内存大小取决于 elements 类型和元素中是否有浮点数。如果有浮点数或者 elements 类型为 PACKED_ELEMETNS,那么每个占8字节,否则4字节

除数字以外的其他类型数据都会以指针的方式存储再数字中,所以除了一些 SMI 不能表示的数字,其他类型和 SMI 能表示的值都可以由32位(4字节)表示。

数组的扩容和收缩机制:如果给数组赋值超出容量的下标会触发扩容机制(不能相差太大,不然会转换位慢数组);如果容量大于等于 length * 2 + 16,会触发收缩机制。

快数组转慢数组
  • 如果快数组扩容后的容量是原来的 9 倍以上,意味着它比 HashTable 形式存储占用更大的内存,快数组会转换为慢数组
  • 如果快数组新增的索引与原来最大索引的差值大于 1024,快数组会被转换会慢数组
慢数组转快数组
  • 当慢数组转换成快数组能节省不少于 50% 的空间时,才会将其转换。
V8执行js的过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BwaP8JaD-1650204450291)(./js代码在v8的执行.jpg)]

核心流程编译运行

CPU只能识别机器代码,二进制指令

计算机如何执行高级语言

第一种:解释执行,需要先将输入的源代码通过解析器编译成中间代码 ,之后直接使用解释器解释执行中间代码,然后直接输出结果;

第二种:编译执行,采用这种方式也需要先将源代码转换为中间代码 ,然后我们的编译器再将中间代码编译成机器代码。通常编译成的机器代码是以二进制文件形式存储的,需要执行这段程序的时候直接执行二进制文件就可以了。

编译器:
优点:将源代码编译成可被CPU直接执行的机器指令,因为执行效率更高;
缺点:跨平台支持不好,不同CPU有不同指令集,同一套源代码需要被编译成不同机器语言;
解释器:
优点:更容易跨平台,解释器自身会将源代码转换为当前平台所需的机器语言;
缺点:执行效率低一些,因为每句源码都要经过解释器解释为可执行的机器语言;

执行的具体流程
  1. 执行之前,准备所需的基础环境
    在 V8 启动执行 JavaScript 之前,它还需要准备执行 JavaScript 时所需要的一些基础环境,这些基础环境包括了 堆空间、栈空间、全局执行上下文、全局作用域、消息循环系统、内置函数 等,这些内容都是在执行 JavaScript 过程中需要使用到的。

比如:

  • JavaScript 全局执行上下文就包含了执行过程中的全局信息,比如一些内置函数,全局变量等信息;
  • 全局作用域包含了一些全局变量,在执行过程中的数据都需要存放在 内存 中;
  • 由于 V8 采用了经典的堆和栈的管理内存管理模式,所以 V8 还需要初始化了内存中的堆和栈结构;
  • 另外,要我们的 V8 系统活起来,还需要 初始化消息循环系统 ,消息循环系统包含了 消息驱动器 和 消息队列 ,它如同 V8 的心脏,不断接受消息并决策如何处理消息。
  1. 准备好基础环境,向V8提交要执行的JS代码
    首先,V8 会接收到要执行的 JavaScript 源代码,不过这对 V8 来说只是一堆 字符串,V8 并不能直接理解这段字符串的含义,它需要 结构化 这段字符串;
  2. 结构化字符串(JS源代码)
    结构化,是指信息经过分析后可 分解成多个互相关联的组成部分。各组成部分间有明确的层次结构,方便使用和维护,并有一定的操作规范;
  3. 生成AST、作用域
    结构化之后,就生成了 抽象语法树(AST),AST 是便于 V8 理解的结构;在生成 AST 的同时,V8 还会 生成相关的作用域,作用域中存放相关变量;
  4. 生成字节码
    有了 AST 和 作用域 之后,接下来就可以生成 字节码 了,字节码是介于 AST 和 机器代码 的中间代码。但是与特定类型的机器代码无关,解释器可以直接解释执行字节码 ,或者通过编译器将其编译为二进制的机器代码再执行;
  5. 解释器解释执行字节码
    生成字节码之后,解释器就登场了,它会 按照顺序 解释执行字节码,并输出执行结果;

注意:关于热点代码
在解释执行字节码的过程中,如果发现了某一段代码会被重复多次执行,那么监控器会将这段代码标记为 热点代码。当某段代码被标记为热点代码后,V8 就会将这段字节码丢给 优化编译器,优化编译器会在后台将字节码编译成二进制代码,然后再对编译后的二进制代码执行优化操作,优化后的二进制机器代码的执行效率会得到大幅提升。如果下面再执行到这段代码时,那么 V8 会优先选择优化之后的二进制代码,这样代码的执行速度就会大幅提升。

任务队列

因为 JavaScript 是一个单线程序的解释器,因此一定时间内只能执行一段代码。
为了控制要执行的代码,就有一个 JavaScript 任务队列。
这些任务会按照将它们添加到队列的顺序执行。
setTimeout() 的第二个参数告诉 JavaScript 再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行

为什么要分微任务和宏任务?

微任务是线程之间的切换,速度快。不用进行上下文切换,可以快速的一次性做完所有的微任务。

宏任务是进程之间的切换,速度慢,且每次执行需要切换上下文。因此一个Eventloop中只执行一个宏任务。

事件传播

DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。

  • 在事件捕获阶段,事件从document对象沿着文档树向下传播给目标节点。如果目标的任何一个祖先注册了捕捉事件句柄,那么在事件传播的过程中,就会运行这些句柄(IE10及以下不支持捕获型事件)。
  • 目标阶段发生在目标节点自身,这与0级事件模型提供的事件处理方法类似。
  • 事件冒泡阶段,在这个阶段,事件将从目标元素向上传播回(像冒泡)Document对象的文档层次(IE8 及以下没有捕获阶段)。

dom标准事件流的触发的先后顺序为:先捕获再冒泡,即当触发dom事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。

原型和原型链:

in找到原型链上的所有可枚举的属性,hasOwnProperty找到实例上有的,所以 Object.prototype.proto 的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思。所以查找属性的时候查到 Object.prototype 就可以停止查找了。

原型:实例上找不到的会去隐式原型上找

实例对象的隐式原型=构造函数的显示原型

每个函数function都有一个prototype, 即显式原型(属性)
每个实例对象都有一个__ proto_,可称为隐式原型(属性)

  1. 函数的显式原型都默认是:空的Object实例对象(除了Object)

把自己看作是构造函数时用prototype另一边就是其实例的_proto_,找自己的原型的时候,真的是往上找,语句:A.prototype instanceOf Object

换种方式表达:A.prototype._proto_= Object.prototype

A.prototype(显式原型)=a._proto_(隐式原型)
A.prototype.constructor=A;

在这里插入图片描述

  1. 所有的函数都是Function的实例对象(包括Function本身)
Function、Object很特殊

它是new自己得到的,所以Function自己既是构造函数又是实例对象

函数我们一般都讨论的显示原型!也是因为此,所有函数的隐式原型都是一样的!都是new Function出来的,所以他们的构造函数都是Function

Function._proto_=Function.prototype
//function的原型对象是空Object实例
Function.prototype._proto_=Object.prototype
//而Object也是函数,那么他也是Function的实例
Object._proto_=Function.prototype

原型链:原型上也找不到就去原型的原型上找,,一直找到Object的原型对象为止。

instanceof

表达式: A instanceof B
如果B函数的显式原型对象在A对象的原型链上,返回true, 否则返回false

访问一个实例对象的属性时,先在自身属性中查找,找到返回
如果没有,再沿着_ proto_ 这条链向上查找, 找到返回
如果最终没找到,返undefined

判断方法 是自己的还是原型的用Object的hasOwnProperty方法

实例成员和静态成员

实例成员:构造函数内部通过this添加的成员,实例成员只能通过实例化的对象来访问,不可以通过构造函数本身来访问

静态成员:在构造函数本身上添加的成员

原型上的:实例对象可以访问,构造函数只能利用原型来访问

delete

作用:delete 操作符用于删除对象的某个属性;如果没有指向这个属性的引用,那它最终会被释放。

注意:

  • 如果你试图删除的属性不存在,那么delete将不会起任何作用,但仍会返回true
  • 如果对象的原型链上有一个与待删除属性同名的属性,那么删除属性之后,对象会使用原型链上的那个属性(也就是说,delete操作只会在自身的属性上起作用)
  • 任何使用var声明的属性不能从全局作用域或函数的作用域中删除。
  • 这样的话,delete操作不能删除任何在全局作用域中的函数(无论这个函数是来自于函数声明或函数表达式)
  • 除了在全局作用域中的函数不能被删除,在对象(object)中的函数是能够用delete操作删除的。
  • 任何用letconst声明的属性不能够从它被声明的作用域中删除。
  • 不可设置的(Non-configurable)属性不能被移除。这意味着像MathArrayObject内置对象的属性以及使用Object.defineProperty()方法设置为不可设置的属性不能被删除。
堆和栈

Stack为自动分配的内存空间,它由系统自动释放;

Heap则是动态分配内存,大小不定也不会自动释放;

**基本类型:**存放在栈中的简单数据段,数据大小确定,内存空间大小可以分配。 5种基本数据类型:undefined、null、boolean、number和string,他们是直接按值存放的,所以可以直接访问。

关于string的类型

在js中,变量没有类型,数据有类型的,js的数据类型分为两类,1. 基本数据类型 (Undefined , Null , Boolean , Number , Infinity , Nan , string ) 2. 引用类型( object ) .。为什么第一种数据类型叫基本数据类型,而不叫值类型,因为string比较特殊。string的定义是引用类型的,但其操作是值类型,即定义一个变量x,将字符串“abc”赋值到x中,这时x指向“abc”数组的首地址,当将x赋值给y时,也是将数组的首地址赋值给y,这样堆中的数据未改变,x和y同时指向堆中的数组首地址,而当要修改y的值时,也就是通过y修改数组时,堆里会在申请一个空间,将原来的值copy进去再新修改数组值,修改过后,通过x访问的是未修改的字符串数组,通过y访问的是修改过的字符数组,所以说,string定义是引用类型,操作时就当值类型用。

**引用类型:**存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况进行特定的分配。

防抖

对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。

首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:

  • 如果在200ms内没有再次触发滚动事件,那么就执行函数
  • 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
节流

类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)

ajax、axios和fetch区别
ajax:

异步网络请求,是一种与服务器交换数据的技术,页面可以无刷新的请求数据,核心使用 XMLHttpRequest 对象。

特点:

  1. 多个请求之间如果有先后关系的话,就会出现回调地狱。
  2. 针对MVC的编程,不符合前端MVVM的浪潮
  3. 配置和调用方法非常的混乱,而且基于事件的异步模型不友好。
axios

基于 promisehttp请求库,可以用在浏览器和 node.js 中,本质上也是对原生XHR的封装,只不过它是Promise 的实现版本,符合最新的ES规则。

特点:

  1. 从浏览器中创建XMLHttpRequest
  2. 支持 Promise API
  3. 客户端支持防止 CSRF
  4. 提供了一些并发请求的接口(重要,方便了很多的操作)
  5. 从 node.js 创建 http 请求
  6. 拦截请求和响应
  7. 转换请求和响应数据
  8. 取消请求
  9. 自动转换JSON数据
fetch

是基于 promise 设计的,原生 JS , 没有使用 XMLHttpRequest 对象。

  1. 语法简洁,更加语义化
  2. 基于标准 Promise 实现,支持 async / await
  3. 同构方便
  4. 更加底层,提供的 API 丰富 (request, response)
  5. 脱离了 XHR,是 ES 规范中新的实现方式。

缺点:

​ 1、fetch 只对网络请求报错,对400,500都当作成功的请求,服务器返回400, 500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时, fetch 才会被 reject。需要封装去处理。

2、fetch 默认不会带 cookie,需要添加配置项: fetch(url, {credentials: ‘include’})

3、fetch 不支持 abort。xhr 有个 xhr.abort 方法可以直接阻断请求),不支持超时控制,使用 setTimeout 及 Promise.reject 的实现的超时控制并不能阻止请求,请求过程继续在后台运行,造成了流量的浪费。

4、fetch 没有办法原生监测请求的进度,而 XHR 可以。

5、fetch 兼容性并不太好,IE 不支持

Set

底层用了hasOwnProperty方法,其实它存的是set[value]=value;

symbol

为解决属性名冲突问题而生。Symbol能够表示一个独一无二的值,这就从根本上防止方法或者属性名的冲突

基本使用:
let s1 = Symbol();
let s2 = Symbol('another symbol');
应用场景:
  1. 没有字面量
  2. 永远不会相等,typeof的返回结果是symbol
  3. 可以作为属性名obj={[mySymbol]:'hello'}
  4. Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。只能使用Reflect.ownkeys()
  5. 代替常量
  6. 没法转换,不能做数学运算、字符串拼接等
Map和Object的区别

Object和Map非常相似,两者都可以完成键-值对的设置、获取value、删除key并且根据key获取对应的value。因此,在过去Object经常被当作Map来使用。但是,Object和Map的不同之处会让Map在特定条件下成为一个更佳的选择。

MapObject
附加的KeyMap没有默认的key值Object具有原型对象,所以它包含默认的key值,并且使用不当时会和自定义的key值产生冲突(在ES5中可以通过Object.create(null)来设置去掉默认的key值,但这种解决方法并不常用)
Key的种类Map的key值可以是任何类型的值,包括函数、Object和任意基础数据类型Object的key值只能是String或Symbol
Key的顺序Map中的key值排序简单直接,一个Map对象迭代键值对、Key、Value的顺序和插入时的顺序相同一般对象的键值是有顺序的,但这并不绝对,有时对象的键值排序会变得很复杂,所以最好不要依赖于插入的顺序。
大小Map的大小可以轻松通过size属性来获得Object的大小必须通过自行获取
迭代Map是可迭代对象,可以轻松完成迭代Object没有实现迭代协议,所以无法被for…of直接迭代(但可以自行实现迭代协议,或者使用Object.keys()Object.entries()来迭代对象的键值和实体,for…in也可以迭代Object的可枚举属性)
性能频繁增减键值对时表现会更好频繁增减键值对时表现较差
class和function
  1. function可以不用new关键字,class一定要用。new的作用是过滤掉function声明构造函数的返回值。
  2. function有变量提升,class没有
  3. class中的static 属性:用于定义类中的静态方法和静态属性关键字,该方式声明的方法与属性,只能通过类名调用,可被继承,并且定义的方法的this指向类而不是实例。
  4. 在class中所有方法和属性都是定义在class的原型上面,使用时也是调用其原型上的方法(注:类中定义的方法都是不可枚举的)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值