JS面试真题 part3

JS面试真题 part3

  • 11、bind、call、apply区别?如何实现一个bind
  • 12、JavaScript中执行上下文和执行栈是什么
  • 13、说说JavaScript中的事件模型
  • 14、解释下什么是事件代理?应用场景?
  • 15、说说你对闭包的理解?闭包使用场景

11、bind、call、apply区别?如何实现一个bind

自己回答:
相同点:都是改变函数的this指向,函数接收的第一个参数都是要指向的对象。
不同点:函数有没有执行以及参数传递的方式不同
bind函数并没有执行。
call和apply函数执行了。
call的参数第一个是要指向的对象,后面是参数列表
apply的参数第一个是要指向的对象,第二个是数组,数组里是参数列表
bind的参数第一个是要指向的对象,后面是参数列表,可以分次传。

bind实现:

 Function.prototype.bindFunc=function(obj,...agament){
			let that=this
			return function(...agament2){
				let agamentAll=[...agament,...agament2]
				let func=that.apply(obj,agamentAll)
			   return func
		}
}

验证:

function getName(age,sex){
		   console.log(this.name,age,sex)
		   return {
			   name:this.name,
			   age:age,
			   sex:sex
		   }
		}
		let obj={
		   name:'张三'
		}
		let newFunc=getName.bindFunc(obj,'18')
		let aa =newFunc('女')
		console.log('aa',aa)

在这里插入图片描述
标准回答:
callapplybind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向

  • 三者都可以改变函数的this对象指向
  • 三者第一个参数都是this要指向的对象,如果没有这个参数或参数为undefinednull,则默认指向全局window
  • 三者都可以传参,但是apply是数组,而call是参数列表,且applycall是一次性传入参数,而bind可以分多次传入
  • bind是返回绑定this之后的函数,applycall则是立即执行

实现:
分为三步

  • 修改this指向
  • 动态传递参数
  • 兼容new关键字
    自己回答实现里的少了兼容new关键字,如果我们对bind返回的函数使用new会发生什么呢
 function Person(name, age) {
 this.name = name;
 this.age = age;
}

const BoundPerson = Person.bind(null, '前端西瓜哥');
const boundPerson = new BoundPerson(100);
// Person {name: '前端西瓜哥', age: 100}

boundPerson.__proto__ === Person.prototype
// true

等价于直接new原来的Person函数,依然可以进行参数分次传递
修改后

 Function.prototype.bindFunc=function(obj,...agament){
 			let that=this	
 			return function Fn(...agament2){
 				let agamentAll=[...agament,...agament2]
				let func=this instanceof Fn?new that(...agamentAll):that.apply(obj,agamentAll);
 			   return func
 		  }
     }

12、JavaScript中执行上下文和执行栈是什么

自己回答:
JavaScript中执行上下文:当前变量或函数的生效范围和集合
执行栈:全局执行上下文和函数执行上下文的执行顺序

标准回答:
定义:执行上下文是一种对js代码执行环境的抽象概念,只要有JavaScript代码运行,那么它就一定是运行在执行上下文中。

执行上下文包含了三个重要的组成部分:变量对象作用域链this值

执行上下文的类型分为三种:

  • 全局执行上下文:只有一个,浏览器的全局对象就是window对象,this指向这个全局对象
  • 函数执行上下文:存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文
  • Eval函数执行上下文:指的是运行在eval函数中的代码,很少用而且不建议使用

执行上下文的生命周期包括三个阶段:创建阶段->执行阶段->回收阶段
在这里插入图片描述

