关于前端:20道前端高频面试题附答案

53次阅读

共计 8395 个字符,预计需要花费 21 分钟才能阅读完成。

代码输入后果

function runAsync (x) {const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log('result:', res))
  .catch(err => console.log(err))

输入后果如下:

1
'result:' 1
2
3

then 只会捕捉第一个胜利的办法,其余的函数尽管还会继续执行,然而不是被 then 捕捉了。

数组去重

实现代码如下:

function uniqueArr(arr) {return [...new Set(arr)];
}

正向代理和反向代理的区别

  • 正向代理:

客户端想取得一个服务器的数据,然而因为种种原因无奈间接获取。于是客户端设置了一个代理服务器,并且指定指标服务器,之后代理服务器向指标服务器转交申请并将取得的内容发送给客户端。这样实质上起到了对实在服务器暗藏实在客户端的目标。实现正向代理须要批改客户端,比方批改浏览器配置。

  • 反向代理:

服务器为了可能将工作负载分不到多个服务器来进步网站性能 (负载平衡)等目标,当其受到申请后,会首先依据转发规定来确定申请应该被转发到哪个服务器上,而后将申请转发到对应的实在服务器上。这样实质上起到了对客户端暗藏实在服务器的作用。
个别应用反向代理后,须要通过批改 DNS 让域名解析到代理服务器 IP,这时浏览器无奈察觉到真正服务器的存在,当然也就不须要批改配置了。

正向代理和反向代理的构造是一样的,都是 client-proxy-server 的构造,它们次要的区别就在于两头这个 proxy 是哪一方设置的。在正向代理中,proxy 是 client 设置的,用来暗藏 client;而在反向代理中,proxy 是 server 设置的,用来暗藏 server。

写代码:实现函数可能深度克隆根本类型

浅克隆:

function shallowClone(obj) {let cloneObj = {};

  for (let i in obj) {cloneObj[i] = obj[i];
  }

  return cloneObj;
}

深克隆:

  • 思考根底类型
  • 援用类型

    • RegExp、Date、函数 不是 JSON 平安的
    • 会失落 constructor,所有的构造函数都指向 Object
    • 破解循环援用
function deepCopy(obj) {if (typeof obj === 'object') {var result = obj.constructor === Array ? [] : {};

    for (var i in obj) {result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
    }
  } else {var result = obj;}

  return result;
}

代码输入后果

function Dog() {this.name = 'puppy'}
Dog.prototype.bark = () => {console.log('woof!woof!')
}
const dog = new Dog()
console.log(Dog.prototype.constructor === Dog && dog.constructor === Dog && dog instanceof Dog)

输入后果:true

解析: 因为 constructor 是 prototype 上的属性,所以 dog.constructor 实际上就是指向 Dog.prototype.constructor;constructor 属性指向构造函数。instanceof 而理论检测的是类型是否在实例的原型链上。

constructor 是 prototype 上的属性,这一点很容易被疏忽掉。constructor 和 instanceof 的作用是不同的,理性地来说,constructor 的限度比拟严格,它只能严格比照对象的构造函数是不是指定的值;而 instanceof 比拟涣散,只有检测的类型在原型链上,就会返回 true。

OPTIONS 申请办法及应用场景

OPTIONS 是除了 GET 和 POST 之外的其中一种 HTTP 申请办法。

OPTIONS 办法是用于申请取得由 Request-URI 标识的资源在申请 / 响应的通信过程中能够应用的性能选项。通过这个办法,客户端能够 在采取具体资源申请之前,决定对该资源采取何种必要措施,或者理解服务器的性能。该申请办法的响应不能缓存。

OPTIONS 申请办法的 主要用途 有两个:

  • 获取服务器反对的所有 HTTP 申请办法;
  • 用来查看拜访权限。例如:在进行 CORS 跨域资源共享时,对于简单申请,就是应用 OPTIONS 办法发送嗅探申请,以判断是否有对指定资源的拜访权限。

