写点什么

美团前端面试题(附答案)

作者:hellocoder2029
  • 2022 年 9 月 12 日
    浙江
  • 本文字数:6300 字

    阅读完需:约 21 分钟

CDN 的原理

CDN 和 DNS 有着密不可分的联系,先来看一下 DNS 的解析域名过程,在浏览器输入的解析过程如下:(1) 检查浏览器缓存(2)检查操作系统缓存,常见的如 hosts 文件(3)检查路由器缓存(4)如果前几步都没没找到,会向 ISP(网络服务提供商)的 LDNS 服务器查询(5)如果 LDNS 服务器没找到,会向根域名服务器(Root Server)请求解析,分为以下几步:


  • 根服务器返回顶级域名(TLD)服务器如.com.cn.org等的地址,该例子中会返回.com的地址

  • 接着向顶级域名服务器发送请求,然后会返回次级域名(SLD)服务器的地址,本例子会返回.test的地址

  • 接着向次级域名服务器发送请求,然后会返回通过域名查询到的目标 IP,本例子会返回www.test.com的地址

  • Local DNS Server 会缓存结果,并返回给用户,缓存在系统中


CDN 的工作原理: (1)用户未使用 CDN 缓存资源的过程:


  1. 浏览器通过 DNS 对域名进行解析(就是上面的 DNS 解析过程),依次得到此域名对应的 IP 地址

  2. 浏览器根据得到的 IP 地址,向域名的服务主机发送数据请求

  3. 服务器向浏览器返回响应数据


(2)用户使用 CDN 缓存资源的过程:


  1. 对于点击的数据的 URL,经过本地 DNS 系统的解析,发现该 URL 对应的是一个 CDN 专用的 DNS 服务器,DNS 系统就会将域名解析权交给 CNAME 指向的 CDN 专用的 DNS 服务器。

  2. CND 专用 DNS 服务器将 CND 的全局负载均衡设备 IP 地址返回给用户

  3. 用户向 CDN 的全局负载均衡设备发起数据请求

  4. CDN 的全局负载均衡设备根据用户的 IP 地址,以及用户请求的内容 URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求

  5. 区域负载均衡设备选择一台合适的缓存服务器来提供服务,将该缓存服务器的 IP 地址返回给全局负载均衡设备

  6. 全局负载均衡设备把服务器的 IP 地址返回给用户

  7. 用户向该缓存服务器发起请求,缓存服务器响应用户的请求,将用户所需内容发送至用户终端。


如果缓存服务器没有用户想要的内容,那么缓存服务器就会向它的上一级缓存服务器请求内容,以此类推,直到获取到需要的资源。最后如果还是没有,就会回到自己的服务器去获取资源。


CNAME(意为:别名):在域名解析中,实际上解析出来的指定域名对应的 IP 地址,或者该域名的一个 CNAME,然后再根据这个 CNAME 来查找对应的 IP 地址。

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;};
复制代码

Promise.all 和 Promise.race 的区别的使用场景

(1)Promise.all Promise.all可以将多个Promise实例包装成一个新的 Promise 实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值


Promise.all 中传入的是数组,返回的也是是数组,并且会将进行映射,传入的 promise 对象返回的值是按照顺序在数组中排列的,但是注意的是他们执行的顺序并不是按照顺序的,除非可迭代对象为空。


需要注意,Promise.all 获得的成功结果的数组里面的数据顺序和 Promise.all 接收到的数组顺序是一致的,这样当遇到发送多个请求并根据请求顺序获取和使用数据的场景,就可以使用 Promise.all 来解决。


(2)Promise.race


顾名思义,Promse.race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。当要做一件事,超过多长时间就不做了,可以用这个方法来解决:


Promise.race([promise1,timeOutPromise(5000)]).then(res=>{})
复制代码

代码输出结果

var F = function() {};Object.prototype.a = function() {  console.log('a');};Function.prototype.b = function() {  console.log('b');}var f = new F();f.a();f.b();F.a();F.b()
复制代码


输出结果:


aUncaught TypeError: f.b is not a functionab
复制代码


解析:


  1. f 并不是 Function 的实例,因为它本来就不是构造函数,调用的是 Function 原型链上的相关属性和方法,只能访问到 Object 原型链。所以 f.a() 输出 a ,而 f.b() 就报错了。

  2. F 是个构造函数,而 F 是构造函数 Function 的一个实例。因为 F instanceof Object === true,F instanceof Function === true,由此可以得出结论:F 是 Object 和 Function 两个的实例,即 F 能访问到 a, 也能访问到 b。所以 F.a() 输出 a ,F.b() 输出 b。

数据类型判断

typeof 可以正确识别:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,但是对于其他的都会认为是 object,比如 Null、Date 等,所以通过 typeof 来判断数据类型会不准确。但是可以使用 Object.prototype.toString 实现。


