JavaScript知识点整理

319 阅读16分钟

JavaScript知识点

(8)不改变原数组的方法

  • join 变成字符
  • Slice,截取
  • concat 合并数组

(三)栈和堆的区别

(1)栈由编译器自动分配释放空间,堆一般由程序员分配释放 ;
(2)栈存放在一级缓存中,调用完毕立即释放
(3)堆则是在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定; image.png

【执行上下文与作用域】

1.什么是作用域,什么是作用域链? ⭐⭐⭐⭐

  • 作用域:规定变量和函数的可使用范围称为作用域
  • 作用域链:每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链

2.什么是执行上下文?什么是执行上下文栈⭐⭐⭐⭐

(1)执行上下文
执行上下文分为:全局执行上下文和函数执行上下文 、eval执行上下文

  • 全局执行上下文(window)
    创建一个全局的window对象,并规定this指向window,执行js的时候就压入栈底,关闭浏览器的时候才弹出
  • 函数执行上下文(创建阶段+执行阶段)
    每次函数调用时,都会新创建一个函数执行上下文
    函数执行上下文分为创建阶段和执行阶段
    • 创建阶段:函数环境会创建变量对象:
      • arguments对象(并赋值)、函数声明(并赋值);
      • 变量声明(不赋值),函数表达式声明(不赋值);
      • 会确定this指向;会确定作用域
    • 执行阶段:变量赋值、函数表达式赋值,使变量对象编程活跃对象
  • eval执行上下文
    (2)执行栈:
  • 首先栈特点:先进后出
  • 当进入一个执行环境,就会创建出它的执行上下文,然后进行压栈
    当程序执行完成时,它的执行上下文就会被销毁,进行弹栈
  • 栈底永远是全局环境的执行上下文,
    栈顶永远是正在执行函数的执行上下文
  • 只有浏览器关闭的时候全局执行上下文才会弹出

【单线程,同步异步】

(一)线程

为什么JS是单线程的? ⭐⭐⭐⭐⭐

  • 这主要和js的用途有关,js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom
  • 如果是多线程的话,假如有一个线程要修改一个dom元素,另一个线程要删除这个dom元素,导致浏览器不知道该听谁的

(二)异步函数Promise

说说 Promise 的原理?你是如何理解 Promise 的? ⭐⭐⭐⭐⭐

promise相对于async…await的优缺点⭐⭐⭐

  • promise
    • 无法取消
    • 错误无法被try…catch捕获,但是可以被catch方法捕获
  • async传染力比较强

fetch优缺点 ⭐⭐⭐⭐

  • fetch脱离了XHR,基于promise实现
  • 对某些错误不会reject,比如状态码400、500
  • fetch不支持超时timeout处理
  • fetch默认不携带cookie,需要手动配置
  • fetch没有办法监测请求进度,而xhr可以

(三)宏任务和微任务

1. setTimeout,setInterval,requestIdleCallback 和 requestAnimationFrame 还有 Promise,这几个有什么区别

名称描述
setTimeout用于在指定的时间后执行一次回调函数。它会在延迟一段时间后将回调函数添加到事件队列,并在主线程空闲时执行。 可以通过clearTimeout取消计时器。
setInterval用于每隔一段时间重复执行一次回调函数。它会在每个周期间隔结束后将回调函数添加到事件队列,并在主线程空闲时执行。可以通过clearInterval取消周期性执行。
requestIdleCallback用于在主线程空闲时执行回调函数,以避免影响关键交互和动画。它会在浏览器空闲的时候调用回调函数,尽量利用未使用的计算资源。可以通过cancelIdleCallback取消回调。
requestAnimationFrame用于在下一帧绘制之前执行回调函数,通常用于执行动画或其他需要与浏览器绘制同步的任务。它会在浏览器进行下一次重绘之前调用回调函数。 在适当的时候使用cancelAnimationFrame取消回调。
Promise是一个表示异步操作最终完成或失败的对象。通过Promise,可以更容易地处理异步操作的结果,避免回调地狱。可以通过thencatch等方法链式调用,也可以使用async/await语法进行处理。

适用场景:

  • setTimeoutsetInterval适合处理简单的定时任务;
  • requestIdleCallbackrequestAnimationFrame则更适合处理需要精确时间控制或优化性能的任务;
  • Promise则是一种通用的异步编程模型,用于处理异步操作的结果和错误。

(四)手写 ajax⭐⭐⭐⭐

  1. 创建 XMLHttpRequest对象xhrxhr = new XMLHttpRequest()
  2. 建立一个 HTTP 请求: xhr.open(method, url, async, username, password)
  3. 发送请求xhr.send(body)
  4. 接收响应数据xhr.responseText
  5. 设置请求头(POST):
    • 使用 setRequestHeader() 方法设置请求消息的内容类型为“application/x-www-form-urlencoded”,它表示传递的是表单值,