代码输入后果

function runAsync (x) {const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
    return p
}

Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))

输入后果如下:

1
2
3
[1, 2, 3]

首先,定义了一个 Promise,来异步执行函数 runAsync,该函数传入一个值 x,而后距离一秒后打印出这个 x。

之后再应用 Promise.all 来执行这个函数,执行的时候,看到一秒之后输入了 1,2,3,同时输入了数组[1, 2, 3],三个函数是同步执行的,并且在一个回调函数中返回了所有的后果。并且后果和函数的执行程序是统一的。

v-model 语法糖是怎么实现的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- v-model 只是语法糖而已 -->
    <!-- v-model 在外部为不同的输出元素应用不同的 property 并抛出不同的事件 -->
    <!-- text 和 textarea 元素应用 value property 和 input 事件 -->
    <!-- checkbox 和 radio 应用 checked  property 和 change 事件 -->
    <!-- select 字段将 value 作为 prop 并将 change 作为事件 -->
    <!-- 留神:对于须要应用输入法 (如中文、日文、韩文等) 的语言,你将会发现 v -model 不会再输入法    组合文字过程中失去更新 -->
    <!-- 再一般标签上 -->
    <input v-model="sth" />  // 这一行等于下一行
    <input v-bind:value="sth" v-on:input="sth = $event.target.value" />
    <!-- 再组件上 -->
    <currency-input v-model="price"></currentcy-input>
        <!-- 上行代码是上行的语法糖         <currency-input :value="price" @input="price = arguments[0]"></currency-input>        --> 
        <!-- 子组件定义 -->
        Vue.component('currency-input', {
         template: `
          <span>
           <input
            ref="input"
            :value="value"
            @input="$emit('input', $event.target.value)"
           >
          </span>
         `,
         props: ['value'],
        })   
</body>
</html>

事件委托的应用场景

场景:给页面的所有的 a 标签增加 click 事件,代码如下:

document.addEventListener("click", function(e) {if (e.target.nodeName == "A")
        console.log("a");
}, false);

然而这些 a 标签可能蕴含一些像 span、img 等元素,如果点击到了这些 a 标签中的元素,就不会触发 click 事件,因为事件绑定上在 a 标签元素上,而触发这些外部的元素时,e.target 指向的是触发 click 事件的元素(span、img 等其余元素)。

这种状况下就能够应用事件委托来解决,将事件绑定在 a 标签的外部元素上,当点击它的时候,就会逐级向上查找,晓得找到 a 标签为止,代码如下:

document.addEventListener("click", function(e) {
    var node = e.target;
    while (node.parentNode.nodeName != "BODY") {if (node.nodeName == "A") {console.log("a");
            break;
        }
        node = node.parentNode;
    }
}, false);

二分查找 – 工夫复杂度 log2(n)

题目形容: 如何确定一个数在一个有序数组中的地位

实现代码如下:

function search(arr, target, start, end) {
  let targetIndex = -1;

  let mid = Math.floor((start + end) / 2);

  if (arr[mid] === target) {
    targetIndex = mid;
    return targetIndex;
  }

  if (start >= end) {return targetIndex;}

  if (arr[mid] < target) {return search(arr, target, mid + 1, end);
  } else {return search(arr, target, start, mid - 1);
  }
}
// const dataArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// const position = search(dataArr, 6, 0, dataArr.length - 1);
// if (position !== -1) {//   console.log(` 指标元素在数组中的地位:${position}`);
// } else {//   console.log("指标元素不在数组中");
// }

说一下 SPA 单页面有什么优缺点?

长处:1. 体验好,不刷新,缩小 申请  数据 ajax 异步获取 页面流程;2. 前后端拆散

3. 加重服务端压力

4. 共用一套后端程序代码,适配多端

毛病:1. 首屏加载过慢;2.SEO 不利于搜索引擎抓取