创建阶段

  • 生成变量对象
    • 创建arguments:如果是函数上下文,首先会创建 arguments 对象,给变量对象添加形参名称和值。
    • 扫描函数声明:对于找到的函数声明,将函数名和函数引用(指针)存入 VO 中,如果 VO 中已经有同名函数,那么就进行覆盖(重写引用指针)。
    • 扫描变量声明:对于找到的每个变量声明,将变量名存入 VO 中,并且将变量的值初始化为undefined 。如果变量的名字已经在变量对象里存在,不会进行任何操作并继续扫描。
  • 建立作用域链:在执行期上下文的创建阶段,作用域链是在变量对象之后创建的。作用域链本身包含变量对象。
  • 确定this的指向:如果当前函数被作为对象方法调用使用 bind、call、apply 等 API 进行委托调用,则将当前代码块的调用者信息(this value)存入当前执行上下文,否则默认为全局对象调用

let和const定义的变量在创建阶段没有被赋值,var声明的变量在创建阶段被赋值为undefined。创建阶段,扫描函数声明和变量,然后将函数声明存储在环境中,变量初始化为undefined(var声明时)

函数提升优先级高于变量提升,且不会被同名变量声明覆盖,但是会被变量赋值后覆盖。而且存在同名函数与同名变量时,优先执行函数。

执行阶段

执行阶段 中,执行流进入函数并且在上下文中运行/解释代码,JS引擎开始对定义的变量赋值、开始顺着作用域链访问变量、如果内部有函数调用就创建一个新的执行上下文压入执行栈并把控制权交出

  • 变量赋值:如果找不到变量的值,将会为其分配undefined
  • 函数的引用
  • 执行其他代码

回收阶段

  • 执行上下文等待虚拟机回收执行上下文

执行上下文栈:

执行栈,也叫调用栈,具有LIFO(后进先出)结构,用于存储在代码执行期间创建的所有上下文。

顺序如下:

  1. 在全局代码执行前, JS引擎就会创建一个来存储管理所有的执行上下文对象
  2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
  3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
  4. 在当前函数执行完后,将栈顶的对象移除(出栈)
  5. 当所有的代码执行完后, 栈中只剩下window

举例:在这里插入图片描述
在这里插入图片描述

13、说说JavaScript中的事件模型

自己回答:
事件模型?
忘了概念,复习浏览器事件详解
标准回答:
事件模型之前先阐述一下,事件与事件流
1、事件与事件流
事件:在html文档或者浏览器中发生的一种交互操作,使得网页具有互动性,常见的有加载事件、鼠标事件、自定义事件等。
由于dom是数结构,父子节点都绑定事件,存在一个顺序问题,这就涉及到了事件流
事件流都会经历三个阶段:
-事件捕获阶段:从上往下传递

  • 处于目标阶段
  • 事件冒泡阶段:从下往上传播
    在这里插入图片描述
    事件模型包括:
  • 原始事件模型(DOM0级)
  • 标准事件模型(DOM2级)
  • IE事件模型(基本不用)

原始事件模型:
DOM0级事件具有很有的跨游览器优势,会以最快的速度绑定,但是由于绑定速度太快,可能页面还没加载完全

  • 只支持冒泡,不支持捕获
  • 同一种类型的事件只能绑定一次
    在这里插入图片描述
    标准事件模型:
    一共三个阶段:事件捕获、事件处理、事件冒泡

事件绑定监听函数如下:

addEventListener(eventTpye,hander,useCapture)

事件移除监听函数如下:

removeEventListener(eventTpye,hander,useCapture)
  • eventTpye指定事件类型(不要加on)
  • hander是事件处理函数
  • useCapture是一个boolean用于指定是否在捕获阶段进行处理,一般设置为false与ie浏览器保持一致

特性:

  • 可以在一个DOM元素上绑定多个事件处理器,各自并不会冲突
  • 执行时机,与useCapture设置有关

IE事件模型:
一共俩个阶段:事件处理、事件冒泡(没有事件捕获)

事件绑定监听函数如下:

attachEvent(eventTpye,hander)

事件移除监听函数如下:

attachEvent(eventTpye,hander)

14、解释下什么是事件代理?应用场景?

