js 中继承实现的几种方式
原型链继承 :
缺点:
- 引用值共享问题。
- 只能继承一个
- 新实例无法向父类构造函数传参。
构造函数继承
特点
- 只继承了父类构造函数的属性、没有继承父类原型上的属性。
- 可以继承多个构造函数属性。
- 在子实例中可以向父实例传参。
缺点:
- 只能继承父类构造函数的属性、拿不到原型上的方法。
- 无法实现构造函数的复用。(每次使用都需要重新调用)
组合继承
特点:
- 可继承父类原型上的方法、属性, 可传参可复用。
- 每个新实例引入的构造函数属性是私有的。
缺点:
- 调用了两次父类构造函数
Sub.prototype = new Super(); // 执行两次
寄生组合继承:
缺点
- 原型上的方法访问不到
this关键字 指向问题
是什么
我们都知道函数调用时内部自动生成的上下文对象。而我们都知道对象是一系列属性的集合、所以 this 为当前执行上下文对象的一个属性。(属性 = 属性名 + 属性值)
绑定规则
- 默认绑定: 一般 this 是全局的 Window。 严格模式下是 undefined。
- 隐式绑定: 函数的 this 是上下文对象。(上下文必须包含我们的函数)
- new 绑定:this 就是实例对象
- 显式绑定:
-
apply( 参数1: 上下文对象、即指向的this,参数2:为一个数组 )、 写法:test.apply({},[1,2]) -
call(参数1: 上下文对象、即指向的this,具体的每个参数1...)、 写法:test.call({},1,2) -
bind 函数中的 this 指向最终要执行的函数、并同时返回一个新的匿名函数。 而该匿名函数作用就是执行 this 指向的函数、并改变其 this 指向 写法: test.bind({})()
常见情况下的 this 指向:
- 全局上下文:在全局作用域中,
this指向全局对象。在浏览器环境中,全局对象是window。 - 函数调用上下文:当一个函数被普通调用时,
this通常指向全局对象。例如:myFunction()。 - 方法调用上下文:当一个函数作为对象的方法被调用时,
this指向调用该方法的对象。例如:obj.myMethod()。 - 构造函数上下文:当使用
new关键字调用一个构造函数时,this指向新创建的实例对象。 - 事件处理上下文:在事件处理函数中,
this通常指向触发事件的元素。 - 自定义上下文:可以使用
call、apply和bind方法改变函数的上下文,从而改变this的指向。
执行上下文
种类
-
全局上下文: 一个程序只有一个全局上下文、它会执行两件事。
- 创建全局的 window 对象。
- 设置 this 值等于这个全局对象
-
函数执行上下文:在函数每次调用的时候就会创建一个函数上下文,每当创建一个函数上下文它就会按照被创建的顺序执行。(函数上下文可以有多个)
-
Eval 函数上下文
创建上下文的阶段
创建阶段
一般会发生以下三件事
-
this 绑定
-
创建词法环境组件(内部有两个组件)
- 环境记录器: 存储变量和函数声明的实际位置
- 外部环境的引用:意味着它可以访问父级的词法环境(作用域)
-
创建变量环境组件:
- 词法环境和变量环境不同之处是
- 词法环境 是被用来存储 函数声明和变量的 (let 和 const)。
- 变量环境 只能存储 var 变量的绑定
- 词法环境和变量环境不同之处是
执行阶段
将这些变量进行分配、最后执行代码
防抖节流
防抖
是什么
- 当事件被触发时、 相应的函数不会立即触发、而是被推迟执行。
- 如果该事件在一定的时间内频繁触发、 那么相应的函数会被一直推迟执行。
- 只有该事件在一定的时间内没有被触发、 才是真正的执行这个函数
简而言之 : 防抖就是将函数的执行延迟一定的时间、如果在该时间内重新触发、该时间就会被重置; 只有真正达到延迟时间、才会执行回调函数。
我们可以把它理解为游戏里的回城。点击回城如果我们不移动英雄、那么一段时间后它就会回去、反之就会一直被刷新回城时间。
应用场景
- 输入框内频繁输入、搜索或者提交信息
- 频繁点击按钮、触发某个事件
- 监听浏览器滚动时间
- 监听用户缩放浏览器的 resize 时间
节流
是什么
- 当事件触发时、 相应的函数就会立即执行。
- 如果该事件一直被触发时、那么相应的函数会按照一定的频率进行执行
我们可以把它理解为技能cd,不管你按了多少次,必须等到cd结束后才能释放技能。也就是说在如果在cd时间段,不管你触发了几次事件,只会执行一次。只有当下一次cd转换,才会再次执行。
应用场景
- DOM 元素的拖拽功能实现
- 计算鼠标移动的距离
- 监听 scorll 事件
事件代理(事件委托)
将事件绑定到父元素上、让父元素担当事件监听的职务。它的原理是DOM元素的事件冒泡。
优点: 可以大量节省内存占用、减少事件注册、新增子元素无需再次对其绑定
事件模型
在JavaScript中,事件模型(Event Model)是一种处理用户交互和浏览器行为的机制。事件模型基于事件监听器(Event Listeners)和事件流(Event Flow)的概念。
事件监听器是用于处理特定类型事件的函数。当事件发生时,如点击、滚动、键盘按键等,事件监听器会被触发并执行相应的操作。在JavaScript中,可以使用addEventListener方法为元素添加事件监听器,使用removeEventListener方法移除事件监听器。
事件流描述了事件从发生到被处理的整个过程。主要包括三个阶段:
- 捕获阶段(Capturing Phase):事件从顶层元素(通常是
document对象或window对象)开始向下传播,直到达到目标元素。在此过程中,事件处理程序可以捕获并处理事件。 - 目标阶段(Target Phase):事件到达目标元素,触发目标元素的事件处理程序。
- 冒泡阶段(Bubbling Phase):事件从目标元素开始向上回传到顶层元素。在此过程中,事件处理程序可以继续处理事件。
事件模型允许开发者在不同阶段对事件进行处理,实现灵活的事件处理逻辑。此外,JavaScript还提供了一些全局事件处理方法,如onclick、onmouseover等,它们可以直接绑定到HTML元素上,但这种方式不如事件监听器灵活且可维护性较差。
总之,JavaScript的事件模型提供了一种处理用户交互和浏览器行为的机制,使得开发者能够根据不同的事件类型和阶段来编写相应的处理逻辑。
事件循环
事件循环是 JS 引擎中用于处理异步操作的的一种机制。它用于调度和管理 Js 代码中的事件、回调函数和任务队列以确保它们可以按照正确的顺序执行。 这种机制可以使 Javascript 具有非阻塞的特性,并能够处理大量的并发操作。
如果在 Js 中遇到异步操作时、它会被添加到任务队列中、而不会立即执行。而 JS 引擎不断的从任务队列取出任务并执行的过程叫做事件循环。
事件循环 = 任务队列 + 宏任务 + 微任务
执行过程
- 从宏任务队列中取出一个任务执行、 直到宏任务队列为空。
- 执行过程中如果遇到微任务、将添加到微任务队列中。
- 当从宏任务队列中取出的任务执行完毕后、 开始执行微任务队列中的任务、一直到微任务队列为空。
- 重复上述步骤、不断地从宏任务队列和微任务队列中取出任务执行,直到两个队列都为空。
垃圾回收机制
是什么
浏览器垃圾回收机制是指、浏览器在运行的过程中自动管理内存的一种机制。当我们使用浏览器打开网页时、会创建很多的对象和变量来存储页面的数据和功能。 但随着页面的加载、交互和关闭等一系列操作、这些对象和变量可能不再使用了。 为了避免内存泄露和性能优化、 浏览器会定期进行进行垃圾回收。
具体的来说它会标记哪些任在使用的对象并将其保留下来。而那些没有被引用的对象会当作垃圾并清除掉。
常见的垃圾回收算法
标记清除
通过从根节点开始遍历所有可达对象、 并对每个可达对象进行标记。 之后在第二次遍历的时候、未被标记的对象会被当作垃圾
引用计数
内部通过维护计数器来记录每个对象在其他地方被引用的次数。若一个对象没有任何引用时、就会把这个对象当作垃圾
在实际应用中、现代浏览器通常采用的更为复杂和高效的垃圾回收算法。如分代回收和增量式回收。这些算法可以根据对象的生命周期和内存分配等因素进行优化、 以提升浏览器性能和用户体验。
Null 和 undefined 的区别
null和undefined 都是属于基本数据类型(都保存在栈中)。ECMAScript认为undefined是null派生出来的。
区别
undefined代表的含义是未定义,null代表的含义是空对象。undefined不是一个关键字(也就是它可以当作一个变量名),null是一个关键字(表示一个空值)。Number(undefined) === NaN,Number(null) === 0。- 当使用双等号对两种类型的值进行比较时会返回
true,使用三个等号时会返回false。
产生null的原因
- 访问不存在的
DOM节点时。 - 原型链的终点。
产生undefined的原因
- 声明变量,但未赋值
- 函数无返回值,执行后返回undefined
- 函数中可选参数,没有传参时返回undefined
- 对象中不存在或未赋值的属性
堆和栈的区别
堆:
- 内存的分配是程序员手动控制的
- 内存管理依赖于垃圾回收机制、当一个对象不再被引用时它会自动回收
- 内存空间相对较大
- 主要用于存储复杂的数据结构
栈:
- 内存的分配和释放由编译器或解释器自动处理的、每当一个函数执行完成之后、与之相关的栈内存就会被自动释放
- 内存空间相对较小
- 一般存储基本数据类型以及函数调用的上下文信息
深拷贝和浅拷贝
浅拷贝只复制属性指向某个对象的指针,而不复制对象本身
- 直接手动复制对象
- 使用扩展运算符
- 使用Concat() 方法合并多个数组,返回一个新的数组。原数组不变
- 使用Object.assign()
深拷贝会创建新的内存地址。可以在不改变原数组的情况下、修改元素
- 使用 JSON.stringify转为字符串再 JSON.parse
- 深度递归遍历
- 使用 Object.create()
- jQuery中的 $.extend()
settimeout setinterval 区别
setinterval
概念: 每隔一段时间就执行一次。重复执行 函数整体执行时间: 设置的固定时间,若异步函数执行时间大于设置的时间,则函数整体时间为异步函数执行时间
settimeout
概念: 一段时间后就执行一次。不重复执行 函数整体执行时间: 设置的固定时间 + 异步函数执行时间