对 HTML 语义化的了解

语义化是指依据内容的结构化(内容语义化),抉择适合的标签(代码语义化)。艰深来讲就是用正确的标签做正确的事件。

语义化的长处如下:

  • 对机器敌对,带有语义的文字表现力丰盛,更适宜搜索引擎的爬虫爬取无效信息,有利于 SEO。除此之外,语义类还反对读屏软件,依据文章能够主动生成目录;
  • 对开发者敌对,应用语义类标签加强了可读性,构造更加清晰,开发者能清晰的看出网页的构造,便于团队的开发与保护。

常见的语义化标签:

<header></header>  头部

<nav></nav>  导航栏

<section></section>  区块(有语义化的 div)<main></main>  次要区域

<article></article>  次要内容

<aside></aside>  侧边栏

<footer></footer>  底部

什么是执行栈

能够把执行栈认为是一个存储函数调用的 栈构造,遵循先进后出的准则。当开始执行 JS 代码时,依据先进后出的准则,后执行的函数会先弹出栈,能够看到,foo 函数后执行,当执行结束后就从栈中弹出了。

平时在开发中,能够在报错中找到执行栈的痕迹:

function foo() {throw new Error('error')
}
function bar() {foo()
}
bar()

能够看到报错在 foo 函数,foo 函数又是在 bar 函数中调用的。当应用递归时,因为栈可寄存的函数是有 限度 的,一旦寄存了过多的函数且没有失去开释的话,就会呈现爆栈的问题

function bar() {  bar()}bar()

Object.is 实现

题目形容:

Object.is 不会转换被比拟的两个值的类型,这点和 === 更为类似,他们之间也存在一些区别。1. NaN 在 === 中是不相等的,而在 Object.is 中是相等的
    2. + 0 和 - 0 在 === 中是相等的,而在 Object.is 中是不相等的

实现代码如下:

Object.is = function (x, y) {if (x === y) {
    // 当前情况下,只有一种状况是非凡的,即 +0 -0
    // 如果 x !== 0,则返回 true
    // 如果 x === 0,则须要判断 + 0 和 -0,则能够间接应用 1/+0 === Infinity 和 1/-0 === -Infinity 来进行判断
    return x !== 0 || 1 / x === 1 / y;
  }

  // x !== y 的状况下,只须要判断是否为 NaN,如果 x!==x,则阐明 x 是 NaN,同理 y 也一样
  // x 和 y 同时为 NaN 时,返回 true
  return x !== x && y !== y;
};

CDN 的作用

CDN 个别会用来托管 Web 资源(包含文本、图片和脚本等),可供下载的资源(媒体文件、软件、文档等),应用程序(门户网站等)。应用 CDN 来减速这些资源的拜访。

(1)在性能方面,引入 CDN 的作用在于:

  • 用户收到的内容来自最近的数据中心,提早更低,内容加载更快
  • 局部资源申请调配给了 CDN,缩小了服务器的负载

(2)在平安方面,CDN 有助于进攻 DDoS、MITM 等网络攻击:

  • 针对 DDoS:通过监控剖析异样流量,限度其申请频率
  • 针对 MITM:从源服务器到 CDN 节点到 ISP(Internet Service Provider),全链路 HTTPS 通信

除此之外,CDN 作为一种根底的云服务,同样具备资源托管、按需扩大(可能应答流量顶峰)等方面的劣势。

10 个 Ajax 同时发动申请,全副返回展现后果,并且至少容许三次失败,说出设计思路

这个问题置信很多人会第一工夫想到 Promise.all,然而这个函数有一个局限在于如果失败一次就返回了,间接这样实现会有点问题,须要变通下。以下是两种实现思路