function typeOf(obj) {-   let res = Object.prototype.toString.call(obj).split(' ')[1]-   res = res.substring(0, res.length - 1).toLowerCase()-   return res// 更好的写法+   return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()}typeOf([])        // 'array'typeOf({})        // 'object'typeOf(new Date)  // 'date'
复制代码

Vue 路由守卫有哪些,怎么设置,使用场景等

常用的两个路由守卫:router.beforeEach 和 router.afterEach
每个守卫方法接收三个参数:
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。
在项目中,一般在beforeEach这个钩子函数中进行路由跳转的一些信息判断。判断是否登录,是否拿到对应的路由权限等等。
复制代码

为什么需要浏览器缓存?

对于浏览器的缓存,主要针对的是前端的静态资源,最好的效果就是,在发起请求之后,拉取相应的静态资源,并保存在本地。如果服务器的静态资源没有更新,那么在下次请求的时候,就直接从本地读取即可,如果服务器的静态资源已经更新,那么我们再次请求的时候,就到服务器拉取新的资源,并保存在本地。这样就大大的减少了请求的次数,提高了网站的性能。这就要用到浏览器的缓存策略了。


所谓的浏览器缓存指的是浏览器将用户请求过的静态资源,存储到电脑本地磁盘中,当浏览器再次访问时,就可以直接从本地加载,不需要再去服务端请求了。


使用浏览器缓存,有以下优点:


  • 减少了服务器的负担,提高了网站的性能

  • 加快了客户端网页的加载速度

  • 减少了多余网络数据传输

深浅拷贝

浅拷贝:只考虑对象类型。


function shallowCopy(obj) {    if (typeof obj !== 'object') return
let newObj = obj instanceof Array ? [] : {} for (let key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key] } } return newObj}
复制代码


简单版深拷贝:只考虑普通对象属性,不考虑内置对象和函数。


function deepClone(obj) {    if (typeof obj !== 'object') return;    var newObj = obj instanceof Array ? [] : {};    for (var key in obj) {        if (obj.hasOwnProperty(key)) {            newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];        }    }    return newObj;}
复制代码


复杂版深克隆:基于简单版的基础上,还考虑了内置对象比如 Date、RegExp 等对象和函数以及解决了循环引用的问题。


