【前端面试题】每日一个前端面试专题
一、typeof 与 instanceof 区别?如何instanceof的原理
问题回答思路
-
typeof和instanceof 都可以检测数据类型,typeof检测如果是基本数据类型返回对应的结果。如果检测是对象的话,默认返回一个object,所以无法区分那种对象。
typeof 数据
-
instanceof是用于判断指定的数据,是否满足和指定构造函数匹配,只要匹配就说明他是同一种数据类型,主要用于判断对象的类型,无法用于基本数据类型,基本数据类型不存在构造器
[] instanceof Array {} instanceof Object
回答原理
instanceof的原理就是获取左边数据的构造函数,用于和右边(指定的构造函数)进行判断是否相等。
如果左边获取到的构造函数不等于右边,并不会马上结束,循环继续找对象的原型的函数是否和右边相等,直到找完了原型链对象,都匹配不了,返回false
代码复现:
function myInstanceof(left, right) {
// 这里先用typeof来判断基础数据类型,如果是,直接返回false
if(typeof left !== 'object' || left === null) return false;
// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left);
while(true) {
if(proto === null) return false;
if(proto === right.prototype) return true;//找到相同原型对象,返回true
proto = Object.getPrototypeof(proto);
}
}
二、bind、call、apply 区别?如何实现一个bind?
回答思路
从上面可以看到,apply
、call
、bind
三者的区别在于:
- 三者都可以改变函数的
this
对象指向 - 三者第一个参数都是
this
要指向的对象,如果如果没有这个参数或参数为undefined
或null
,则默认指向全局window
- 三者都可以传参,但是
apply
是数组,而call
是参数列表,且apply
和call
是一次性传入参数,而bind
可以分为多次传入 bind
是返回绑定this之后的函数,apply
、call
则是立即执行
三、说说 JavaScript 中内存泄漏的几种情况?
内存泄漏不等于内存溢出。完全两个概念。内存泄漏可能导致内存溢出
回答思路
内存泄漏指的就是代码运行过程中,变量使用完毕后无法被垃圾回收机制销毁(回收)。一直占这内存。这种情况称为内存泄漏。
常见的内存泄漏:
- 使用闭包,导致延长了变量的声明周期,函数调用完成后,变量无法及时被销毁。
- 定时器,组件中开启定时器后,离开了这个组件,但是定时器忘记清除,导致内存一直在运行定时器。
- 意外的全局变量。定义变量忘记使用var来声明。导致这个变量是全局的。函数被销毁了,变量无法被销毁
- dom元素删除:给dom元素绑定了事件,但是dom元素被删除后,事件绑定函数还存在。
四、说说 Javascript 数字精度丢失的问题,如何解决?
标准答案:
在javascript的世界中,所有的数据要进行处理,都要进行二进制的转换。
整数转化为二进制没有任何问题。但是小数转化二二进制就会出现无法整除的情况。
比如:
0.3-0.2 = 0.99999998
本身0.3和0.2
五、大文件上传如何做断点续传?
标准答案:
大文件上传上传遇到的问题,文件过大,上传很慢,上传中途可能出现网络异常、加上暂停功能解决续传的问题。
所以才出现了大文件断点续传。
思路:
-
本地通过
<input type="file" onchange="">
change事件可以获取文件对象file【size、type、name】 -
前端获取file对象后,使用slice对文件进行切片操作。得到多个切片数组。
-
前端封装文件上传代码。通过promise.all的方式来进行批量上传。虽然js是单线程的语言。但是浏览器是多线程,可以一次性处理多个文件上传任务。
promise.all() //同时处理多个promise任务,当所有任务成功。all返回成功,但凡一个失败。all失败 promise.race() //同时处理多个promise任务,拿到最快的请求结果 promise.allsetlet() //同时处理多个promise任务,不管成功还是失败,都会返回结果。返回所有结果
-
如果需要暂停,需要使用axios提供的cancleToken来取消请求。
-
所有切片上传完毕后,在调用后端提供的合并接口,要求后端合并所有切片。返回结果给前端
-
前端拿到结果在提示用户上传成功
六、web常见的攻击方式有哪些?如何防御?
web攻击指的就是利用前端页面。或者前端身份信息。对服务区发起恶意的请求。
-
XSS (Cross Site Scripting) 跨站脚本攻击:
- 攻击目标是为了盗取存储在客户端的
cookie
或者其他网站用于识别客户端身份的敏感信息。一旦获取到合法用户的信息后,攻击者甚至可以假冒合法用户与网站进行交互。 - 在页面文本框里面容易输入
<script>
标签,里面写死循环造成对服务器攻击,或者写入特殊的代码。
- 攻击目标是为了盗取存储在客户端的
-
CSRF(Cross-site request forgery)跨站请求伪造
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求
利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目
一个典型的CSRF攻击有着如下的流程:
- 受害者登录a.com,并保留了登录凭证(Cookie)
- 攻击者引诱受害者访问了b.com
- b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie
- a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求
- a.com以受害者的名义执行了act=xx
- 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作
-
SQL注入攻击:前端在输入框恶意输入sql语句,传递给后端,后端服务器如果在没有验证的情况,直接拿去进行sql拼接。就可能出现绕过身份,直接访问数据库。
七、如何递归实现一个深度拷贝
递归实现一个深度拷贝,最核心的问题就是知道你每次克隆的属性是基本类型还是引用类型,
如果是基本类型直接返回结果,赋值给新对象,如果是引用类型,我们采用递归来生成新对象,在返回上一层对象
function deepClone(obj, hash = new Map()) {
if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== "object") return obj;
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
八、事件代理你在项目中那个地方使用到了?具体说一个场景
标准:
事件代理和事件委托利用了冒泡机制来实现页面优化。
当需要绑定在组件身上的事件冒泡到父组件,我们可以在父组件统一处理业务
比如在React开发过程中,表格的每一行数据都添加删除功能。
如果你每一行数据都绑定一个点击事件,数据多了性能并不好。
可以在表格上面动态获取点击的按钮编号来实现删除。
九、说一下你对this在不同环境下的指向问题
标准:
this指向最核心的概念:
- this永远指向一个对象,无论在哪个环境下执行
- 箭头函数本身没有this,他的this来源于父级作用域(作用域概念理解清楚)
- 普通函数的this,遵循一个原则,谁调用这个函数,this就指向谁。
十、ajax原理是什么?axios和ajax什么关系。
ajax就是浏览器提出的前端异步请求的一种技术。
核心代码如下:
const xmlhttp = new XMLHttpRequest()
//建立连接
xmlhttp.open(method, url, [async][, user][, password])
xmlhttp.open("GET","http://127.0.0.1:8002/users/findUser",false)
//发送请求
xmlhttp.send("username=xiaowang&password=123")
//得到响应结果
//监听浏览器状态码,一旦状态码发生变化,这个事件触发
xnlhttp.onreadystatechange = function(){
if(xmlhttp.status>=200 && xmlhttp.readystate==4){
const msg = xmlhttp.responseText
const data = JSON.parse(msg)
}
}
步骤:
- 创建异步请求对象XMLHttpRequest。
- 建立服务器连接,open函数。只有通过TCP三次握手成功后,前端才可以继续后面的操作
- 前端可以发送请求,send传递参数
- 前端要直到请求成功没有,只能通过状态码来判断。所以需要onreadystatechange监听状态码的变化。当状态码等于200,才代表成功,readystate等于才代表可以获取后端数据
axios是一个第三方的请求工具。
底层:ajax+promise进行封装。调用axios的api函数默认返回的就是promise对象。解决了回调地狱的问题。
并且还提供了请求拦截器、响应拦截器等等功能。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!