目录
3、两个数组 var A = [1, 5, 6]; var B = [2, 6, 7],实现一个方
4、 你了解构造函数吗 ? class 是什么 ? 两者有什么区别 ?
5、for (var i = 0; i < 5; i++) { setTimeout(function() {
console.log(i); }, 1000); } 要求:输出0,1,2,3,4
7、 JS 的数据类型有哪些 ? 如何判断数据类型 ?他们的优缺点是什么?
1、 常见的兼容问题有哪些 ?
1.1 获取标签节点:
document.getElementsByClassName('
类名
')
在低版本
ie
中不兼容。解决方法是使用其他方式获取:
document.getElementById('id名')
document.getElementsByTagName('标签名')
document.getElementsByName('name属性值')
document.querySelector('css选择器')
document.querySelectorAll('css选择器')
1.2 获取卷去的高度:
// 当有文档声明的时候document.documentElement.scrollTopdocument.documentElement.srollLeft// 没有文档声明的时候document.body.scrollTopdocument.body.scrollLeft
解决办法使用兼容写法:
// 获取
var t = document.documentElement.scrollTop || document.body.scrollTop
var l = document.documentElement.srollLeft || document.body.scrollLeft
// 设置
document.documentElement.scrollTop = document.body.scrollTop = 数值
document.documentElement.srollLeft = document.body.scrollLeft = 数值
1.3 获取样式 :
// W3C 标准浏览器window.getComputedStyle( 元素 )// 低版本 IE 中元素 .currentStyle
使用函数封装的方式兼容:
function getStyle(ele,attr){if (window.getComputedStyle){return getComputedStyle(ele)[attr]}else{return ele.currentStyle[attr]}}
1.4 事件侦听器:
// W3C 浏览器ele.addEventListener( 事件类型 , 函数 )// 低版本 Ieele.attachEvent( 'on 事件类型 ' , 函数 )
使用函数封装的方式解决:
function bindEvent(ele,type,handler){
if(ele.addEventListener){
ele.addEventListener(type,handler)
}else if(ele.attachEvent){
ele.attachEvent('on'+type,handler)
}else{
ele['on'+type] = handler
}
}
1.5 事件解绑 :
// W3C 浏览器ele.removeEventListener( 事件类型 , 函数 )// 低版本 Ieele.detachEvent( 'on 事件类型 ' , 函数 )
使用函数封装的方式解决:
function unBind(ele,type,handler){
if(ele.removeEventListener){
ele.removeEventListener(type,handler)
}else if(ele.detachEvent){
ele.detachEvent('on'+type,handler)
}else{
ele['on'+type] = null
}
}
1.6 事件对象的获取 :
// W3C 浏览器元素 .on 事件类型 = function (e){}元素 .addEventListener( 事件类型 ,fn)function fn(e){}// 在低版本 IE 中元素 .on 事件类型 = function (){ window.event }元素 .addEventListener( 事件类型 ,fn)function fn(){window.event}
使用短路运算符解决:
元素.on事件类型 = function(e){
var e = e || window.event
}
元素.addEventListener(事件类型,fn)
function fn(e){
var e = e || window.event
}
1.7 阻止默认行为 :
// W3C 浏览器元素 .on 事件类型 = function (e){e.preventDefault()}// 在低版本 IE 中元素 .on 事件类型 = function (){ window.event.returnValue = false }
通过封装函数解决:
元素.on事件类型 = function(e){
var e = e || window.event
e.preventDefault?e.preventDefault():e.returnValue=false
}
1.8 阻止事件冒泡:
// W3C 浏览器元素 .on 事件类型 = function (e){e.stopPropagation()}// 在低版本 IE 中元素 .on 事件类型 = function (){ window.event.cancelBubble = true }
通过函数封装解决:
元素.on事件类型 = function(e){
var e = e || window.event
e.stopPropagation?e.stopPropagation():e.cancelBubble=true
}
1.9 获取精准的目标元素:
// W3C 浏览器元素 .on 事件类型 = function (e){e.target}// 在低版本 IE 中元素 .on 事件类型 = function (){ window.event.srcElement }
通过短路运算符解决:
元素.on事件类型 = function(e){
var e = e || window.event
var target = e.target || e.srcElement;
}
1.10 获取键盘码:
// W3C 浏览器元素 .on 事件类型 = function (e){e.keyCode}// 在低版本火狐中元素 .on 事件类型 = function (e){e.which}
通过短路运算符解决:
元素.on事件类型 = function(e){
var e = e || window.event
var keycode = e.keyCode || e.which;
}
2、 在 JS 中如何阻止事件冒泡 ?
使用事件对象阻止事件冒泡,以前的
w3c
浏览器中,使用事件对象的方法阻止:
事件对象 .stopPropagation()
在ie低版本浏览器中,使用事件对象的属性阻止:
现在的 w3c 浏览器也支持 ie 低版本浏览器中的写法,所以以前在阻止事件冒泡的时候,需要考虑兼容写法,现在就不需要了,直接用ie 低版本浏览器中的写法即可
3、两个数组 var A = [1, 5, 6]; var B = [2, 6, 7],实现一个方
法,找出仅存在于A 或者 仅 存在于B中的所有数字。
function getDiff(arr, brr){
// 仅存在于arr中的内容
var onlyArr = arr.filter(item => !brr.some(v => item === v))
// 仅存在于brr中的内容
var onlyBrr = brr.filter(v => !arr.some(item => v === item))
// 需要哪个就返回哪个,或者一起返回
return {
"仅存在于arr中的内容": onlyArr,
"仅存在于brr中的内容": onlyBrr
}
}
4、 你了解构造函数吗 ? class 是什么 ? 两者有什么区别 ?
在 es5 中构造函数其实就是在定义一个类,可以实例化对象, es6 中 class 其实是构造函数的语法糖。但还是有点区别的:
在
class
内部和
class
的方法内部,默认使用
严格模式
class
类不存在预解析,也就是
不能先调用class生成实例
,再定义
class
类,但是
构造函数可以。
class
中定义的方法默认不能被枚举,也就是
不能被遍历
。
class
必须
使用
new
执行,但是构造函数没有
new
也可以执行。
class
中的
所有方法都没有原型
,也就不能被
new
class
中继承
可以继承静态方法
,但是构造函数的继承不能。
5、for (var i = 0; i < 5; i++) { setTimeout(function() {
console.log(i); }, 1000); } 要求:输出0,1,2,3,4
首先这个面试题考察的是对于 js 中异步代码以及作用域的理解:js 中常见的异步代码包括定时器和 ajax 。 js 执行代码的流程是碰到同步代码就执行,碰到异步就交给浏览器的webAPI处理,当 webAPI 中的异步该执行时, webAPI 会将需要执行的回调函数放在任务队列中,等候执行,所以, js中所有的异步代码总会在所有同步代码执行结束后,再执行任务队列中的代码。在这个问题中,循环是同步代码,定时器是异步代码,所以整个循环都执行结束以后才会执行定时器代码。for 循环中使用 var 定义的变量是全局变量,定时器回调函数中输出变量的时候,根据作用域规则,先在当前作用域中变量i 的定义表达式,如果没有找到,就去上一级作用域中找,此时,在局部作用域中没有找到,去上级作用域中,也就是全局找到了,全局中的i ,因为循环已经执行结束了,所以 i 的值是 5 。最终,会输出 5 个 5 。其次考察的是对于类似问题的解决方式,间接性判断你是否有过类似情况的开发:这个问题的解决思路就是让回调函数中输出 i 的时候,不要去全局中找 i ,因为全局的 i 在循环执行结束后已经变成 5了,根据这个思路,有2 种解决办法:
1.在异步代码外面嵌套一层函数作用域:
for(var i = 0;i < 5; i++){
(function(i) {
setTimeout(function() {
console.log(i)
}, 1000)
})(i)
}
原理是自调用函数会产生作用域,循环
5
次就会产生
5
个作用域,每个作用域代码在执行的时候都有形参
i传递。所以每个作用域中的i
都是不同的,分别是:
0 1 2 3 4
。当作用域中的异步代码执行的时候,自己作用域中没有i
变量的定义,然后上级作用域就是自调用函数的作用域,找到了单独的
i
。最终可以输出:
0 1 2 3 4
2. 将循环代码中的var换成es6的let
for(let i = 0;i < 5; i++){
setTimeout(function() {
console.log(i)
}, 1000)
}
es6
的
let
自带块级作用域,原理跟第一种解决思路是一样的,转成
es5
后,代码是一样的。
6、常见的 HTTP 请求有哪些 ? 他们的区别是什么 ?
常见的有5种,分别是GET、HEAD, POST、PUT、 DELETEGET :它是最常见的方法,用于获取资源,常用于向服务器查询某些信息。打开网页一般都是用GET方法,因为要从 Web 服务器获取信息HEAD :类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头。POST :向指定资源提交数据进行处理请求(例如提交表单或者上传文件), 数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或对已有资源的修改。PUT :从客户端向服务器传送的数据取代指定文档的内容。DELETE :请求服务器删除指定的页面。最常见的HTTP请求方法是GET 和 POST。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。GET和POST的区别:GET 提交的数据会放在 ? 之后,以问号(?)分割 URL 和传输数据,参数之间以&相连GET 提交的数据大小有限制(因为浏览器对 URL 的长度有限制), 而 POST 方法提交的数据大小没有限制。GET 方式提交数据会带来安全问题,比如一个登录页面通过 GET 方式提交数据时,用户名和密码将出现在URL 上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码
7、 JS 的数据类型有哪些 ? 如何判断数据类型 ?他们的优缺点是什么?
typeof 用来检测数据类型的运算符
检测的不管是数组还是正则都返回的是 "object", 所以 typeof 不能判断一个值是否为数组
instanceof/constructor。检测某一个实例是否属于某一个类使用instanceof/constructor可以检测数组和正则
用 instanceof 检测的时候 , 只要当前的这个类在实例的原型链上 ( 可以通过 原型链__proto__ 找到它 ), 检测出来的结果都是true 。基本数据类型 的值是不能用 instanceof 来检测的在 类的原型继承 中 ,instanceof 检测出来的结果其实是 不准确的
Object.prototype.toString.call(value) ->找到Object原型上的toString方法,让方法执行,并且让方法中的this变为value(value->就是我们要检测数据类型的值)。检测的类型比较多,也比较精准。
8、数组常用方法有那些?
数组的常用方法 这样的面试题 算是非常基础的面试题 面试官的目的 也不会只是单纯的让你背诵出数组的所有方法
这里的关键点 是 常用 这两个字 面试官的 目的是 通过 这个问题 看你平时在项目中 对于 数组函数的应用和理解 然后判断出 你平时在项目中对于数组的应用 然后推测出你真实的技术水平
这里建议的回答方式是 通过一个 自己用的最多的数组函数方法 深入展开的说一说 在 实际项目中的应用
例如谈到 数组单元删除 数组
,splice()
除了要说 函数的用法之外 还要谈到 具体的项目中 删除数组单元之后 数组坍塌的影响 以及如何处理
concat() 连接两个或更多的数组,并返回结果。join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。pop() 删除并返回数组的最后一个元素。shift() 删除并返回数组的第一个元素push() 向数组的末尾添加一个或更多元素,并返回新的长度。unshift() 向数组的开头添加一个或更多元素,并返回新的长度。reverse() 颠倒数组中元素的顺序。slice() 从某个已有的数组返回选定的元素sort() 对数组的元素进行排序splice() 删除元素,并向数组添加新元素。toSource() 返回该对象的源代码。toString() 把数组转换为字符串,并返回结果。toLocaleString() 把数组转换为本地数组,并返回结果。valueOf() 返回数组对象的原始值
9、JavaScript如何存储cookie
基本语法是 document.cookie = '键名=键值;expires=时间对象;path=路径' ;时效 如果不设定 默认是 seeion 会话时效路径 如果不设定 默认是 当前文件所在文件夹设定时效 要 设定一个时间对象 时间对象的时间戳 就是 时效期要注意计算 当前时区 和 世界标砖时间的时间差路径一般设定为根目录 也就是 '/'
10、函数的柯里化
所谓的柯里化函数 指的是 把
接受多个参数的函数
变换成
接受一个单一参数的函数
并且返回接受余下的
参数而且返回结果的新函数
// 普通的add函数
function add(x, y) {
return x + y
}
// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
优点:
1 , 参数复用例如 一个函数 有两个参数 但是第一个参数会被反复使用 每次都需要输入 一个重复的参数使用柯里化函数之后 只需要 输入一个参数就可以了2 , 提前确认提前定义好一个参数 也就 决定了整个函数程序的执行方向 避免每次都执行判断比较等
缺点:
只能提前定义一个参数 如果想要提前定义多个参数 这样的语法是不支持
柯里化函数执行效能上的问题:
存取 arguments 对象通常要比存取命名参数要慢一点一些老版本的浏览器在 arguments.length 的实现上是相当慢的使用 函数 .apply() 和 函数 .call() 通常比直接调用 fn() 稍微慢点创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上