2019年秋招
本人亲身经历的面试,已通过,面试时长30分钟,岗位:web前端开发工程师
以下为具体的面试题:
自我介绍
问:前端从什么时候开始学的?
问:现在还有在实习吗?
问:前端的学习方式?
问:看了哪些前端的书籍了?
JavaScript判断数组的方式
arr instanceof Array
arr.constructor == Array
arr.__proto__ == Array.prototype
Object.prototype.toString.call(arr) == '[object Array]'
Array.isArray(arr)
- 补充不能用
typeof arr
来判断,因为该方式对数组和对象返回都是 object
深拷贝对象的方法
JSON.parse(JSON.stringify(obj))
- 递归拷贝
function deepClone(obj) {
let cloneObj = {}; //在堆内存中新建一个对象
for(let key in obj){ //遍历参数的键
if(typeof obj[key] ==='object'){
cloneObj[key] = deepClone(obj[key]) //值是对象就再次调用函数
}else{
cloneObj[key] = obj[key] //基本类型直接复制值
}
}
return cloneObj
}
如果对象/数组只有一个层级的话
如果对象/数组的元素是基本类型的话,则可以实现深拷贝,如果是引用类型的话,则不会实现深拷贝,改变引用类型中的值,拷贝与被拷贝对象都改变。
Object.assign(newobj,obj)
let cloneObj = { ...obj };
let cloneArr = oldArr.slice(0)
补充:
let target = {};
let source = { a: { b: 2 } };
Object.assign(target, source);
console.log(target); // { a: { b: 10 } };
source.a.b = 10;
console.log(source); // { a: { b: 10 } };
console.log(target); // { a: { b: 10 } };
注意,次打印的结果target竟然是操作后的值
因为target中含有引用元素,则target和sourse还有联系
而console.log是从内存中查值,改变sourse后内存中两者共有的这个对象元素{b:2}都改变了
通俗地讲:
控制台一开始显示的是{a: {...}}
展开的这个过程是从内存查值的过程,不是相当于照相(固定一瞬间的状态)
定义对象不可修改的属性
Object.defineProperty()
Object.defineProperty(对象名,属性名,描述符)
描述符
{
writable: false, // 不可读写
configurable // 不可配置
}
// 举例
Object.defineProperty(objName, "name", {writable: false, value: "zhangSan"});
- 单例模式
// 匿名函数+立即执行+返回对象+闭包
var obj = function(){
// 定义私有属性和方法
return {
// 共有属性和方法
}
}()
-
Object.freeze(obj)
使用该方法后,不可给对象添加和删除属性,也不可对现有属性进行更改。
但是,可以完全重写该对象,如obj = newObj
出题意图:
假设一个场景, 我们写了一个 JS, 在其中定义了一个对象, 会开放出来给第三方使用。 如果想让这个对象安全的被第三方使用, 需要避免这个对象被下钩子(hook), 也就是要避免这个对象被覆盖重写。如:给对象的一个属性重新赋值为一个带有恶意代码的函数。
闭包是什么
一个有权力访问另一个函数内部变量和方法的函数。
太基础了,懒得展开了。
给滚动条添加滚动事件,1秒触发一次,避免频繁操作
考察节流和防抖
防抖(debounce)
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
function debounce(func, wait) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args)
}, wait);
}
}
节流(throttle)
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
补充:
其实还分节流和防抖还分立即执行和非立即执行,节流还分时间戳版和定时器版
详细内容见:函数的节流和防抖
如何给ul下的li标签绑定事件
用事件委托,给外层的ul绑定事件,根据事件冒泡的原理内层的li也被绑定,好处是如果再多了几个li标签,就不用重复绑定了,而且比遍历绑定更省资源
还可以通过e.target.nodeName
来指定某个标签才响应该事件
window.onload = function(){
var oUl = document.getElementById("ul1");
oUl.onclick = function(ev){
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == 'li'){
alert(123);
alert(target.innerHTML);
}
}
}
用token来防范csrf攻击要如何操作
我的回答:
因为csrf攻击无法获取表单中的内容,将token放在post请求的表单里,服务端会根据表单里的token来验证用户是否是真实用户。
权威资料:
CSRF(Cross-site request forgery)跨站请求伪造
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
防范策略:
-
同源检测。
根据请求头的Origin和Referer判断是否是信任的域名发起的请求,若不是则阻止,若无这两个个请求头参数,则直接阻止
-
CSRF Token
原理:让请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。
具体操作:用户登录时根据用户名和密码等加密生成一个Token值,同时写入到浏览器的Cookie和Hidden中,然后根据每次的请求将Token和Cookie同时带入到服务端进行验证和判断。
前端实时更新
- 用meta标签refresh,设置前端多长时间再向服务端更新
setInternal(func, 1000)
- 在ajax成功请求后用
setTimeout()
递归调用ajax请求 - web Socket
浏览器控制台的netWork分析加载缓慢的原因
一时没反应过来就是平时经常查看请求头和响应头的东西,直接说没有了解过【哭了】
有没有对网站的性能分析过,比如performance工具
window.performance.timing保存着各种时间
补充说了前端性能方面:白屏时间、首屏时间
更多详细内容可以看本人的另一篇博客:前端性能监控方案(首屏、白屏时间等)
Vue中的computed和watch的区别和应用场景
计算属性computed
当其依赖的变量变化,计算属性也会随之变化,默认定义的是getter方法,还可以设置setter方法,比方法method更节省性能
监听器watch
若自身变化,则会调用回调函数
Vue兄弟组件间通信的方式
基础的东西,详见Vue官方网站
有没有使用过Vue-router
额,这个记不太清了,就说没有了
冒泡排序是怎么排序的
元素两两比较,每次遍历都找出大或小的元素
function bubbleSort(arr) {
var len = arr.length;
for (var i = ; i < len - 1; i++) {
for (var j = ; j < len - 1 - i; j++) {
if (arr[j] > arr[j+1]) { // 相邻元素两两对比
var temp = arr[j+1]; // 元素交换
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
快速排序
先用个元素当中间值,比它小的元素都放前面,比它大的元素都放后面,则一次遍历后把数组分为了两组,对每组用相同的方法继续划分直到排序完成。
function quickSort(arr, left, right) {
var len = arr.length,
partitionIndex,
left = typeof left != 'number' ? : left,
right = typeof right != 'number' ? len - 1 : right;
if (left < right) {
partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex-1);
quickSort(arr, partitionIndex+1, right);
}
return arr;
}
function partition(arr, left ,right) { // 分区操作
var pivot = left, // 设定基准值(pivot)
index = pivot + 1;
for (var i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
swap(arr, pivot, index - 1);
return index-1;
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function partition2(arr, low, high) {
let pivot = arr[low];
while (low < high) {
while (low < high && arr[high] > pivot) {
--high;
}
arr[low] = arr[high];
while (low < high && arr[low] <= pivot) {
++low;
}
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
function quickSort2(arr, low, high) {
if (low < high) {
let pivot = partition2(arr, low, high);
quickSort2(arr, low, pivot - 1);
quickSort2(arr, pivot + 1, high);
}
return arr;
}
一个300宽的div包含2个200宽的div,如何保持一个不变,另一个变为100宽
用flex布局,第二个div设置flex: 1
即可
作者:K_ON
链接:https://juejin.im/post/5df9cca6e51d455829730b58
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。