const isObject = (target) => (typeof target === "object" || typeof target === "function") && target !== null;
function deepClone(target, map = new WeakMap()) { if (map.get(target)) { return target; } // 获取当前值的构造函数:获取它的类型 let constructor = target.constructor; // 检测当前对象target是否与正则、日期格式对象匹配 if (/^(RegExp|Date)$/i.test(constructor.name)) { // 创建一个新的特殊对象(正则类/日期类)的实例 return new constructor(target); } if (isObject(target)) { map.set(target, true); // 为循环引用的对象做标记 const cloneTarget = Array.isArray(target) ? [] : {}; for (let prop in target) { if (target.hasOwnProperty(prop)) { cloneTarget[prop] = deepClone(target[prop], map); } } return cloneTarget; } else { return target; }}
复制代码

label 的作用是什么?如何使用?

label 标签来定义表单控件的关系:当用户选择 label 标签时,浏览器会自动将焦点转到和 label 标签相关的表单控件上。


  • 使用方法 1:


<label for="mobile">Number:</label><input type="text" id="mobile"/>
复制代码


  • 使用方法 2:


<label>Date:<input type="text"/></label>
复制代码

什么是 JavaScript 中的包装类型?

在 JavaScript 中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时 JavaScript 会在后台隐式地将基本类型的值转换为对象,如:


const a = "abc";a.length; // 3a.toUpperCase(); // "ABC"
复制代码


在访问'abc'.length时,JavaScript 将'abc'在后台转换成String('abc'),然后再访问其length属性。


JavaScript 也可以使用Object函数显式地将基本类型转换为包装类型:


var a = 'abc'Object(a) // String {"abc"}
复制代码


也可以使用valueOf方法将包装类型倒转成基本类型:


var a = 'abc'var b = Object(a)var c = b.valueOf() // 'abc'
复制代码


看看如下代码会打印出什么:


var a = new Boolean( false );if (!a) {    console.log( "Oops" ); // never runs}
复制代码


答案是什么都不会打印,因为虽然包裹的基本类型是false,但是false被包裹成包装类型后就成了对象,所以其非值为false,所以循环体中的内容不会运行。

setInterval 模拟 setTimeout

描述:使用setInterval模拟实现setTimeout的功能。


思路setTimeout的特性是在指定的时间内只执行一次,我们只要在setInterval内部执行 callback 之后,把定时器关掉即可。


实现


const mySetTimeout = (fn, time) => {    let timer = null;    timer = setInterval(() => {        // 关闭定时器,保证只执行一次fn,也就达到了setTimeout的效果了        clearInterval(timer);        fn();    }, time);    // 返回用于关闭定时器的方法    return () => clearInterval(timer);}
// 测试const cancel = mySetTimeout(() => { console.log(1);}, 1000); // 一秒后打印 1
复制代码

如果一个构造函数,bind 了一个对象,用这个构造函数创建出的实例会继承这个对象的属性吗?为什么?

不会继承,因为根据 this 绑定四大规则,new 绑定的优先级高于 bind 显示绑定,通过 new 进行构造函数调用时,会创建一个新对象,这个新对象会代替 bind 的对象绑定,作为此函数的 this,并且在此函数没有返回对象的情况下,返回这个新建的对象

说一下 vue3.0 你了解多少?

 <!-- 响应式原理的改变 Vue3.x 使用Proxy取代 Vue2.x 版本的Object.defineProperty --> <!-- 组件选项声明方式Vue3.x 使用Composition API setup 是Vue3.x新增的一个选项,他    是组件内使用Composition API 的入口 --> <!-- 模板语法变化slot具名插槽语法 自定义指令 v-model 升级 --> <!-- 其它方面的更改Suspense支持Fragment(多个根节点) 和Protal (在dom其他部分渲染组建内容)组件     针对一些特殊的场景做了处理。基于treeshaking优化,提供了更多的内置功能。 -->
复制代码

组件之间的传值有几种方式

1、父传子2、子传父3、eventbus4、ref/$refs5、$parent/$children6、$attrs/$listeners7、依赖注入(provide/inject)
复制代码

懒加载的实现原理

图片的加载是由src引起的,当对src赋值时,浏览器就会请求图片资源。根据这个原理,我们使用 HTML5 的data-xxx属性来储存图片的路径,在需要加载图片的时候,将data-xxx中图片的路径赋值给src,这样就实现了图片的按需加载,即懒加载。


注意:data-xxx 中的xxx可以自定义,这里我们使用data-src来定义。


懒加载的实现重点在于确定用户需要加载哪张图片,在浏览器中,可视区域内的资源就是用户需要的资源。所以当图片出现在可视区域时,获取图片的真实地址并赋值给图片即可。


使用原生 JavaScript 实现懒加载:


知识点:


(1)window.innerHeight 是浏览器可视区的高度


(2)document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的距离


(3)imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离)


(4)图片加载条件:img.offsetTop < window.innerHeight + document.body.scrollTop;


图示: 代码实现:


<div class="container">     <img src="loading.gif"  data-src="pic.png">     <img src="loading.gif"  data-src="pic.png">     <img src="loading.gif"  data-src="pic.png">     <img src="loading.gif"  data-src="pic.png">     <img src="loading.gif"  data-src="pic.png">     <img src="loading.gif"  data-src="pic.png"></div><script>var imgs = document.querySelectorAll('img');function lozyLoad(){        var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;        var winHeight= window.innerHeight;        for(var i=0;i < imgs.length;i++){            if(imgs[i].offsetTop < scrollTop + winHeight ){                imgs[i].src = imgs[i].getAttribute('data-src');            }        }    }  window.onscroll = lozyLoad();</script>
复制代码

CSS 中可继承与不可继承属性有哪些

一、无继承性的属性


  1. display:规定元素应该生成的框的类型

  2. 文本属性


  • vertical-align:垂直文本对齐

  • text-decoration:规定添加到文本的装饰

  • text-shadow:文本阴影效果

  • white-space:空白符的处理

  • unicode-bidi:设置文本的方向


  1. 盒子模型的属性:width、height、margin、border、padding

  2. 背景属性:background、background-color、background-image、background-repeat、background-position、background-attachment

  3. 定位属性:float、clear、position、top、right、bottom、left、min-width、min-height、max-width、max-height、overflow、clip、z-index

  4. 生成内容属性:content、counter-reset、counter-increment

  5. 轮廓样式属性:outline-style、outline-width、outline-color、outline

  6. 页面样式属性:size、page-break-before、page-break-after

  7. 声音样式属性:pause-before、pause-after、pause、cue-before、cue-after、cue、play-during


二、有继承性的属性


  1. 字体系列属性


  • font-family:字体系列

  • font-weight:字体的粗细

  • font-size:字体的大小

  • font-style:字体的风格


  1. 文本系列属性


  • text-indent:文本缩进

  • text-align:文本水平对齐

  • line-height:行高

  • word-spacing:单词之间的间距

  • letter-spacing:中文或者字母之间的间距

  • text-transform:控制文本大小写(就是 uppercase、lowercase、capitalize 这三个)

  • color:文本颜色


  1. 元素可见性


  • visibility:控制元素显示隐藏


  1. 列表布局属性


  • list-style:列表风格,包括 list-style-type、list-style-image 等


  1. 光标属性


  • cursor:光标显示为何种形态

用户头像

还未添加个人签名 2022.09.08 加入

还未添加个人简介

评论

发布
暂无评论
美团前端面试题(附答案)_JavaScript_hellocoder2029_InfoQ写作社区