BAT及各大互联网公司2020前端笔试面试题--JavaScript篇

1.请你谈谈Cookie的优缺点

优点:极高的扩展性和可用性

  1. 数据持久性。
  2. 不需要任何服务器资源。 Cookie 存储在客户端并在发送后由服务器读取。
  3. 可配置到期规则。 控制 cookie 的生命期,使之不会永远有效。偷盗者很可能拿到一个过期的 cookie 。
  4. 简单性。 基于文本的轻量结构。
  5. 通过良好的编程,控制保存在 cookie 中的 session 对象的大小。
  6. 通过加密和安全传输技术( SSL ),减少 cookie 被破解的可能性。
  7. 只在 cookie 中存放不敏感数据,即使被盗也不会有重大损失。

缺点:

  1. Cookie 数量和长度的限制 。
    数量:每个域的 cookie 总数有限。
    a) IE6 或更低版本最多 20 个 cookie
    b) IE7 和之后的版本最后可以有 50 个 cookie
    c) Firefox 最多 50 个 cookie
    d) chrome 和 Safari 没有做硬性限制
    长度:每个 cookie 长度不超过 4KB ( 4096B ),否则会被截掉。
  2. 潜在的安全风险 。 Cookie 可能被拦截、篡改。如果 cookie 被拦截,就有可能取得所有的 session 信息。
  3. 用户配置为禁用 。有些用户禁用了浏览器或客户端设备接受 cookie 的能力,因此限制了这一功能。
  4. 有些状态不可能保存在客户端 。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。

2.Array.prototype.slice.call(arr,2)方法的作用是:

利用Array原型上的slice方法,使用call函数的第一个参数,让这个方法中的this指向arr,并传递参数2,实际上等于arr.slice(2),即从下标为2开始截取到末尾。

3.JavaScript的数据类型都有什么?

基本数据类型:String,Boolean,Number,Undefined, Null

引用数据类型:Object(Array,Date,RegExp,Function)

那么问题来了,如何判断某变量是否为数组数据类型?

方法一.判断其是否具有“数组性质”,如slice()方法。可自己给该变量定义slice方法,故有时会失效
方法二.obj instanceof Array 在某些IE版本中不正确
方法三.方法一二皆有漏洞,在ECMA Script5中定义了新方法Array.isArray(), 保证其兼容性,最好的方法如下:

if(typeof Array.isArray==="undefined")
{
  Array.isArray = function(arg){
        return Object.prototype.toString.call(arg)==="[object Array]"
    }; 
}

4.简单说一下浏览器本地存储是怎样的

总的来说,浏览器存储分为以下几种:
1、Cookie存储,明文,大小限制4k等
2、localStorage,持久化存储方式之一,不用在两端之间传输,且限制大小为10M
3、sessionStorage,会话级存储方式,浏览器关闭立即数据丢失
4、indexDb,浏览器端的数据库

5.原型链:

原型链是由原型对象组成,每个对象都有 proto 属性,指向了创建该对象的构造函数的原型,proto 将对象连接起来组成了原型链。是一个用来实现继承和共享属性的有限的对象链。

属性查找机制: 当查找对象的属性时,如果实例对象自身不存在该属性,则沿着原型链往上一级查找,找到时则输出,不存在时,则继续沿着原型链往上一级查找,直至最顶级的原型对象Object.prototype,如还是没找到,则输出 undefined;

属性修改机制: 只会修改实例对象本身的属性,如果不存在,则进行添加该属性,如果需要修改原型的属性时,则可以用: b.prototype.x = 2;但是这样会造成所有继承于该对象的实例的属性发生改变。

6.变量对象

变量对象,是执行上下文中的一部分,可以抽象为一种 数据作用域,其实也可以理解为就是一个简单的对象,它存储着该执行上下文中的所有 变量和函数声明(不包含函数表达式)。
活动对象 (AO): 当变量对象所处的上下文为 active EC 时,称为活动对象。

7.作用域链

我们知道,我们可以在执行上下文中访问到父级甚至全局的变量,这便是作用域链的功劳。作用域链可以理解为一组对象列表,包含 父级和自身的变量对象,因此我们便能通过作用域链访问到父级里声明的变量或者函数。
由两部分组成:

  • [[scope]]属性: 指向父级变量对象和作用域链,也就是包含了父级的[[scope]]和AO
  • AO: 自身活动对象
    如此 [[scopr]]包含[[scope]],便自上而下形成一条 链式作用域。

8.闭包

闭包属于一种特殊的作用域,称为 静态作用域。它的定义可以理解为: 父函数被销毁 的情况下,返回出的子函数的[[scope]]中仍然保留着父级的单变量对象和作用域链,因此可以继续访问到父级的变量对象,这样的函数称为闭包。
闭包会产生一个很经典的问题:
多个子函数的[[scope]]都是同时指向父级,是完全共享的。因此当父级的变量对象被修改时,所有子函数都受到影响。
解决:
变量可以通过 函数参数的形式 传入,避免使用默认的[[scope]]向上查找
使用setTimeout包裹,通过第三个参数传入
使用 块级作用域,让变量成为自己上下文的属性,避免共享