// 以下是不残缺代码,着重于思路 非 Promise 写法
let successCount = 0
let errorCount = 0
let datas = []
ajax(url, (res) => {if (success) {
         success++
         if (success + errorCount === 10) {console.log(datas)
         } else {datas.push(res.data)
         }
     } else {
         errorCount++
         if (errorCount > 3) {
            // 失败次数大于 3 次就应该报错了
             throw Error('失败三次')
         }
     }
})
// Promise 写法
let errorCount = 0
let p = new Promise((resolve, reject) => {if (success) {resolve(res.data)
     } else {
         errorCount++
         if (errorCount > 3) {
            // 失败次数大于 3 次就应该报错了
            reject(error)
         } else {resolve(error)
         }
     }
})
Promise.all([p]).then(v => {console.log(v);
});

实现 JSONP 跨域

JSONP 外围原理script 标签不受同源策略束缚,所以能够用来进行跨域申请,长处是兼容性好,然而只能用于 GET 申请;

实现

const jsonp = (url, params, callbackName) => {const generateUrl = () => {
        let dataSrc = "";
        for(let key in params) {if(params.hasOwnProperty(key)) {dataSrc += `${key}=${params[key]}&`
            }
        }
        dataSrc += `callback=${callbackName}`;
        return `${url}?${dataSrc}`;
    }
    return new Promise((resolve, reject) => {const scriptEle = document.createElement('script');
        scriptEle.src = generateUrl();
        document.body.appendChild(scriptEle);
        window[callbackName] = data => {resolve(data);
            document.removeChild(scriptEle);
        }
    });
}

AJAX

实现:利用 XMLHttpRequest

// get
const getJSON = (url) => {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();
        // open 办法用于指定 HTTP 申请的参数: method, url, async(是否异步,默认 true)xhr.open("GET", url, false);
        xhr.setRequestHeader('Content-Type', 'application/json');
        // onreadystatechange 属性指向一个监听函数。// readystatechange 事件产生时(实例的 readyState 属性变动),就会执行这个属性。xhr.onreadystatechange = function(){
            // 4 示意服务器返回的数据曾经齐全接管,或者本次接管曾经失败
            if(xhr.readyState !== 4) return;
            // 申请胜利,基本上只有 2xx 和 304 的状态码,示意服务器返回是失常状态
            if(xhr.status === 200 || xhr.status === 304) {
                // responseText 属性返回从服务器接管到的字符串
                resolve(xhr.responseText);
            }
            // 申请失败
            else {reject(new Error(xhr.responseText));
            }
        }
        xhr.send();});
}

// post
const postJSON = (url, data) => {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();
        xhr.open("POST", url);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function(){if(xhr.readyState !== 4) return;
            if(xhr.status === 200 || xhr.status === 304) {resolve(xhr.responseText);
            }
            else {reject(new Error(xhr.responseText));
            }
        }
        xhr.send(data);
    });
}

用过 TypeScript 吗?它的作用是什么?

为 JS 增加类型反对,以及提供最新版的 ES 语法的反对,是的利于团队合作和排错,开发大型项目

事件循环机制(Event Loop)

事件循环机制从整体上通知了咱们 JavaScript 代码的执行程序 Event Loop即事件循环,是指浏览器或 Node 的一种解决 javaScript 单线程运行时不会阻塞的一种机制,也就是咱们常常应用 异步 的原理。

先执行 Script 脚本,而后清空微工作队列,而后开始下一轮事件循环,持续先执行宏工作,再清空微工作队列,如此往返。

  • 宏工作:Script/setTimeout/setInterval/setImmediate/ I/O / UI Rendering
  • 微工作:process.nextTick()/Promise

上诉的 setTimeout 和 setInterval 等都是工作源,真正进入工作队列的是他们散发的工作。

优先级

  • setTimeout = setInterval 一个队列
  • setTimeout > setImmediate
  • process.nextTick > Promise
for (const macroTask of macroTaskQueue) {handleMacroTask();    
  for (const microTask of microTaskQueue) {handleMicroTask(microTask);  
  }
}

正文完
 0