自己回答:
事件代理,由父级绑定事件,通过子节点id触发对应子节点的事件,避免多次绑定事件,也避免新增的子节点漏掉绑定事件
应用场景:li节点事件监听,在父级ul或div上绑定监听事件

标准回答:
事件代理,就是把一个元素响应事件(click、keydown…)的函数委托到另一个元素
事件流会经过三个阶段:捕获->目标->冒泡阶段,事件委托就是在冒泡阶段完成
事件委托,会把一个或者一组元素的事件委托到它的父层或者更外的元素上,真正绑定事件的是外层元素,而不是目标元素
当事件响应到目标元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。
应用场景:
如果有个列表,列表中有大量的列表项,点击列表项响应一个事件。如果给每个列表项都绑定一个函数的话,那么对内存的消耗是非常大的,如果用户还能动态增加或删除列表元素,那每次改变的时候都需要给增加的元素绑定事件,给删除的元素解除事件。这时候就可以事件委托,把事件绑定在父级元素ul上面,然后执行事件时候再匹配目标元素,和目标元素的增减也没有关系。
如:

<input type="button" name="" id="btn" value=" " />
<ul id="ul1">
 <li>item 1</li>
 <li>item 2</li>
 <li>item 3</li>
 <li>item 4</li>
</ul>

使用事件委托

const oBtn = document.getElementById("btn");
const oUl = document.getElementById("ul1");
const num = 4;
//
oUl.onclick = function (ev) {
 ev = ev || window.event;
 const target = ev.target || ev.srcElement;
 if (target.nodeName.toLowerCase() == 'li') {
 console.log('the content is: ', target.innerHTML);
 }
};
//
oBtn.onclick = function () {
 num++;
 const oLi = document.createElement('li');
 oLi.innerHTML = `item ${num}`;
 oUl.appendChild(oLi);
};

总结:
适合事件委托的事件有:click、mousedown、mouseup、keydown、keyup、keypress

15、说说你对闭包的理解?闭包使用场景

自己回答:
函数内部访问外部的变量,形成闭包
使用场景:var定义变量的for循环,内部使用异步,如定时器内访问变量,用闭包进行传递,可以 让for循环的变量传参正确

标准回答:
一个函数和对其周围状态的引用绑定在一起,这样的组合就是闭包
闭包让你可以在一个内层函数中访问到其外层函数的作用域
使用场景:
任何闭包的使用场景都离不开这两点:

  • 创建私有变量
  • 延长变量的生命周期

一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到延长变量的生命周期的目的

