前端面试题js总结
2023-12-13 21:47:49
实现JavaScript继承
原型链继承: 这招有点粗糙,适合对象简单的情况。就像大家共用一把刷子一样,得注意别乱动,不然会影响其他人。
构造继承: 挺直截了当的,通过在子类构造函数中调用父类构造函数。不过有点不足,父类原型上的方法没法继承。
实例继承: 就是把父类实例当成子类的“学习资料”。方便点,可以调用父类方法,但也只能是“实例方法”。
拷贝继承: 这就有点暴力,直接把父类的东西都拷贝给子类。适合小规模的数据,不过得当心点,别搞得太乱。
组合继承: 感觉这一招比较全面,结合了构造继承和原型链继承的优点。避免了一些坑,有点像两全其美的感觉。
寄生组合继承: 这是优化过的组合继承,避免了一些不必要的麻烦。有点像是组合继承的进化版。
instanceof的原理是什么
有个对象,你想知道它是不是某个类型的。那就得查查它的“家谱”,也就是原型链。你瞅瞅这个对象的祖宗是谁,往上追溯到底能不能找到那个类型的“族谱”(也就是prototype)。
如果你追溯到头都找不到,那这个对象就不是那个类型的实例。反之,如果找到了,那就说明这个对象是那个类型的后代。就这么简单,就像查家谱一样,得一级一级往上翻,看看是不是同一家的。
请描述一下 cookies,sessionStorage 和 localStorage 的区别?
1.Cookies(饼干): 想象一下你在网上逛街,看中了一些商品,你把它们放进购物车,这个购物车就像是一盒饼干,可以一会儿关了网页再打开,饼干还在那里。Cookies就是这样,它可以存储小段的信息,而且在不同的页面和不同的浏览器之间都能共享。不过,饼干有个问题,就是每次发请求的时候,饼干都会被带上,有点像是带了个标签,有时候可能会有点“沉”。
2.sessionStorage(会话存储): 想象一下你在玩一个网页游戏,游戏刷新了一下,但是你的分数没了。这就是sessionStorage,它是短期记忆,只在当前会话(也就是你打开的这个页面)有效。刷新页面的话,它就忘了,就像你玩游戏忘掉上一关的得分了一样。
3.localStorage(本地存储): 现在,想象一下你写了一个日记,关了网页再打开,你的日记还在。这就是localStorage,它是长期记忆,数据可以一直保存在你的电脑上,就像是一个小保险柜。不过要小心,因为这个保险柜是不分会话的,删了浏览器的历史数据,它也可能跟着没了。
所以,cookies是个饼干,一直带着;sessionStorage像是短期记忆,只记得当前事情;localStorage是长期记忆,像是小保险柜,数据一直在那儿。希望这样子理解更清楚!存储大小
a 标签中,如何禁用 href 跳转页面或定位链接?
onclick="return false;"
onclick="ds(event)"
<a href="#" onclick="return false;">return false;</a>
<a href="http://www.baidu.com" onclick="ds(event)">baidu</a>
<script>
function ds(e){
e.preventDefault();
}
</script>
为什么 await 在 forEach 中不生效?如何解决?
在 forEach 里面使用 await 的时候,它好像没啥用,因为 forEach 不太懂得等待。
看,你有一堆任务要处理,就像一群小伙伴排队等你点名一样。但 forEach 就是个有点急性子的队伍,你让它们等等,它偏不肯。所以,你在 forEach 里用了 await,可这一伙小伙伴全都跑到前面去了,根本没等你点名完。
解决方案呢,就是换个听话的队伍,比如说 for...of 或者 map。这些队伍听你的,遇到 await 就老老实实等着。就好像你点名,他们一个个站好,等你慢慢来,不插队。所以,别用 forEach,选个听话的队伍,你的 await 就能生效了!
是否所有函数都有 prototype 一说?
箭头函数没有
使用Function.prototype.bind创建的函数对象没有
简述原生 js 发 ajax 的步骤
当你想要用原生的 JavaScript 来发送 AJAX 请求的时候,其实就像你给朋友发一条短信一样。下面是一种口语化的描述:
首先,你要拿出你的手机,也就是创建一个 XMLHttpRequest(XHR)对象,就像打开短信APP一样。然后,你得设置这条短信的内容和目的地。在XHR对象上设置一些东西,比如URL(目的地)和请求方法(GET、POST之类的)就像写收件人和编辑短信内容。
接下来,你得告诉你的朋友,“嘿,我准备好了,你可以发送了!”也就是用XHR对象的send()方法发送这条短信。等着,等着,有时候可能要等一段时间才能收到回复。
最后,当你收到朋友的回复,也就是服务器返回的数据,你要做些什么就看你的需求了。可能是显示在页面上,或者做些其他处理。就像你收到朋友的回复,你可以看看内容,然后决定怎么回应。
这样描述的话,就是用手机发短信,等回复,然后看看回复的内容,做相应的处理。希望这样描述能让你更容易理解用原生 JavaScript 发送 AJAX 请求的过程!
ts 和 js 的区别
嘿,JS和TS其实就像是两个兄弟,有点像巧克力冰淇淋和草莓冰淇淋的区别。让我给你聊聊:
首先,JS(JavaScript)就像是那个传统的巧克力冰淇淋,一开始大家都很喜欢,但有时候你吃多了可能会觉得有点太甜,有点不够健康。
然后,TS(TypeScript)就是像草莓冰淇淋一样的升级版。TS不仅有JS所有的甜蜜,还多了点果味。具体来说,TS是在JS的基础上加了点类型的概念,就像是在草莓冰淇淋里多了些草莓块。
这个类型的概念就是让你在写代码的时候更有条理,不容易搞混。有时候,你写了一堆代码,等运行的时候才发现有个地方写错了,就像是吃了块不好吃的冰淇淋,有点失望。TS帮你避免了这种情况,提前告诉你哪里有问题。
总的来说,JS是传统的、甜蜜的冰淇淋,而TS是升级版,加了点类型的草莓味。两者都有各自的优点,就看你喜欢哪种口味啦!
你了解原型链吗 你能说说 prototype 与 proto 的区别吗
当谈到原型链,就像是玩传话游戏一样。
首先,prototype就像是一个祖传的本子,里面有你家族的秘密配方。这个东西是函数特有的,就像是你家的传家宝,用来创建新对象时可以继承这些“秘密”。
然后,proto就像是一个小纸条,上面写着“谁是你的爸爸”。这个东西是每个对象特有的,告诉你对象的父亲是谁。也就是说,它指向了创建这个对象的函数的prototype。
所以说,prototype是函数的东西,是用来创建新对象的特殊秘籍;而proto是对象的东西,告诉你它的“家世”。这俩东西都在帮助建立那个有趣的东西叫做原型链,就像是家谱一样,把一代又一代的对象联系在一起。
你知道什么是原型吗?我们为什么要用原型呢?或者说原型为我们提供了什么?
嘿,当谈到原型的时候,就像在说我们的编程世界里的“祖传秘籍”。
原型就好比是一个蓝图,告诉了你的对象应该是什么样子,有什么特殊技能。它就像是一张指南,告诉你的编程小伙伴们:“嘿,我们这个家族有个传统,都要有这个样子,有这些本领。”
为啥我们要用原型呢?想象一下,你有一堆相似的东西要做,比如一群小怪兽要创建,它们都有一些共同的特征和技能。你不想一个个重头创建,那太累了。原型就是告诉你,创建一个新的小怪兽,不用从头开始,直接按照这个蓝图来,省事又方便。
原型给我们提供了一种轻松创建和共享代码的方式,就像是告诉你如何制作一道传家菜谱一样。这样,我们就能更高效地写代码,而且新创建的对象还能和原型分享一些功能,就好比新一代也能继承家族传统技能。所以,原型不仅省事,还能让我们的代码更有组织、更易维护。
简述 src 和 href 的区别?
嘿,src和href就像是两个不同的引路标。
首先,src(source)是用来告诉浏览器:“嘿,这里有个东西得加载进来!”比如说,图片、音频或者脚本文件,都是用src来指定的。就像给一个地方打个标记,告诉你要从这儿拿点东西过来。
然后,href(hypertext reference)是给超链接用的,告诉浏览器:“嘿,这是个链接,点击它会带你去另一个地方!”比如说,你点一下页面上的链接,它就会带你到另一个页面或者资源。就像给你指一条路,告诉你怎么到达下一个地方。
总的来说,src是用来加载东西,比如图片、脚本之类的;而href是用来告诉你怎么去别的地方,就像是页面上的链接。两者都是帮助我们在网页世界里找到我们想要的东西或者去往想去的地方的引路标!
js中的常用事件绑定方法
嗨!JS里常用的事件绑定方法就像是给网页添加各种酷炫功能的神奇方式。
首先,我们有addEventListener。这个家伙就像是给你的元素贴上“耳朵”,它能听到各种事件,比如点击、鼠标移动、键盘敲击等等。你可以告诉它:“嘿,如果有人点了这个按钮,就跟我说一声!”然后你就能在按钮被点击时执行一些操作。
其次,还有onclick这样的老方法。这个家伙就像是直接把事件黏在元素上,比如说你可以在一个按钮上写上onclick="doSomething()",意思是点击这个按钮的时候就执行doSomething()函数。
还有其他类似的方法,比如onmouseover(鼠标移过去时触发)或者onkeyup(按键被按下并松开时触发)等等,它们就像是元素自带的“魔法指令”,告诉你在什么情况下触发某个操作。
总的来说,这些事件绑定方法就是让你的网页元素具备了与用户交互的能力,让用户点击、输入或者移动鼠标的时候,网页可以做出相应的反应,变得更有趣、更有动态性!
插入几万个 dom ,如何实现页面不卡顿?
通过分批次异步插入大量数据到页面中。它设定了总数据量和每次插入的数据条数,然后利用 `requestAnimationFrame` 实现异步循环插入,避免一次性插入大量数据造成页面性能问题。每次循环内部创建文档片段,并将一定数量的 `li` 元素批量添加到文档片段中,最终通过 `appendChild` 将文档片段添加到页面。
为什么操作 dom 慢?
嘿,DOM操作有时候就像是在找东西,但是这个找东西有时候比较慢,就好像你找钥匙找了半天还是找不到一样。
首先,DOM是个大家族,你要是去找个特定的元素,有时候得从家里的每个角落一个个摸索。就好比你得找到那个藏在柜子深处的袜子,一样费时间。
其次,每次你动一下DOM,浏览器都要重新计算一下页面上的布局和样式。就像你挪动房间里的一张桌子,整个房间的布局都得重新安排一下,这样就会耗费不少时间。
还有就是频繁的DOM操作,就像是你要是一下子在房间里翻个底朝天,浏览器可能会有点手忙脚乱。频繁操作DOM就相当于不停地给浏览器添乱,它得一直忙碌着应付你的“折腾”。
所以,DOM操作有时候慢,主要是因为它是个大家族,找东西有点费劲;每次操作都得重新计算布局和样式,有点费时;而且频繁操作就像是给浏览器添堵,可能会让它有点不知所措。所以,最好在可能的情况下,尽量减少DOM操作,让浏览器轻松点。
并行和并发的区别是什么?
嘿,并行和并发有点像是在谈论人们同时干活的不同方式。
首先,想象你有两个任务要处理。并发就像是你一个人要同时处理这两件事情,你可能会一会儿做这个,然后转头又处理那个,来回切换,看起来就像是同时在做。
然后,来说说并行。这就像是你有两个好朋友,可以一起帮你完成这两个任务。每个朋友负责一件事,然后他们一起动手,同时做这两个任务,速度更快。
简单说,并发是在一段时间内处理多件事情,有点像来回切换;而并行是真正同时处理多件事情,有点像团队合作一起干活。就好比你一个人能同时吃饭、听音乐,这是并发;但如果你叫上朋友,大家一起做不同的事情,这就是并行了。
介绍一下 ES6 中 Set, Map的区别?
当然可以!ES6中的Set和Map就像是你的工具箱里的两把不同的工具,各有各的特色。
首先,说说Set。Set就像是一个超大的去重工具,你往里塞东西,它自动帮你去掉重复的。就好比你有一袋糖果,你往里放了两颗一样的,Set就会帮你只留下一颗,不重不多。
然后,Map就是一个记事本。你可以把键和值放进去,就像是你有一本地址簿,键是人名,值是他们的地址。这样你想查找某个人的地址的时候,就特别迅速,不用翻整本。
总结一下,Set就是去重大师,让你集合里的东西井然有序;而Map就是你的私人记事本,让你以键值对的方式轻松查找信息。两者都是ES6给我们提供的很实用的工具,让编程变得更加方便。当然可以!ES6中的Set和Map就像是你的工具箱里的两把不同的工具,各有各的特色。
首先,说说Set。Set就像是一个超大的去重工具,你往里塞东西,它自动帮你去掉重复的。就好比你有一袋糖果,你往里放了两颗一样的,Set就会帮你只留下一颗,不重不多。
然后,Map就是一个记事本。你可以把键和值放进去,就像是你有一本地址簿,键是人名,值是他们的地址。这样你想查找某个人的地址的时候,就特别迅速,不用翻整本。
总结一下,Set就是去重大师,让你集合里的东西井然有序;而Map就是你的私人记事本,让你以键值对的方式轻松查找信息。两者都是ES6给我们提供的很实用的工具,让编程变得更加方便。
promise 常见方法和 all 和 race的应用场景
`Promise.race()` 允许多个 Promise 同时执行,当有一个 Promise 完成或拒绝时,整个 `race` 就会结束。它的场景在于设置超时或并发执行多个异步任务,以最快的结果为准。
`Promise.all()` 则是等待所有的 Promise 完成,只有当所有的 Promise 都成功完成时才会返回一个成功结果,否则返回一个失败结果。常用于并行请求多个数据,等待它们都完成后再进行下一步操作。
常见内存泄漏
内存泄漏常见于:
1. 静态集合类和持有长生命周期对象的引用,如HashMap等。长生命周期对象持有短生命周期对象的引用,即使短生命周期对象不再使用,也因为引用关系无法释放,导致内存泄漏。
2. 连接未关闭,比如数据库、网络、IO连接等。如果连接未显式关闭,会导致对象无法被垃圾回收,产生内存泄漏。
3. 变量作用域不合理,变量定义的作用范围大于使用范围,且未及时设置为null,可能引起内存泄漏。
4. 内部类持有外部类对象的引用,导致外部类对象无法被垃圾回收。
5. 修改哈希值,在存储HashSet集合后修改对象的哈希值,导致无法在集合中找到对象。
6. 缓存泄漏,将对象引用放入缓存中并遗忘,解决方式可以使用WeakHashMap代表缓存。
7. 监听器和回调,注册回调但未显式取消,导致回调积聚。使用弱引用的方式保存回调可以解决此问题。
数组去重的方法
方法一:双层循环,外层循环元素,内层循环时比较值如果有相同的值则跳过,不相同则push进数组
方法二:利用splice直接在原数组进行操作,双层循环,外层循环元素,内层循环时比较值
,值相同时,则删去这个值
注意点:删除元素之后,需要将数组的长度也减1.
方法三:利用对象的属性不能相同的特点进行去重
方法四:利用indexOf以及forEach
方法五:利用ES6的set
异步的解决方案有哪些?
1.回调函数callback
2.事件发布订阅
3.Promise
4.Generator
5.async/await
介绍下事件代理,主要解决什么问题
1. 大量事件绑定增加浏览器内存占用,严重影响性能。
2. Ajax引入导致局部刷新,需要重新绑定事件。
3. 部分浏览器在移除元素时,未及时移除绑定的事件,可能导致内存泄漏。
4. Ajax中重复绑定事件造成代码过于耦合,影响后期维护。
JS中的常见设计模式以及应用场景?
1、单例模式
单例模式就是一个实例在整个网页的生命周期里只创建一次,后续再调用实例创建函数的时候,返回的仍是之前创建的实例。在实际开发中应用十分广泛,例如页面中的登录框,显示消息的提示窗
2、策略模式
策略模式是指将策略(算法)封装起来,策略的目的是将算法和使用分离开。
3、代理模式
代理模式很好理解,我们不能直接使用目标函数,而是通过调用代理函数来实现对目标函数的使用。
4、发布订阅模式
发布订阅模式在实际应用中非常常见,例如,我们在微信App上关注了某个公众号,当该公众号有新文章发布时,就会通知我们。
发布订阅模式定义了一种一对多的依赖关系,当“一”发生变化,通知多个依赖。
5、命令模式
所谓命令模式就是将下要执行的业务逻辑封装到一个函数或类中,不需要具体谁来执行该命令的
构造函数、实例对象、原型对象三者的关系是什么?
每个函数(构造函数)都有一个原型对象,可以通过 .prototype 属性访问。
通过 new 关键字创建的实例对象可以访问构造函数的原型对象中的属性和方法。
实例对象有一个内部指针([[Prototype]] 或 __proto__),指向构造函数的原型对象。
构造函数的原型对象上的属性和方法可以被所有实例对象共享,而不是每个实例都拥有一份独立的拷贝,这有助于节省内存空间。
JS的基本数据类型判断有什么方法?
typeof
instanceof
constructor
toString
map、fillter、reduce 各自有什么作用?
1. Map: 生成新数组,遍历原数组的每个元素并对其进行转换后放入新数组。回调函数接收当前元素、索引和原数组作为参数。
2. Filter: 创建新数组,遍历原数组并将返回值为 `true` 的元素添加到新数组中,可用于删除不需要的元素。回调函数同样接收当前元素、索引和原数组作为参数。
3. Reduce: 将数组元素通过回调函数最终转换为一个值。它接受两个参数:回调函数和初始值。回调函数有四个参数:累计值、当前元素、当前索引和原数组。其中累计值是该函数执行的结果,初始值在第一次回调时作为第一个参数传入。
使用箭头函数应该注意什么?
1. 不要在对象里面定义函数,对象里面的行数应该用传统的函数方法
2. 不要在对原型对象上定义函数,在对象原型上定义函数也是遵循着一样的规则
3. 不要用箭头定义构造函数
4. 不要用箭头定义事件回调函数
为什么 JavaScript 是单线程
js作为主要运行在浏览器的脚本语言,js主要用途之一是操作DOM。
举一个栗子,如果js同时有两个线程,同时对同一个dom进行操作,这时浏览器应该听哪个线程的,如何判断优先级?
为了避免这种问题,js必须是一门单线程语言
import export commonJS 对比区别
ES6 模块与 CommonJS 在导入和导出方式上有很多不同:
1. **语法差异**:
- ES6 模块使用 `import` 和 `export` 关键字来导入和导出模块。
- CommonJS 使用 `require()` 函数来导入模块,`module.exports` 或 `exports` 导出模块。
2. **运行时加载 vs 编译时加载**:
- ES6 模块是在代码编译阶段进行静态分析,模块的依赖关系在编译时确定。
- CommonJS 是在运行时加载,模块的依赖关系在代码执行时动态确定。
3. **模块输出**:
- ES6 模块的输出是对值的引用,模块之间共享的是原始值的引用,可以动态更新。
- CommonJS 模块输出的是值的拷贝,模块之间共享的是拷贝的副本,不会动态更新。
4. **上下文差异**:
- 在 ES6 模块中,`this` 指向 `undefined`,因为 ES6 模块自身就是一个严格模式下的函数。
- 在 CommonJS 模块中,`this` 指向一个模块对象,通常表示当前模块的导出对象。
这些差异意味着在使用 ES6 模块和 CommonJS 模块时需要注意其运行机制、导入导出方式以及模块间数据共享的方式。
禁止事件冒泡,禁止默认事件
e.stopPropagation();
e.preventDefault();
进程线程区别是什么?
进程和线程的区别体现在以下几个方面:
1.地址空间和其他资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其他进程内不可见。
2.通信:进程间通信IPC(管道,信号量,共享内存,消息队列),线程间可以直接独写进程数据段(如全局变量)来进程通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
3.调度和切换:线程上下文切换比进程上下文切换快得多。
4.在多线程OS中,进程不是一个可执行的实体。
Promise 避免回调地狱的语法糖–实现链式调用的核心点是什么?
解决回调地狱的终极方法 async/await ES7的语法,可以通过 async/await让代码看起来像同步的
async异步 await等待
await 等待 就是当后面跟的是promise对象,就让他停止 ,先让里面的异步事情做完,在把结果返回给前面的新变量,在继续向后执行
他只生效当前作用域内部,也就是async函数内部。
实现链式调用的核心点:
在 then 中新创建的 Promise,它的状态变为 fulfilled 的节点是在上一个 Promise的回调执行完毕的时候。也就是说当一个 Promise 的状态被 fulfilled 之后,会执行其回调函数,而回调函数返回的结果会被当作 value,返回给下一个 Promise(也就是then 中产生的 Promise),同时下一个 Promise的状态也会被改变(执行 resolve 或 reject),然后再去执行其回调,以此类推下去…
ES6 箭头函数和普通函数有什么差异?
1. 相比普通函数更简洁的语法
2. 没有this,捕获其所在上下文的 this 值,作为自己的 this 值
3. 不能使用new,箭头函数作为匿名函数,是不能作为构造函数的,不能使用new
4. 不绑定arguments,用rest参数...解决
let test3=(...a)=>{console.log(a[1])} //22
5. 使用call()和apply()调用:由于 this 已经在词法层面完成了绑定,通过 call() 或 apply() 方法调用一个函数时,只是传入了参数而已,对 this 并没有什么影响:
6. 箭头函数没有原型属性
7. 不能简单返回对象字面量
let fun5 = ()=>({ foo: x }) //如果x => { foo: x } //则语法出错
8. 箭头函数不能当做Generator函数,不能使用yield关键字
9. 箭头函数不能换行
let a = ()
=>1; //SyntaxError: Unexpected token =>
简述call、apply、bind,call 和 apply哪个性能更好?
1、call()
call() 方法调用一个函数, 其具有一个指定的 this值和分别地提供的参数(参数的列表)。 第一个参数:在 fun 函数运行时指定的 this 值;如果指定了 null 或者 undefined 则内部 this 指向 window,后面的参数:指定的参数列表
2、apply()
apply()方法调用一个函数, 其具有一个指定的 this 值,以及作为一个数组(或类似数组的对象)提供的参数。apply() 与 call() 非常相似,不同之处在于提供参数的方式。apply() 使用参数数组而不是一组参数列表。
3、bind()
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
/* ------call 和 apply哪个性能更好?------ */
call的性能要比apply好一些,尤其是传递给函数的参数超过3个时所以后期开发的时候,可以使用call多一些
(传参数3个以内的话,call和apply性能差不多,超过3个以上call更好一些)
for-in 循环会遍历出原型上的属性吗?怎么避免遍历到原型上的属性
使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问
只遍历对象自身的属性,而不遍历继承于原型链上的属性,需要使用hasOwnProperty 方法过滤一下。
图片懒加载怎么实现?
原理:随着滚轮滚动,底部的图片会被不断地加载,从而显示在页面上,按需加载,当页面需要显示图片的时候才进行加载,否则不加载
eventloop 渲染在哪一步?
在前端开发中,任务可以分为同步任务和异步任务。同步任务会立即执行,而异步任务会在后台执行,并通过任务队列的机制进行协调。
事件循环是处理这些任务的方式之一,其中有两种任务:宏任务(Macro Task)和微任务(Micro Task)。主线程执行宏任务,每个宏任务结束后都要清空微任务队列。
常见宏任务包括整体脚本、定时器(setTimeout、setInterval)、I/O 操作、UI 交互事件等。微任务主要包括 Promise、MutationObserver、process.nextTick(在 Node.js 环境中)等。
一个事件循环(tick)的执行过程包括选择最先进入队列的宏任务,执行它,然后检查并执行微任务,更新渲染,重复这个过程。
举个例子:当执行整体脚本时,遇到 setTimeout 会将其回调函数放入宏任务队列,遇到 Promise 会将其 then 函数放入微任务队列。然后执行完整体脚本后,先执行微任务队列中的任务,再执行宏任务队列中的任务。这个过程不断循环。
简单来说,事件循环是一种处理同步和异步任务的机制,确保宏任务和微任务按照一定的顺序执行。这样可以保证页面渲染和用户交互的顺畅性。
super() 是否必须执行?不执行怎么让它不报错?
非必须,
在 JavaScript 中,super 指的是父类的构造函数
如果想在构造函数中使用this,你必须首先调用super。 先让父类做完自己的事
不执行无法使用this,
不报错的方法:
1:不使用this
2:手动修正this
什么是函数式编程?什么的声明式编程?
函数式编程注重于将函数作为主要的构建块,可以将函数传递给其他函数,也可以将函数作为返回值。这样的编程方式更注重于“做什么”而非“怎么做”。
声明式编程是一种以数据结构表达程序逻辑的方式,强调告诉计算机要做什么,而不是具体怎么做。SQL语句是声明式编程的例子,它描述了选择满足条件的数据,而不需要指定具体的操作步骤。
总的来说,函数式编程强调函数的重要性和灵活性,而声明式编程注重描述程序的目标而非具体执行步骤。
跨域是什么?有哪些解决跨域的方法和方案?
跨域是指在web开发中,当一个域名的网页通过JavaScript等方式访问另一个域名的资源时,由于浏览器的同源策略(同源是指协议、域名、端口号都相同),会阻止这种跨域请求,以保障用户的安全性。
解决跨域的方法有几种:
1.服务端代理请求: 通过服务器端进行跨域请求,服务器端去获取资源然后返回给客户端。这绕过了浏览器的同源策略。
2.JSONP跨域: 利用script标签的跨域能力,通过在页面中动态创建script标签,将请求放在script的src属性中,返回的数据会作为脚本被执行。需要注意的是JSONP只适用于GET请求。
3.CORS(跨域资源共享): 通过服务器设置响应头来允许跨域请求。CORS是一种标准,允许浏览器在请求头中携带特定的信息,服务器通过响应头表示是否允许跨域请求。这是一种比较现代且安全的解决方案。
4.Nginx代理跨域: 使用Nginx等服务器作为中间层,将跨域请求先发送到Nginx服务器,然后由Nginx服务器再去请求目标服务器,最后再将结果返回给客户端。这样也可以绕过浏览器的同源策略。
这些方法各有优劣,具体的选择取决于实际需求和场景。
0.1+0.2 等不等于 0.3?自己封装一个让他们相等的方法
在JavaScript中,由于使用的是二进制浮点数表示法,0.1 和 0.2 无法被精确表示。这导致它们相加的结果在二进制表示中存在舍入误差,使得最终结果变成一个接近但不完全等于 0.3 的数字,例如 0.30000000000000004。
为了解决这个问题,有两种常见的方法:
方法1:设置误差范围值
在JavaScript中,可以使用机器精度,通常表示为 2 的负 52 次方(2^-52),即 Number.EPSILON。通过比较两个浮点数之差的绝对值是否小于 Number.EPSILON,可以判断它们是否在可接受的误差范围内相等。
function numbersEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
这个函数可以用于比较两个浮点数是否相等,考虑到了可能的舍入误差。
方法2:转为整数运算
另一种方法是将浮点数转换为整数进行运算,然后再将结果转回浮点数。这是因为整数在二进制表示中不会有舍入误差。
function numbersEqual(a, b) {
// 将浮点数转换为整数,乘以一个足够大的值
const intA = a * 1e15;
const intB = b * 1e15;
// 判断整数是否相等
return intA === intB;
}
这个方法通过乘以一个足够大的值,将小数点移动到整数位,避免了浮点数的舍入误差。
这两种方法都可以用来处理在JavaScript中由于浮点数表示不精确而导致的数学运算误差。
2018/01/01 转换成 2018年/1月/1日
function fun(str){
var date = new Date(str)
return date.getFullYear()+'年/'+date.getMonth()+'月/'+date.getDate()+'日'
}
ES6 的新特性
1. const与let
2. 模板字符串
3. 解构赋值
4. 对象简写法
5. for...of循环
6. 展开运算符
7. 剩余参数(可变参数)
8. ES6箭头函数
9. 参数默认值
10.类和继承
11.模块化规范
map 和 foreach 的区别
forEach()方法不会返回执行结果,而是undefined。也就是说,forEach()会修改原来的数组。
map()方法会得到一个新的数组并返回。
模板引擎原理
1.模板定义: 开发者创建模板,其中包含占位符表示待填充的数据,同时使用特定语法来控制模板的结构和逻辑。
2.模板解析: 模板引擎对模板进行解析,转换为内部数据结构或语法树。在这个过程中,引擎识别和记录模板中的各种标记、占位符和控制语句,以便后续处理。
3.数据插入: 应用程序准备好数据后,模板引擎将数据插入到模板中的对应位置。这可能涉及将数据与模板中的占位符匹配,或者执行特定语法来嵌入动态内容。
4.渲染生成: 根据解析后的模板和插入的数据,模板引擎生成最终输出。这输出可以是HTML、XML、纯文本等格式,具体取决于模板引擎的设计和使用场景。
jQuery 怎么找到事件源元素
$(".btn").click(function(e){
// e 就是事件对象
e.target; // 事件的目标 dom
e.currentTarget; // 事件处理程序正在处理事件的那个元素
e.srcElement; // 事件的目标 ie
});
AMD 怎么加载文件的?
引入RequireJS库: 首先,在HTML文件中引入RequireJS库,可以通过在<script>标签中指定RequireJS库的路径来实现。这个库负责处理模块加载和定义。
定义模块: 将应用程序的功能划分为模块,并在各个模块文件中使用define函数进行定义。每个模块可以包含特定的功能、变量或对象,并通过return语句将其暴露出来供其他模块使用。
异步加载模块: 在主程序或其他模块中,通过require函数异步加载所需的模块。这样的异步加载使得应用程序能够在需要时动态加载模块,而不是在一开始就加载所有代码。
执行回调函数: require函数接受一个回调函数作为参数,该函数在所需的模块加载完成后执行。在回调函数中,你可以使用已加载的模块进行操作,以确保模块的功能正确执行。
jQuery,$() 能传什么参数? html 代码怎么解析? 传 function 呢?
这个函数是jQuery的核心,它的主要作用是根据传递的CSS选择器字符串来查找一组元素。jQuery的所有功能都围绕着这个函数展开。你可以将它看作是一个强大的工具,通过传递不同的选择器表达式,你可以在页面中找到特定的元素。
基本上,你可以用这个函数来执行类似于CSS选择器的操作,比如通过类名、标签名、ID等来选择元素。默认情况下,如果没有提供context参数,那么这个函数将在整个HTML文档中查找匹配的元素。如果提供了context参数,函数会在这个指定的上下文中进行查找。
一个简单的比喻是,这个函数就像是一个搜索工具,你提供了一个描述元素特征的搜索关键词(CSS选择器字符串),它会返回符合条件的所有元素。而且,这个函数不仅能在整个文档中搜索,还可以在指定的上下文环境中进行搜索。
总的来说,这个函数是jQuery的灵魂,通过巧妙地利用CSS选择器,它让你可以方便地选取和操作页面中的元素,为实现各种交互和动态效果提供了基础工具。
require.js 源码看过吗?怎么做到异步加载的
requirejs 导入模块的方式实际就是创建脚本标签,一切的模块都需要经过这个方法创建。requirejs 使用 onload 事件来处理回调函数:
简述 commonJS、AMD 和 CMD
CommonJS、AMD和CMD是JavaScript模块化的不同规范。
CommonJS:
1.特点: 同步加载模块的规范,主要用于服务器端JavaScript,例如Node.js。
2.加载方式: 使用require()和module.exports定义和导出模块功能。
3.模块定义: 在运行时同步加载,通过require()导入模块,通过module.exports导出模块。
4.适用范围: 主要用于服务器端应用程序。
AMD (Asynchronous Module Definition):
5.特点: 异步加载模块的规范,用于浏览器端JavaScript,解决浏览器中异步加载模块的问题。
6.加载方式: 通过define()函数定义模块,使用require()函数异步加载模块。
7.模块定义: 运行时异步加载,按需加载模块,提高性能和效率。
8.适用范围: 主要用于浏览器端,适合大型前端应用程序的开发。
CMD (Common Module Definition):
9.特点: 异步加载模块的规范,用于浏览器端JavaScript,类似于AMD。
10.加载方式: 通过define()函数定义模块,使用require()函数触发模块加载和执行。
11.模块定义: 更倾向于延迟执行模块代码,即在需要使用模块时才加载和执行。
12.适用范围: 主要用于浏览器端,强调延迟执行模块,适合动态加载场景。
总体而言,这些规范都致力于解决JavaScript模块化开发中的问题,但它们在模块定义、加载方式和适用场景上存在差异,每种规范都有其独特的优势和适用环境。
iframe 跨域问题,页面之间怎么传值?
一般有两个解决方案
一个是建立一个代理页面,通过代理页面传值,
另一个方法是通过H5的postMessage方法传值,
ES6 class 关键字原理跟 function 什么区别?
在 JavaScript 中,function可以通过call、apply和bind改变执行上下文,但是class却不能。虽然class本质上也是一个函数,但是在内部(例如使用 Babel 转译后),它做了一层代理来禁止这种行为。
关于构造器(constructor):
1.在使用function定义的构造函数中,其prototype.constructor属性指向构造器自身。
2.在使用class定义的类中,constructor实际上相当于定义在prototype属性上。
关于重复定义:
3.使用function定义的方法会覆盖之前定义的同名方法。
4.使用class定义的方法如果重复定义会导致报错。
关于原型或类中方法的枚举:
5.在class中定义的方法不能通过Object.keys(Point.prototype)枚举到。
6.在使用function构造器定义的原型方法(除了constructor)可以通过Object.keys(Point.prototype)枚举到。
7.所有原型方法属性都可以通过Object.getOwnPropertyNames(Point.prototype)访问到。
jQuery 优点和缺点
优点
1.出色的浏览器兼容性
2、出色的DOM操作的封装,使他具备强大的选择器,可以进行快速的DOM元素操作
3、可靠的事件处理机制、jq在处理事件绑定的时候是相当的可靠
4、完善的ajax(对ajax的封装非常好,不需要考虑复杂的浏览器的兼容和XMLhttprequest对象的创建和使用)
5、支持链式操作(什么是链式操作?通过‘.’来操作)和隐士迭代
6、减少服务器的压力和带宽并且加快了加载速度(为什么这么说?原因就是:当你打开网页之前打开了其他的网页,并且该网页也用了cdn的方式来
加载相同版本的jq文件,那么,浏览器就不会加载第二次,为啥舍近求远呢,和生活中的道理一样一样的!)
7、支持丰富的插件,当然你也可以自定义插件,再加上jq的文档也很丰富,对于程序员来说,是一件非常美好的事情
缺点
1.不能向后兼容。
每一个新版本不能兼容早期的版本。举例来说,有些新版本不再支持某些selector,新版jQuery却没有保留对它们的支持,而只是简单的将其移除。这可能会影响到开发者已经编写好的代码或插件。
2.插件兼容性。
与上一点类似,当新版jQuery推出后,如果开发者想升级的话,要看插件作者是否支持。通常情况下,在最新版jQuery版本下,现有插件可能无法正常使用。开发者使用的插件越多,这种情况发生的几率也越高。我有一次为了升级到jQuery 1.3,不得不自己动手修改了一个第三方插件。
3.多个插件冲突。
在同一页面上使用多个插件时,很容易碰到冲突现象,尤其是这些插件依赖相同事件或selector时最为明显。这虽然不是jQuery自身的问题,但却又确实是一个难于调试和解决的问题。
4.jQuery的稳定性。
jQuery没有让浏览器崩溃,这里指的是其版本发布策略。jQuery 1.3版发布后仅过数天,就发布了一个漏洞修正版1.3.1。他们还移除了对某些功能的支持,可能会影响许多代码的正常运行。我希望类似修改不要再出现。
5.对动画和特效的支持差。
在大型框架中,jQuery核心代码库对动画和特效的支持相对较差。但是实际上这不是一个问题。目前在这方面有一个单独的jQuery UI项目和众多插件来弥补此点。
ES6 中,数组监测怎么实现的(代理)
在 ES6 中,数组的监测通常通过代理(Proxy)来实现。Proxy 是 ES6 引入的一种机制,允许你创建一个对象的代理,可以截取该对象的操作。通过使用 Proxy,你可以拦截和定制对象的各种操作,包括读取属性、设置属性、函数调用等。
swiper 插件从后台获取数据没问题,css 代码啥的也没问题,但是图片不动,应该怎么解决?
主要原因:
swiper提前初始化了,而这个时候,数据还没有完全出来。
解决方法
从swiper 入手,在swiper中写 observer:true/observeParents:true
深拷贝是什么?项目哪里是用到了深拷贝?
深拷贝是复制对象及其所有嵌套对象的过程,确保新对象与原对象完全独立。在项目中,常用于状态管理、数据传递、配置对象等,以避免意外修改原始数据。在前端框架、状态管理库等场景中经常用到。
了解 ES6 的 Proxy 吗?
Proxy是ES6引入的元编程特性,用于创建对象代理以拦截和定制操作,如属性查找、赋值和函数调用。通过handler对象定义拦截陷阱,可以灵活控制对象行为,适用于数据验证、数据绑定等场景。功能强大,提供了可编程的方式改变对象操作。请问有其他问题我可以帮助你解答吗?
数据类型(判断,和=)堆栈、内存泄漏及垃圾回收机制
1,== 判断值是否相等; === 判断值和数据类型是否严格相等
2,Javascript堆栈和垃圾回收机制
堆栈溢出
? 当储存的数据导到某一限制时就会造成堆栈溢出
内存泄漏
? 当不断向堆中存储数据,而不进行清理,这就是内存泄漏
垃圾回收机制(清除孤儿机制)
? 语言当中一般分两种,一种是自动清理,一种是手动清理(GC),js中只有自动清理
? 垃圾回收机制就是将引用对中的地址的对象设置为null,并且将所有引用该地址的对象都设置为null,并且移除事件侦听
? 不会即时清除,垃圾回收车会根据内存的情况在适当的时候进行清除堆中的对象 内存到达一定程度了才会进行回收
函数节流、防抖。scroll resize 使用函数节流实现不要频繁触发事件的需求
防抖:
//scroll方法中的do somthing至少间隔500毫秒执行一次
window.addEventListener('scroll',function(){
var timer;//使用闭包,缓存变量
return function(){
if(timer) clearTimeout(timer);
timer = setTimeout(function(){
console.log('do somthing')
},500)
}
}());//此处()作用 - 立即调用return后面函数,形成闭包
节流:
//scroll方法中当间隔时间大于2s,do somthing执行一次
window.addEventListener('scroll',function(){
var timer ;//使用闭包,缓存变量
var startTime = new Date();
return function(){
var curTime = new Date();
if(curTime - startTime >= 2000){
timer = setTimeout(function(){
console.log('do somthing')
},500);
startTime = curTime;
}
}
}());//此处()作用 - 立即调用return后面函数,形成闭包
eval 是做什么的?性能怎么样?安全如何?
eval 是一个将字符串转换为可执行代码的 JavaScript 方法。它可以用于动态执行代码,但可能影响性能。使用 eval 时需要小心安全问题,避免执行不受信任的代码。通常推荐避免使用 eval,而是选择更安全的替代方法,如 JSON.parse 或函数构造器。
面向对象编程与面向过程编程的区别?
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
CommonJS 和 RequireJS 的实现原理
commonjs是通过module.exports导出模块,用require引入一个模块,原理:闭包
requirejs是通过define定义导出模块,用require引入模块。
promise的实现原理,如果我现在向服务器发送一个请求,但是我后悔了,不想让服务器返回数据,去实现一个delay
取消结束Promise的方法?
1. 返回一个pending状态的Promise,原Promise链会终止
2. Promise.race竞速方法
3. 当Promise链中抛出错误时,错误信息沿着链路向后传递,直至捕获
实现一个函数 clone,可以对 JavaScript 中的5种主要数据类型进行值复制
function clone(value) {
if (typeof value !== 'object' || value === null) {
return value; // 复制基本数据类型或 null
}
let clonedValue = Array.isArray(value) ? [] : {}; // 创建新对象或数组
for (let key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
clonedValue[key] = clone(value[key]); // 递归复制属性或元素
}
}
return clonedValue;
}
这个函数将检查传入值的类型。如果是基本数据类型(例如字符串、数字、布尔值、null 和 undefined),它将直接返回该值。如果是对象或数组,则会创建一个新的对象或数组,并递归地复制其属性或元素。这是一个简洁但有效的方法来复制 JavaScript 的主要数据类型。
请写出 ES6 Array.isArray()
if (!Array.isArray){
Array.isArray = function(arg){
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
写一个函数,接受可变个数参数,且每个参数均为数字,返回参数的最大值
function myMax(){
return Math.max(arguments)
}
如何判断一个对象是否为数组,函数
方法一: instanceof:
var arr=[];
console.log(arr instanceof Array) //返回true
方法二: constructor:
console.log(arr.constructor == Array); //返回true
方法三: Array.isArray()
console.log(Array.isArray(arr)); //返回true
浏览器有哪些兼容问题,你封装过什么插件
1.滚动条到顶端的距离(滚动高度)
2.滚动条到左端的距离
3. IE9以下byClassName
4. 获取非行内样式兼容 IE:currentStyle 标准:getComputedStyle
5. 获取事件对象的兼容
7. 获取键盘按键编码值的兼容
8. 阻止事件冒泡的兼容
9. 阻止超链接的默认行为的兼容
10. 添加事件监听器的兼容
11. 移除事件监听器的兼容
12. 获取事件源的兼容
已知a,b两个构造函数,现在 let c = new a(),如何在c的存储地址不变的情况下,改变c的继承(c->a 转为 c->b)
1. 改变原型链:通过改变C的prototype为b,实现内存地址不动,改变继承
请编写获取当前窗口地址中查询参数name的值,当前窗口地址为:https://foo.com/?id=1&name=tom
function GetQueryString(name){
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)
return unescape(r[2]);
return null;
}
开发过程中遇到内存泄漏的问题都有哪些?
1. 当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。
2. 由于是函数内定义函数,并且内部函数--事件回调的引用外暴了,形成了闭包。闭包可以维持函数内局部变量,使其得不到释放。
谈谈垃圾回收机制方法以及内存管理
垃圾回收方式
① 标记清除
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
② 引用计数
工作原理:跟踪记录每个值被引用的次数。一旦没有引用,内存就直接释放了。
内存管理
什么时候触发垃圾回收?
垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。
1、合理的GC方案:(1)、遍历所有可访问的对象; (2)、回收已不可访问的对象。
2、GC缺陷: (1)、停止响应其他操作;
3、GC优化策略: (1)、分代回收(Generation GC);(2)、增量GC
简述一下面象对象的六法则
1. 单一职责原则:一个类只做它该做的事情
2. 开闭原则:软件实体应当对扩展开放,对修改关闭
3. 依赖倒转原则:面向接口编程
4. 接口隔离原则:接口要小而专,绝不能大而全
5. 合成聚合复用原则:优先使用聚合或合成关系复用代码
6. 迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解(低耦合)
用 JavaScript 实现观察者模式
观察者模式的实现思路:
1.定义主题对象: 主题对象维护观察者列表,提供添加、删除观察者和通知观察者的方法。
2.定义观察者对象: 观察者需要实现一个更新方法,以响应主题状态的变化。
3.创建主题实例和观察者实例: 主题对象被创建,并观察者被注册到主题上。
4.主题状态变化时通知观察者: 当主题状态改变时,通知所有注册的观察者执行相应操作。
这样的设计模式有助于实现对象间的松耦合,特别适用于事件处理和实时更新的场景。
数组扁平化,不用 api
数组扁平化的思路是通过递归遍历数组,将嵌套的数组元素逐级展开为一个平级数组。在遍历数组的过程中,对于每个元素,判断其类型,若为数组则递归展开,若为非数组则加入结果数组。这样就能实现将多层嵌套的数组转化为一个平级数组的效果。
简述下 Promise 对象
Promise是异步编程的一种解决方案,它是一个对象,表示一个尚未完成且可能是异步的操作。具有两个主要特点:状态不受外界影响,且一旦状态改变就不会再变。Promise有三种状态:进行中(Pending)、已完成(Resolved)和已失败(Rejected)。通过这种机制,我们可以更合理、强大地处理异步操作,避免了回调地狱,提供了更清晰的异步流程表达和统一的接口。
jQuery 的事件委托方法 on,live,delegate之间有区别?
live 把事件委托交给了document(根节点),document 向下去寻找符合条件的元素(), 不用等待document加载结束也可以生效。
delegate可指定事件委托对象,相比于live性能更优,直接锁定指定选择器;
on事件委托对象选填,如果不填,即给对象自身注册事件,填了作用和delegate一致。
统计字符串中字母个数或统计最多的字母数
统计字符串中字母的个数或最多的字母数的思路是遍历字符串中的每个字符,使用一个数据结构(例如哈希表)来记录每个字母出现的次数。在遍历的过程中,对每个字符进行判断,如果是字母,则更新对应字母的计数。最终,可以得到每个字母出现的次数,进而进行进一步的统计,例如找到出现次数最多的字母。
这种方法的关键在于建立字符和计数的映射关系,以便有效地统计字母的出现次数。这样的思路可以适用于解决统计字符串中字母个数或最多字母数的问题。
JavaScript 中 callee 和 caller 的作用
这两者都与函数调用的上下文和调用链有关。arguments.callee 主要用于函数内部的递归调用,而 caller 主要用于获取调用当前函数的上下文。需要注意的是,由于严格模式下不允许使用 arguments.callee,推荐使用具名函数表达式或箭头函数来实现递归。caller 在现代 JavaScript 开发中使用较少,因为它会在严格模式下报错,而且其使用场景较为有限。
简述同步和异步的区别
同步和异步是指程序执行的两种不同方式:
1.同步: 在同步执行中,任务按照顺序一个接一个地执行,当前任务执行完毕后才能开始下一个任务。这意味着一个任务的执行会阻塞后续任务的开始,直到当前任务完成。
2.异步: 在异步执行中,任务不会按照顺序一个接一个地执行。相反,程序会继续执行后续任务,而不管前一个任务是否完成。当异步任务完成时,通常通过回调函数、Promise 或事件来处理结果。
关键区别在于同步任务会阻塞程序的执行,而异步任务允许程序在等待结果的同时继续执行其他任务。异步操作常见于处理网络请求、文件读取等涉及时间较长的操作,以保持程序的响应性。同步操作则更适用于顺序执行且不涉及长时间等待的任务。选择同步或异步取决于任务的性质和程序的设计需求。
使用正则表达式验证邮箱格式
function fChkMail(emailAddress){
var reg = new RegExp("^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$");
var bChk=reg.test(emailAddress);
return bChk;
}
写一个 function,清除字符串前后的空格。(兼容所有的浏览器)
function trim(str) {
if (str && typeof str === "string") {
return str.replace(/(^\s*)|(\s*)$/g,""); //去除前后空白符
}
}
如何避免回调地狱?
1. Promise 对象
2. ES6 co/yield方案
3. ES7 async/await 方案
实现一个函数 add(),运算结果可以满足如下预期结果
这种函数称为柯里化函数。为了实现这种功能,可以让 add() 函数返回一个函数,并且这个返回的函数也能接收参数。这样可以实现链式调用,每次调用都返回一个新的函数来处理传入的参数。最终,当需要获取结果时,对所有传入的参数进行求和计算即可。
使用 setTimeout 模拟 setInterval 的功能做一个60秒的倒数计时
使用 setTimeout 模拟 setInterval 的思路是通过递归调用 setTimeout 来实现定时执行的效果。在每次 setTimeout 的回调函数中,可以进行倒计时的逻辑,然后再设置下一次的 setTimeout,以实现循环执行。
具体而言,通过一个计数器变量记录经过的时间,每次 setTimeout 的回调函数中更新这个计数器,并检查是否达到了目标时间。如果未达到目标时间,继续设置下一次的 setTimeout;如果已经达到目标时间,结束递归调用,完成倒计时。这样就能在模拟中实现类似 setInterval 的效果。
如何判断 user 对象里有没有 a 这个属性?如果把user对象中所有的属性都输出出来?
js对象的Object.hasOwnProperty()方法
for(item for user){
console.log(item)
}
如何用 setTImeout 来实现 setInterval?
当使用 setTimeout 模拟 setInterval,可以在 setTimeout 的回调函数中再次设置一个新的 setTimeout,这样就能够创建连续的定时触发效果。递归调用 setTimeout 实现类似 setInterval 的功能。
如何自定义事件?
要自定义事件,首先用 new Event('eventName') 创建一个事件对象。然后,使用 dispatchEvent() 触发这个自定义事件。为了处理这个自定义事件,用 addEventListener() 添加一个监听器,以便在事件触发时执行特定的操作。
document load 和 documen ready的区别
document load 在整个页面及资源加载完成后触发,而 document ready 在 DOM 结构解析完成后触发。因此,document ready 的触发可能比 document load 更早,适合执行早期脚本操作。
var let const的区别
var 具有变量提升,无块级作用域,允许重复声明,可变值。
let 具有块级作用域,不允许重复声明,可变值。
const 具有块级作用域,不允许重复声明,常量,对于基本类型值不可变,对于引用类型值变量不可变。 推荐优先使用 const。
Promise.resolve(Promise.resolve(1)).then(console.log) 输出?
1
bind 的作用是什么?
bind()方法主要就是将函数绑定到某个对象,
bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()第一个参数的值
将 1234567 转换为 1,234,567
function fun(n){
return String(n).replace(/(?!^)(?=(\d{3})+\.)/g, ",")
}
Array.forEach() 与 Array.map() 的区别,Array.slice() 与 Array.splice() 的区别?
Array.forEach() 与 Array.map() 的区别:
1.forEach 用于迭代数组,对每个元素执行指定的回调函数,但它没有返回值,仅用于遍历数组。
2.map 同样用于迭代数组,但它会返回一个新数组,新数组的元素是原数组元素经过回调函数处理后的结果。
Array.slice() 与 Array.splice() 的区别:
3.slice 用于提取数组的一部分,返回一个新数组,原数组不受影响。
4.splice 用于删除或替换数组的元素,会修改原数组,并返回被删除元素组成的数组。
将 [1,2] 与 [3,[4]] 合并为 [1,2,3,[4]]
JS数组合并方法:
let arr3 = [1,2].concat([3,[4]]); //[1,2,3,[4]]
什么是反柯里化
在JavaScript中,当我们调用对象的某个方法时,其实不用去关心该对象原本是否被设计为拥有这个方法,这是动态类型语言的特点。可以通过反柯里化(uncurrying)函数实现,让一个对象去借用一个原本不属于他的方法。
如何实现函数的柯里化?比如 add(1)(2)(3)
柯里化是将接收多个参数的函数转化为接收一个参数的函数,并返回一个新函数的过程。实现柯里化的基本思路是每次只接收一个参数,并返回一个接收下一个参数的函数,直到所有参数都收集完毕,然后执行原始函数并返回结果。这样可以通过链式调用的方式传递参数。
Cookie 有哪些属性?其中HttpOnly,Secure,Expire分别有什么作用?
name 字段为一个cookie的名称。
value 字段为一个cookie的值。
domain 字段为可以访问此cookie的域名。
path 字段为可以访问此cookie的页面路径。 比如domain是abc.com,path是/test,那么只有/test路径下的页面可以读取此cookie。
expires/Max-Age 字段为此cookie超时时间。若设置其值为一个时间,那么当到达此时间后,此cookie失效。不设置的话默认值是Session,意思是cookie会和session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效。
Size 字段 此cookie大小。
http 字段 cookie的httponly属性。若此属性为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie。
secure 字段 设置是否只能通过https来传递此条cookie
1.HttpOnly:防止通过 JavaScript 访问 Cookie,提高防御跨站脚本攻击(XSS)的能力。
2.Secure:仅在使用 HTTPS 加密连接时发送 Cookie,增加信息传输的安全性。
3.Expires(或 Max-Age):规定 Cookie 的有效期,控制 Cookie 在浏览器中的存活时间,管理用户状态等。
移动端点击事件 300ms 延迟如何去掉?原因是什么?
300毫秒原因:
当用户第一次点击屏幕后,需要判断用户是否要进行双击操作,于是手机会等待300毫秒,
解决方法:FastClick.js
FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。
简述 aync await 的好处
1. async/await最重要的好处是同步编程风格
2. async/await有本地浏览器支持。截至今天,所有主流浏览器 查看都完全支持异步功能。
3. async关键字。它声明 getBooksByAuthorWithAwait()函数返回值确保是一个 promise,以便调用者可以安全调用 getBooksByAuthorWithAwait().then(...)或 await getBooksByAuthorWithAwait()
async 和 await 、promise的区别 和 这两个的本质
/*---------Promise概念:---------*/
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,简单地说,Promise好比容器,里面存放着一些未来才会执行完毕(异步)的事件的结果,而这些结果一旦生成是无法改变的
/*---------async await概念:---------*/
async await也是异步编程的一种解决方案,他遵循的是Generator 函数的语法糖,他拥有内置执行器,不需要额外的调用直接会自动执行并输出结果,它返回的是一个Promise对象。
两者的区别:
Promise的出现解决了传统callback函数导致的“地域回调”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。而async await代码看起来会简洁些,使得异步代码看起来像同步代码,await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句。
async await与Promise一样,是非阻塞的。
async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数。
异步解决方案有哪些?
1.回调函数callback
2.事件发布订阅
3.Promise
4.Generator
5.async/await
浏览器和 Node 事件循环的区别?
浏览器和 Node.js 都采用单线程事件循环,但处理异步操作的方式有所不同。浏览器主要关注用户交互、渲染等,使用宏任务和微任务队列;Node.js 处理文件 I/O、网络请求等,采用 libuv 库,具有不同的事件阶段,包括轮询和检查。两者在单线程环境下优化异步操作,但适应不同的运行环境和需求。
微任务和宏任务有什么区别?
微任务在当前任务完成后触发,优先于宏任务执行,确保在下一个渲染步骤前完成。宏任务由浏览器触发,包括 I/O 操作、定时器等,在事件循环的末尾执行,等待微任务执行完毕后执行。理解它们的触发时机和执行顺序有助于优化异步代码。
你对事件循环有了解吗?说说看
事件循环是异步操作的执行模型,包括宏任务队列和微任务队列。在每个循环中,从宏任务队列中取一个任务执行,执行过程可能产生新的宏任务和微任务。微任务优先于宏任务执行,确保及时响应用户操作。这个循环不断重复,直到任务队列为空。理解事件循环是编写高效异步代码的关键。
ES5/ES6 的继承除写法以外还有什么区别?
继承在ES5中通过创建子类实例并添加父类方法(Parent.apply(this)),而在ES6中通过先创建父类实例(调用super()),再用子类构造函数修改实现。ES5可通过原型或构造函数实现,而ES6使用class和extends。在子类构造函数中必须调用super()以获取父类实例,否则报错。super指代父类实例,需在子类构造函数中使用。
谈一下你对原型链的理解
因为每个对象和原型都有原型,对象的原型指向原型对象,
而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。
介绍箭头函数的 this
由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值
1. 所以 call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。
2. 考虑到 this 是词法层面上的,严格模式中与 this 相关的规则都将被忽略
作为方法的箭头函数this指向全局window对象,而普通函数则指向调用它的对象
如何确定 this 指向?改变 this 指向的方式有哪些?
this 指向:
1. 全局上下文(函数外)
无论是否为严格模式,均指向全局对象。注意:严格模式下全局对象为undifined
2. 函数上下文(函数内)
默认的,指向函数的调用对象,且是最直接的调用对象:
简单调用,指向全局对象注意:严格模式下全局对象为undifined,某些浏览器未实现此标准也可能会是window
改变this指向的方式:
第一种: new关键字改变this指向
第二种: call()
第三种:apply()
第四种:bind()
简述闭包的问题以及优化
闭包的缺点:占用内层空间 大量使用闭包会造成 栈溢出
由于闭包会一直占用内存空间,直到页面销毁,我们可以主动将已使用的闭包销毁:
将闭包函数赋值为null 可以销毁闭包
对闭包的看法,为什么要用闭包?说一下闭包的原理以及应用场景?闭包的 this 指向问题?
闭包的作用:
1. 在外部访问函数内部的变量
2. 让函数内的局部变量可以一直保存下去
3. 模块化私有属性和公共属性
闭包的原理:
全局变量生存周期是永久,局部变量生存周期随着函数的调用介绍而销毁。
闭包就是 在函数中定义且成为该函数内部返回的函数的自由变量 的变量,该变量不会随着外部函数调用结束而销毁。
(注:不光是变量,函数内声明的函数也可以形成闭包)
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。
闭包的应用场景:
1. 返回值 最常见的一种形式
2. 函数赋值 一种变形的形式是将内部函数赋值给一个外部变量
3. 函数参数 通过函数参数引用内部函数产生闭包
4. IIFE(自执行函数)
5. 循环赋值
6. getter和setter
7.迭代器(计数器)
8.触发事件
闭包的this指向问题:
1.this指向window对象(因为匿名函数的执行具有全局性,所以其this对象指向window);
2.不能实现value加1(每个函数在被调用时都会自动取得两个特殊变量,this和arguments,内部函数在搜索这两个对象时,只会搜索到其活动对象为止,所以不能实现访问外部函数的this对象);
3.修改代码实现正确功能
第一种解决办法:定义变量that用于保存上层函数的this对象
第二种解决办法:使用apply改变helper的this对象指向,使其指向myNumber对象
第三种解决办法:使用bind绑定,和apply相似,只是它返回的是对函数的引用,不会立即执行
说说你对 JavaScript 的作用域的理解。什么是作用域链?
在 JavaScript 中有两种作用域类型:
1. 局部作用域:只能在函数内部访问它们
2. 全局作用域:网页的所有脚本和函数都能够访问它
JavaScript 拥有函数作用域:每个函数创建一个新的作用域。
作用域决定了这些变量的可访问性(可见性)。
函数内部定义的变量从函数外部是不可访问的(不可见的)。
作用域链:
当查找变量的时候,会先从当前上下文的变量对象中查找,
如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。
这样由多个执行上下文的变量对象构成的链表就叫做作用域链
介绍一下类型转换
/*-------------------显式转换---------------------*/
1. toString() // 转化为字符串,不可以转null和underfined
2. Number() // 转换为数字,字符串中有一个不是数值的字符,返回NaN
3. parseInt() // 转换为数字,第一个字符不是数字或者符号就返回NaN
4. String() // 转换为字符串,
5. Boolean() // 转换为布尔值
/*-------------------隐式转换(+-)---------------------*/
当 JavaScript 尝试操作一个 "错误" 的数据类型时,会自动转换为 "正确" 的数据类型
1. num + "" -> String
2. num + bool -> num
// 当加号运算符时,String和其他类型时,其他类型都会转为 String;其他情况,都转化为Number类型
3. string - num -> num
// 其他运算符时, 基本类型都转换为 Number,String类型的带有字符的比如:
4. 'a1' - num -> NaN
// 与undefined 一样。
/*-------------------隐式转换(逻辑表达式)---------------------*/
1. 对象和布尔值比较
对象和布尔值进行比较时,对象先转换为字符串,然后再转换为数字,布尔值直接转换为数字
[] == true; //false []转换为字符串'',然后转换为数字0,true转换为数字1,所以为false
2. 对象和字符串比较
对象和字符串进行比较时,对象转换为字符串,然后两者进行比较。
[1,2,3] == '1,2,3' // true [1,2,3]转化为'1,2,3',然后和'1,2,3', so结果为true;
3. 对象和数字比较
对象和数字进行比较时,对象先转换为字符串,然后转换为数字,再和数字进行比较。
[1] == 1; // true `对象先转换为字符串再转换为数字,二者再比较 [1] => '1' => 1 所以结果为true
4. 字符串和数字比较
字符串和数字进行比较时,字符串转换成数字,二者再比较。
'1' == 1 // true
5. 字符串和布尔值比较
字符串和布尔值进行比较时,二者全部转换成数值再比较。
'1' == true; // true
6. 布尔值和数字比较
布尔值和数字进行比较时,布尔转换为数字,二者比较。
true == 1 // true
介绍一下 typeof 区分类型的原理
typeof原理: 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位存储其类型信息。
000: 对象
010: 浮点数
100:字符串
110: 布尔
1: 整数
/*----------------------------------------------*/
typeof null 为"object", 原因是因为 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回"object"
介绍一下JS的内置类型有哪些?
1. 空类型:null
2. 未定义:undefined
3. 布尔:boolean
4. 数字:number
5. 字符串:string
6. 符号:symbol(ES6新增)
7. 对象:object
除了对象之外,其他为基本类型.
文章来源:https://blog.csdn.net/wsq_yyds/article/details/134841817
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!