ES6 知识点

【模块化】

1. ES6和commonjs的区别⭐⭐⭐

Commonjs、AMD、CMD、UMD、ESM 都有什么区别

(1)Commonjs(同步模块加载)

  • 同步模块加载,需要使用 require()指定依赖,
  • CommonJS 模块语法不能在浏览器中直接运行,不适合前端,后端 nodejs 可以使用 commonjs。
  • 使用方式:
module.exports = xxx
require('xxx')

(2)AMD(Asynchronous module definition:异步模块定义)

(AMD/CMD/UMD 适用前端 异步执行)

  • AMD 模块实现的核心是用函数包装模块定义。这样可以防止声明全局变量,并允许加载器库控制何时加载模块

  • 异步运行:异步模块定义,主要采用异步的方式加载模块,模块的加载不影响后面代码的执行。所有依赖这个模块的语句都写在一个回调函数中,模块加载完毕,再执行回调函数

  • AMD要求在使用前需要先把所有的模块都写出来

  • 使用方式:

define(["a","b","c","d","e"],function(a,b,c,d,e){
  // 相当于在前面声明并初始化了要用到的所有模块
  a.dosomething()
  if(false) {
    // 即使没有用到模块 b,也会提前执行
    b.dosomething()
  }	
})

(3)CMD(Common Module Definition:通用模块定义)

  • 异步运行
  • seajs 规范
  • AMD 和 CMD 的差别是:
    • AMD 是依赖前置(把依赖放在前面)、提前执行(即使没有用到某个模块,也会提前执行)
    • CMD依赖就近、延时执行(用到的时候在声明依赖
  • 使用方式:
define(function(require, exports, module){
  var a = require("./a") //需要的时候声明
  a.dosomething()
  if(false) {
    var b = require("./b")
    b.dosomething()
  }
})

(4)ESM(ES Module)

  • 使用 export 、 export default 来导出模块,使用 import 来引入模块
  • ESM 和 commonjs 的区别主要在于:
    • commonjs 是运行时加载 ;ESM 是编译时加载
    • commonjs 是同步加载模块;ESM 是异步加载模块
    • commonjs 是对值的浅拷贝;ESM 是对值的引用,而且不可修改(直接地址不可修改,类似于 const)。

2. JS模块包装格式有哪些?⭐⭐⭐

commonjs、AMD、CMD

3. require 和 import的区别?⭐⭐⭐

区别require
调用时机require 是运行时调用,所以可以放在任何地方Import 是编译时调用,所以必须放在文件的开头
使用方式require 需要使用:
module.exports = fs(导出全部)或者
exports.fs = xxx(导出部分)
import 用 :
export default(导出全部) 或者
export const xx(导出部分)
解构赋值require 是赋值的过程import 是解构的过程

【集合引用类型】

【Array】

数组的常用方法

  • every 和 some()
    every()和some()方法是数组的逻辑判定:它们对数组元素应用指定的函数进行判定,返回true或false。
    • every()方法:当且仅当针对数组中的所有元素调用判定函数都返回true,它才返回true
    • some()方法:当数组中至少有一个元素调用判定函数返回true,它就返回true;并且当且仅当数值中的所有元素调用判定函数都返回false,他才返回false
  • find() 和 findIndex()
  • fill()
  • includes()
  • flat()

数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响
flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。 如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数
如果原数组有空位,flat()方法会跳过空位。

const numbers = [1, 2, [3, 4, [5, 6]]];
// Considers default depth of 1
numbers.flat(); 
> [1, 2, 3, 4, [5, 6]]

// With depth of 2
numbers.flat(2); 
> [1, 2, 3, 4, 5, 6]

// Executes two flat operations
numbers.flat().flat(); 
> [1, 2, 3, 4, 5, 6]

// Flattens recursively until the array contains no nested arrays
numbers.flat(Infinity)
> [1, 2, 3, 4, 5, 6]

[ 延伸问题]

(1)js 遍历数组的方法⭐⭐⭐⭐⭐

reduce、map、filter、every、some、foreach.

foreach 和 map 有什么区别⭐⭐⭐⭐

  • foreach 没有返回值,一般如果用来遍历修改原数组的话可以用 foreach 方法

(2)数组可以改变原数组的方法⭐⭐⭐⭐⭐

序号方法名作用返回值备注
头部操作shift把数组的第一个元素从其中删除返回第一个元素值-
unshift向数组的开头添加一个或更多元素返回新的长度-
尾部操作push向数组的末尾添加一个或多个元素返回新的长度-
pop移除最后一个数组元素返回删除的元素会改变数组的长度
顺序sort对数组的元素进行排序-array.sort(sortfunction)在原数组上进行排序
reverse用于颠倒数组中元素的顺序-array.reverse()在原数组上进行排序
splice用于添加或删除数组中的元素返回删除元素的数组array.splice(index,howmany,item1,.....,itemX)
  • sort
    • 升序:array.sort(function(a,b){return a-b})
    • 降序:array.sort(function(a,b){return b-a})
  • array.splice(index,howmany,item1,.....,itemX)
    • index:从0开始,第一个是0位
    • howmany:删除的个数/0
    • item1,.....,itemX:插入的数据...

【Map】~对象

参考链接:

Map对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map可以接受一个数组作为参数。

  • JSON字符串要转换成Map可以先利用JSON.parse()转换成数组或者对象,然后再转换即可。

Map对象的属性和方法

Map对象的属性:

  • size:返回Map对象中所包含的键值对个数

Map对象的方法:

  • set(key, val):向Map中添加新元素

  • get(key):通过键值查找特定的数值并返回

  • has(key):判断Map对象中是否有Key所对应的值,有返回true,否则返回false

  • delete(key):通过键值从Map中移除对应的数据

  • clear():将这个Map中的所有元素删除 遍历方法:

  • keys():返回键名的遍历器

  • values():返回键值的遍历器

  • entries():返回键值对的遍历器

  • forEach():使用回调函数遍历每个成员

Map和Object的区别

  • 一个Object 的键只能是字符串或者 Symbols,但一个Map 的键可以是任意值。
  • Map中的键值是有序的(FIFO 原则: first in,first out先进先出法”),而添加到对象中的键则不是。
  • Map的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

【Set】~数组

Map对象的属性和方法

Set 对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set 本身是一个构造函数,用来生成Set 数据结构。Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构作为参数,用来初始化。

Set中的特殊值:

Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:

  • +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复
  • undefined 与 undefined 是恒等的,所以不重复
  • NaN 与 NaN 是不恒等的,但是在 Set 中认为NaN与NaN相等,所有只能存在一个,不重复。
  • {} {} 两个空对象的指针不一样,所以会重复

Set实例对象的属性:

  • size:返回Set实例的成员总数。

Set实例对象的方法:

  • add(value):添加某个值,返回 Set 结构本身(可以链式调用)。
  • delete(value):删除某个值,删除成功返回true,否则返回false。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。

遍历方法: 和map一样
(由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。)

Set 对象作用

  • 数组去重(利用扩展运算符)
  • 合并两个set对象:
  • 交集 new Set([...a].filter(x => b.has(x)))
let a = new Set([1, 2, 3])
let b = new Set([2, 3, 6])
let intersect = new Set([...a].filter(x => b.has(x)))  // {2, 3} 利用数组的filter方法。
  • 差集 new Set([...a].filter(x => !b.has(x)))
let a = new Set([1, 2, 3])
let b = new Set([4, 3, 2])
let difference = new Set([...a].filter(x => !b.has(x))) //  {1} 

Map和Set的区别

  1. Map是键值对,Set是值的集合,当然键和值可以是任何的值;
  2. Map可以通过get方法获取值,而set不能因为它只有值;
  3. 都能通过迭代器进行for…of遍历;
  4. Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储
  5. map和set都是stl中的关联容器,map以键值对的形式存储,key=value组成pair,是一组映射关系。set只有值,可以认为只有一个数据,并且set中元素不可以重复且自动排序

【对象、类与面向对象编程】

(一)对象

[ 延伸问题 ]

(1)字面量创建对象和new创建对象有什么区别?new内部都实现了什么,手写一个new⭐⭐⭐⭐⭐

new和字面量创建对象的区别:

1.字面量创建对象,不会调用Object构造函数,简洁且性能更好;
2.new Object() 方式创建对象本质上是方法调用,涉及到在proto链中遍历该方法,当找到该方法后,又会生产方法调用必须的 堆栈信息,方法调用结束后,还要释放该堆栈,性能不如字面量的方式。
new的工作原理(new在执行时会做的四件事):

  1. 创建:创建一个新的空对象let obj = {}

  2. 继承:继承了构造函数的原型obj.__proto__ = fn.prototype
    (即 让新对象的__proto__指向原函数的prototype)。

  3. 执行:执行构造函数方法let result = fn.apply(obj, args)
    把构造函数方法的属性和方法都添加到this引用的对象中,让this指向这个新的对象

  4. 返回:返回这个新对象return result instanceof Object ? result : obj (如果构造函数中没有返回新对象,那么返回this,即创建新对象;否则,返回构造函数中返回的对象。).

(2)手写new

function myNew(fn, ...args) {
      // 创建一个空对象
      let obj = {}
      // 使空对象的隐式原型指向原函数的显式原型
      obj.__proto__ = fn.prototype
      // 执行构造函数里面的代码(执行结果保存起来作为result ),
      // 给这个新对象添加属性和方法。
      let result = fn.apply(obj, args)// 让this指向这个新的对象obj。
      // 返回
      // 判断执行函数的结果是不是null或Undefined,
      // 如果是则返回之前的新对象,如果不是则返回result 
      return result instanceof Object ? result : obj
 }

【其他】

什么是防抖?什么是节流?手写一个⭐⭐⭐⭐⭐

setTimeOut第三个参数是什么?⭐⭐⭐⭐⭐

可以作为参数传给前面的函数,一般用于 for 循环赋值

什么是暂时性死区?(先使用后定义)⭐⭐⭐⭐⭐

暂时性死区是指,当进入一个作用域,我去使用一个变量名,而这个变量名已经存在了,但是是不可获取的,就会报错,造成暂时性死区问题;
比如一个作用域下面使用了 let 定义了 x,但是在定义之前就使用了 x,就会报错;暂时性死区意味着 typeof 也不是绝对安全的操作

x = '123'; // 报错
let x = 1

---------------------
typeof y; // 报错
let y = 123

?? 如何实现大文件上传?⭐⭐⭐⭐

使用 input 接受大文件,使用file.slice进行分割分块上传(制定好一个块的大小,然后进行分割),等所有块上传完毕之后,promise.all(),运行成功回调

Object.keys和Object.getOwnPropertyNames有什么区别?⭐⭐⭐

  • Object.keys只列出非原型上可枚举的key值
  • 而Object.getOwnPropertyNames列出非原型上的所有key值(Symbol除外)

??如何配置rem⭐⭐⭐

思路:给html设置一个根字体的大小html{font-size:' + rootFontSize + 'px!important}

 //rem适配
 (function () {
    const styleEle = document.createElement('style');
    const docWidth = document.documentElement.clientWidth;
    const rootFontSize = docWidth / 16;

    styleEle.innerHTML = 'html{font-size:' + rootFontSize + 'px!important}';
    document.head.appendChild(styleEle);

})()

倒计时用setimeout来实现还是setInterval⭐⭐⭐⭐

  • setTimeout
    • 因为假如用setInterval的话,该程序执行需要105ms,而设置的间隔为100ms,则还没运行完最后的那5毫秒就会运行下一次的函数

??秒传、分片传输、断点传输⭐⭐⭐⭐

  • 秒传
    文件上传前,服务器先对文件做MD5校验,如果服务器上有同样的文件,则返回一个新地址,如果不想秒传也可以,修改文件中的内容就可以了(改名字不行)
  • 分片传输
    利用Blob提供的slice方法把大文件分割为一个个小文件分别传输。全部上传完成时候由服务端进行归总整合
  • 断点传输
    在分片上传的基础上,分成一个个小文件之后,每个小文件上传完毕之后对其进行状态的存储(localStorage),如果中间发生网络断线或者刷新,下次可以接着上次的进度上传

e.target和e.currentTarget的区别⭐⭐⭐

  • e.target指向的是触发事件的元素;(点击的元素)
  • e.currentTarget指向的是添加监听事件的元素;(绑定方法的元素)
<button type="button" class="btn btn-default" attr="workstep">
    <i class="icon-test"></i>
    <p style="display: inline-block;vertical-align:top;line-height: 28px;">按钮</p>
</button>

点击p标签,target是p,currenttarget是button

jquery 如何实现链式调用⭐⭐⭐

  • 在 jQuery 中,如果对同一个对象进行多种操作,则可以使用链式调用的语法。
  • 链式调用是 jQuery 中经典语法之一,不仅节省代码量,还可以提高网站的性能。
let fun = {
    fun1: function() {
        console.log("fun1");
        return this;
    },
 
    fun2: function() {
        console.log("fun2");
        return this;
    },
 
    fun3: function() {
        console.log("fun3");
        return this;
    }
}

//链式调用
fun.fun1().fun2().fun3();

??JS性能优化的方式⭐⭐⭐⭐⭐

  • 垃圾回收
  • 闭包中的对象清除
  • 防抖节流
  • 分批加载(setInterval,加载10000个节点)
  • 事件委托
  • 少用with
  • requestAnimationFrame的使用
  • script标签中的defer和async
  • CDN

【设计模式】