目录
1、 常见的兼容问题有哪些 ?
1.1 获取标签节点:
1.2 获取卷去的高度:
1.3 获取样式 :
1.4 事件侦听器:
1.5 事件解绑 :
1.6 事件对象的获取 :
1.7 阻止默认行为 :
1.8 阻止事件冒泡:
1.9 获取精准的目标元素:
1.10 获取键盘码:
2、 在 JS 中如何阻止事件冒泡 ?
3、两个数组 var A = [1, 5, 6]; var B = [2, 6, 7],实现一个方
法,找出仅存在于A 或者 仅 存在于B中的所有数字。
4、 你了解构造函数吗 ? class 是什么 ? 两者有什么区别 ?
5、for (var i = 0; i < 5; i++) { setTimeout(function() {
console.log(i); }, 1000); } 要求:输出0,1,2,3,4
6、常见的 HTTP 请求有哪些 ? 他们的区别是什么 ?
7、 JS 的数据类型有哪些 ? 如何判断数据类型 ?他们的优缺点是什么?
8、数组常用方法有那些?
10、函数的柯里化
document.getElementById('id名')
document.getElementsByTagName('标签名')
document.getElementsByName('name属性值')
document.querySelector('css选择器')
document.querySelectorAll('css选择器') // 当有文档声明的时候 document.documentElement.scrollTop document.documentElement.srollLeft // 没有文档声明的时候 document.body.scrollTop document.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 = 数值
// W3C标准浏览器 window.getComputedStyle(元素) // 低版本IE中 元素.currentStyle
使用函数封装的方式兼容:
function getStyle(ele,attr){ if(window.getComputedStyle){ return getComputedStyle(ele)[attr] }else{ return ele.currentStyle[attr] } }
// W3C浏览器 ele.addEventListener(事件类型,函数) // 低版本Ie ele.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}
}
// W3C浏览器 ele.removeEventListener(事件类型,函数) // 低版本Ie ele.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}}
// 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
}
// W3C浏览器 元素.on事件类型 = function(e){ e.preventDefault() } // 在低版本IE中 元素.on事件类型 = function(){ window.event.returnValue = false }
通过封装函数解决:
元素.on事件类型 = function(e){var e = e || window.evente.preventDefault?e.preventDefault():e.returnValue=false
}
// W3C浏览器 元素.on事件类型 = function(e){ e.stopPropagation() } // 在低版本IE中 元素.on事件类型 = function(){ window.event.cancelBubble = true }
通过函数封装解决:
元素.on事件类型 = function(e){var e = e || window.evente.stopPropagation?e.stopPropagation():e.cancelBubble=true
}
// W3C浏览器 元素.on事件类型 = function(e){ e.target } // 在低版本IE中 元素.on事件类型 = function(){ window.event.srcElement }
通过短路运算符解决:
元素.on事件类型 = function(e){var e = e || window.eventvar target = e.target || e.srcElement;
}
// W3C浏览器 元素.on事件类型 = function(e){ e.keyCode } // 在低版本火狐中 元素.on事件类型 = function(e){ e.which }
通过短路运算符解决:
元素.on事件类型 = function(e){var e = e || window.eventvar keycode = e.keyCode || e.which;
}
事件对象.stopPropagation()
在ie低版本浏览器中,使用事件对象的属性阻止:
现在的w3c浏览器也支持ie低版本浏览器中的写法,所以以前在阻止事件冒泡的时候,需要考虑兼容写法,现在就不需要了,直接用ie低版本浏览器中的写法即可
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}
}
在es5中构造函数其实就是在定义一个类,可以实例化对象,es6中class其实是构造函数的语法糖。但还是有点区别的:在class内部和class的方法内部,默认使用严格模式 class类不存在预解析,也就是不能先调用class生成实例,再定义class类,但是构造函数可以。 class中定义的方法默认不能被枚举,也就是不能被遍历。 class必须使用new执行,但是构造函数没有new也可以执行。 class中的所有方法都没有原型,也就不能被new class中继承可以继承静态方法,但是构造函数的继承不能。
首先这个面试题考察的是对于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后,代码是一样的。
常见的有5种,分别是GET、HEAD, POST、PUT、 DELETE GET:它是最常见的方法,用于获取资源,常用于向服务器查询某些信息。打开网页一般都是用GET方法,因为要从 Web 服务器获取信息 HEAD:类似于 GET请求,只不过返回的响应中没有具体的内容,用于获取报头。 POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件), 数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或对已有资源的修改。 PUT:从客户端向服务器传送的数据取代指定文档的内容。 DELETE:请求服务器删除指定的页面。 最常见的HTTP请求方法是GET 和 POST。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。GET和POST的区别: GET提交的数据会放在?之后,以问号(?)分割URL 和传输数据,参数之间以&相连 GET提交的数据大小有限制(因为浏览器对URL的长度有限制), 而POST 方法提交的数据大小没有限制。 GET方式提交数据会带来安全问题,比如一个登录页面通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码
检测的不管是数组还是正则都返回的是"object",所以typeof不能判断一个值是否为数组
instanceof/constructor。检测某一个实例是否属于某一个类使用instanceof/constructor可以检测数组和正则
用instanceof检测的时候,只要当前的这个类在实例的原型链上(可以通过原型链__proto__找到它),检测出来的结果都是true。 基本数据类型的值是不能用instanceof来检测的 在类的原型继承中,instanceof检测出来的结果其实是不准确的
Object.prototype.toString.call(value) ->找到Object原型上的toString方法,让方法执行,并且让方法中的this变为value(value->就是我们要检测数据类型的值)。检测的类型比较多,也比较精准。
concat() 连接两个或更多的数组,并返回结果。 join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。 pop() 删除并返回数组的最后一个元素。 shift() 删除并返回数组的第一个元素 push() 向数组的末尾添加一个或更多元素,并返回新的长度。 unshift() 向数组的开头添加一个或更多元素,并返回新的长度。 reverse() 颠倒数组中元素的顺序。 slice() 从某个已有的数组返回选定的元素 sort() 对数组的元素进行排序 splice() 删除元素,并向数组添加新元素。 toSource() 返回该对象的源代码。 toString() 把数组转换为字符串,并返回结果。 toLocaleString() 把数组转换为本地数组,并返回结果。 valueOf() 返回数组对象的原始值
基本语法是 document.cookie = '键名=键值;expires=时间对象;path=路径' ; 时效 如果不设定 默认是 seeion 会话时效 路径 如果不设定 默认是 当前文件所在文件夹 设定时效 要 设定一个时间对象 时间对象的时间戳 就是 时效期 要注意计算 当前时区 和 世界标砖时间的时间差 路径一般设定为根目录 也就是 '/'
// 普通的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() 稍微慢点 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上