9. script 引入方式:

html 静态<script>引入
js 动态插入<script>
<script defer>: 延迟加载,元素解析完成后执行
<script async>: 异步加载,但执行时会阻塞元素渲染

10.对象的拷贝

浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
Object.assign
展开运算符(...)

深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响
JSON.parse(JSON.stringify(obj)): 性能最快
具有循环引用的对象时,报错
当值为函数、undefined、或symbol时,无法拷贝
递归进行逐一赋值

11.new运算符的执行过程

新生成一个对象
链接到原型: obj.proto = Con.prototype
绑定this: apply
返回新对象(如果构造函数有自己 retrun 时,则返回该值)

12.instanceof原理

能在实例的 原型对象链 中找到该构造函数的prototype属性所指向的 原型对象,就返回true。即:

// __proto__: 代表原型对象链
instance.[__proto__...] === instance.constructor.prototype
// return true

13.类型判断

判断 Target 的类型,单单用 typeof 并无法完全满足,这其实并不是 bug,本质原因是 JS 的万物皆对象的理论。因此要真正完美判断时,我们需要区分对待:
基本类型(null): 使用 String(null)
基本类型(string / number / boolean / undefined) + function: 直接使用 typeof即可
其余引用类型(Array / Date / RegExp Error): 调用toString后根据[object XXX]进行判断
很稳的判断封装:

let class2type = {}'Array Date RegExp Object Error'.split(' ').forEach(e => class2type[ '[object ' + e + ']' ] = e.toLowerCase()) 
function type(obj) {
    if (obj == null) return String(obj)
    return typeof obj === 'object' ? class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof obj
}

14.模块化

模块化开发在现代开发中已是必不可少的一部分,它大大提高了项目的可维护、可拓展和可协作性。通常,我们 在浏览器中使用 ES6 的模块化支持,在 Node 中使用 commonjs 的模块化支持。
分类:
oes6: import / export
ocommonjs: require / module.exports / exports
oamd: require / defined
require与import的区别
orequire支持 动态导入,import不支持,正在提案 (babel 下可支持)
orequire是 同步 导入,import属于 异步 导入
orequire是 值拷贝,导出值变化不会影响导入值;import指向 内存地址,导入值会随导出值而变化

15.防抖与节流

防抖与节流函数是一种最常用的 高频触发优化方式,能对性能有较大的帮助。
防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。

function debounce(fn, wait, immediate) {
    let timer = null

    return function() {
        let args = arguments
        let context = this

        if (immediate && !timer) {
            fn.apply(context, args)
        }

        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(context, args)
        }, wait)
    }
}

节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms执行一次即可。

function throttle(fn, wait, immediate) {
    let timer = null
    let callNow = immediate
    
    return function() {
        let context = this,
            args = arguments

        if (callNow) {
            fn.apply(context, args)
            callNow = false
        }

        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(context, args)
                timer = null
            }, wait)
        }
    }
}

为了不影响阅读体验,只分享部分面试题,更多面试题及答案可以【点击我】阅读下载哦~无偿分享给大家,算是一个感恩回馈吧

16.函数柯里化

在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,称为函数的柯里化。通常可用于在不侵入函数的前提下,为函数 预置通用参数,供多次重复调用。

const add = function add(x) {
    return function (y) {
        return x + y
    }
}
const add1 = add(1)
add1(2) === 3
add1(20) === 21

17.get请求传参长度的误区

误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。
实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下面几点:
HTTP 协议 未规定 GET 和POST的长度限制
GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度
不同的浏览器和WEB服务器,限制的最大长度不一样
要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度 8182byte

18.补充get和post请求在缓存方面的区别

post/get的请求区别,具体不再赘述。
补充补充一个get和post在缓存方面的区别:
get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。

19.说说前端中的事件流

HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。
事件捕获阶段
处于目标阶段
事件冒泡阶段
addEventListener:addEventListener 是DOM2 级事件新增的指定事件处理程序的操作,这个方法接收3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
IE只支持事件冒泡。

20.如何让事件先冒泡后捕获

在DOM标准事件模型中,是先捕获后冒泡。但是如果要实现先冒泡后捕获的效果,对于同一个事件,监听捕获和冒泡,分别对应相应的处理函数,监听到捕获事件,先暂缓执行,直到冒泡事件被捕获后再执行捕获之间。

21.说一下事件委托

简介:事件委托指的是,不在事件的发生地(直接dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素DOM的类型,来做出不同的响应。
举例:最经典的就是ul和li标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在li标签上直接添加,而是在ul父元素上添加。
好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制.

为了不影响阅读体验,只分享部分面试题,更多面试题及答案可以【点击我】阅读下载哦~无偿分享给大家,算是一个感恩回馈吧

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):