1、在页面上添加一些可以调整字号的按钮

 function makeSizer(size) {
 return function() {
 document.body.style.fontSize = size + 'px';
 };
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

2、柯里化函数
柯里化的目的在于避免频繁调用具有相同参数函数的同时,又能轻松的重用

// 
function getArea(width, height) {
 return width * height
}
// 10
const area1 = getArea(10, 20)
const area2 = getArea(10, 30)
const area3 = getArea(10, 40)
// 
function getArea(width) {
 return height => {
 return width * height
 }
}
const getTenWidthArea = getArea(10)
// 10
const area1 = getTenWidthArea(20)
// 
const getTwentyWidthArea = getArea(20)

3、用闭包模拟私有方法
javaScript中,没有支持声明私有变量,但我们可以使用闭包来模拟私有方法

var Counter = (function() {
 var privateCounter = 0;
 function changeBy(val) {
 privateCounter += val;
 }
 return {
 increment: function() {
 changeBy(1);
 },
 decrement: function() {
 changeBy(-1);
 },
 value: function() {
 return privateCounter;
 }
 }
})();
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

上述通过闭包来定义公共函数,并令其可以访问私有函数和变量,这种方式也叫模块方式
两个计数器 Counter1Counter2是维护它们各自的独立性的,每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境,不会影响另一个闭包的变量
4、例如计数器、延迟调用、回调等闭包的应用,核心思想是创建私有变量和延长变量的生命周期

注意事项
如果不是某些特定任务需要使用闭包,在其他函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响
例如,在创建新的对象或者类时,方法通常应该关联于对象的原型,而不是定义到对象的构造器中。
原因在于每个对象的创建。方法都会被重新赋值

function MyObject(name, message) {
	 this.name = name.toString();
	 this.message = message.toString();
	 this.getName = function() {
	  return this.name;
	 };
	 this.getMessage = function() {
	   return this.message;
    };
}

上面的代码中,我们并没有利用到闭包的好处,因此可以避免使用闭包,修改如下:

function MyObject(name, message) {
	 this.name = name.toString();
	 this.message = message.toString();
}
MyObject.prototype.getName = function() {
	 return this.name;
};
MyObject.prototype.getMessage = function() {
	 return this.message;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/874338.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分布式技术概览

文章目录 分布式技术1. 分布式数据库&#xff08;Distributed Databases&#xff09;2. 分布式文件系统&#xff08;Distributed File Systems&#xff09;3. 分布式哈希表&#xff08;Distributed Hash Tables, DHTs&#xff09;4. 分布式缓存&#xff08;Distributed Caching…

面向对象需求分析

1. 面向对象分析概述 1.1 面向对象基本概念 以对象为中心&#xff0c;以类为构造机制&#xff0c;来认识、理解、刻画客观世界和设计、构建相应的软件系统。 1.2 UML统一建模语言 为什么要使用UML UML基本概念 统一建模语言&#xff08;UML&#xff09;是一个支持模型化和软…

【电子通识】半导体工艺——刻蚀工艺

在文章【电子通识】半导体工艺——光刻工艺中我们讲到人们经常将 Photo Lithography&#xff08;光刻&#xff09;缩写成 Photo。光刻工艺是在晶圆上利用光线来照射带有电路图形的光罩&#xff0c;从而绘制电路。光刻工艺类似于洗印黑白照片&#xff0c;将在胶片上形成的图像印…

opencv之图像梯度

图像梯度 图像梯度计算的是图像变化的速度。对于图像的边缘部分&#xff0c;其灰度值变化较大&#xff0c;梯度值也较大&#xff1b;相反&#xff0c;对于图像中比较平滑的部分&#xff0c;其灰度值变化较小&#xff0c;相应的梯度值也较小。一般情况下&#xff0c;图像梯度计…

首批通过!华为云CodeArts Snap智能开发助手通过可信AI智能编码工具评估,获当前最高等级

近日&#xff0c;华为云CodeArts Snap智能开发助手在中国信通院组织的智能编码工具首轮评估中&#xff0c;最终获得4级评级, 成为国内首批通过该项评估并获得当前最高评级的企业之一。 此次评估以《智能化软件工程技术和应用要求 第2部分&#xff1a;智能开发能力》为依据&…

ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录(失败记录)

ubuntu 20.04 一直卡在登录界面&#xff0c;即使密码正确也无法登录 这次是装实体机&#xff0c;一次失败的尝试。。。 名称型号CPUIntel Xeon E5-2673 V3GPURTX 3060 mobile 安装的时候不要选install third-party software for graphics and Wi-fi hardware and additional …

Leetcode面试经典150题-55.跳跃游戏

解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public boolean canJump(int[] nums) {/**如果就一个位置&#xff0c;你本来就在这&#xff0c;肯定可以跳到*/if(nums.length 1) {return true;}/**这个题的解题思路是遍历数组&#xff0c;如果当前位置不在之…

每日OJ_牛客_数组中出现次数超过一半的数字

目录 牛客_数组中出现次数超过一半的数字 解析代码1 解析代码2 牛客_数组中出现次数超过一半的数字 数组中出现次数超过一半的数字__牛客网 给一个长度为 n 的数组&#xff0c;数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。例如输入一个长度为…

RP2040 C SDK clocks时钟源配置使用

RP2040 C SDK clocks时钟源配置使用 &#x1f33f;RP2040时钟源API函数文档&#xff1a;https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_clocks &#x1f341;RP2040时钟树&#xff1a; 系统时钟源可以来自外部时钟输入&#xff08;exte…

程序员如何写笔记并整理资料?

整理笔记 word。没错&#xff0c;我也看了网上一大堆软件&#xff0c;还有git管理等等。个人认为如果笔记只是记录个人的经验积累&#xff0c;一个word就够了&#xff0c;那些notepad&#xff0c;laTex个人觉得不够简练。word。 1.word可以插入任何文件附件(目前最大的word 20…

9.9(QT Day 2)

将day1做的登录界面升级优化【资源文件的添加】 在登录界面的登录取消按钮进行以下设置&#xff1a; 使用手动连接&#xff0c;将登录框中的取消按钮使用第2种方式的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt4版本的连接到自定义…

面试题复习(0902-0909)

1. 完全背包问题 和01背包唯一的区别是&#xff0c;每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09; 代码和01唯一的区别在于j的循环是从小到大&#xff0c;不是从大到小。ij谁在外谁在内层区别不大。 #include <bits/stdc.h> using namespace std…

国产化数据库挑战及发展趋势

非国产数据库如Oracle、MySQL和MSSQL等在某些领域占据重要地位&#xff0c;但国产数据库的市场份额正在逐步提升&#xff0c;特别是在政策支持和市场需求的双重推动下&#xff0c;国产数据库的替代进程正在加速。 一、国产数据库市场规模 2024年中国数据库市场规模预计为543.1亿…

【树和二叉树的相关定义】概念

1.回顾与概览 2.什么是树型结构 3.树的&#xff08;递归&#xff09;定义与基本术语 3.1树的定义 注意&#xff1a;除了根结点以外&#xff0c;任何一个结点都有且仅有一个前驱 3.2树的其他表示方式 3.3树的基本术语 结点&#xff1a;数据元素以及指向子树的分支根结点:非空…

AI基础 L16 Logic Agents I

What is an Agent? • The main point about agents is they are autonomous: capable of acting independently, exhibiting control over their internal state • Thus: an agent is a computer system capable of autonomous action in some environment in order to mee…

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端&#xff0c;一般来说需要版本检测和自动更新功能&#xff0c;这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤&#xff1a; 读取本地应用版本拉取远程版本并比较两个版本如果需要升级&…

手机TF卡格式化后数据恢复:方法、挑战与预防措施

在现代生活中&#xff0c;‌手机已经成为我们不可或缺的一部分&#xff0c;‌而TF卡&#xff08;‌即MicroSD卡&#xff09;‌作为手机存储的扩展&#xff0c;‌更是承载了我们大量的重要数据。‌然而&#xff0c;‌不慎的格式化操作往往导致数据丢失&#xff0c;‌给用户带来不…

【重学 MySQL】五、MySQL 的卸载

【重学 MySQL】五、MySQL 的卸载 停止MySQL服务卸载MySQL程序删除残余文件清理注册表删除环境变量配置重启电脑 MySQL的卸载过程需要仔细操作&#xff0c;以确保彻底卸载并清理所有相关文件和配置。 停止MySQL服务 打开任务管理器&#xff1a;右键点击任务栏空白处&#xff0…

C++笔记---list

1. list的介绍 list其实就是就是我们所熟知的链表&#xff08;双向循环带头结点&#xff09;&#xff0c;但其是作为STL中的一个类模板而存在。 也就是说&#xff0c;list是可以用来存储任意类型数据的顺序表&#xff0c;既可以是内置类型&#xff0c;也可以是自定义类型&…

单词排序C++实现

代码如下&#xff1a; #include<iostream> #include<string> #include<fstream> #include<map> #include<iomanip> #include<algorithm> #include<vector>int read_file(std::map<std::string,int> &map_words) {std::st…