> 文档中心 > 前端面试锦集

前端面试锦集


基本面试:
做过最满意的项目是什么?项目背景?为什么要做这件事情?最终达到什么效果?你处于什么样的角色,起到了什么方面的作用?在项目中遇到什么技术问题?具体是如何解决的? 如果再做这个项目,你会在哪些方面进行改善?
基础扎实:
IT行业,哪些发展好的同学都是具备扎实基础知识,如果理解计算机基础会更好,因为我们面临很多非前端计算问题的。
主动思考:
被动完成任务成长会很慢的,需要有自己的想法,而不是仅仅是完成任务的。
自动学习:
前端领域知识淘汰速度很快,需要经常学习新知识。
追溯深度:
遇到问题是多研究背后深层次的原因,而不是绕过去。如遇到一个bug,有时间一定要理解本质原因。
宽阔视野:
创新往往来自不同领域的交集,如果理解更多领域,就会有更多的想法。
学习总结:
谈到某个技术的时候,需扪心自问,学这个是为了做什么?如何学习的?可以从什么渠道了解最新的知识?

目录概览

    • HTML
      • 1.Doctype
      • 2.对语义化的理解
      • 3.link和@import引入css样式区别
      • 4.HTML5新特性
        • 4.1常见语义化标签
        • 4.2新增表单元素
        • 4.3新增视频 和音频 标签
    • CSS
      • 1.css水平、垂直居中的写法,请至少写出4种?
      • 2.文本超出省略号表示
      • 3.让图文不可复制
      • 4.行内元素有哪些?块级元素有哪些? 空(void)元素有那些?
      • 5.画一条0.5px的直线
      • 6.怎样处理 移动端 1px 被渲染成 2px 问题?
      • 7.盒子模型
      • 8.flex自适应布局
      • 9.清除浮动的几种方式,及原理?
      • 10.1rem、1em、1vh、1px各自代表的含义?
      • 11.消除 transition 闪屏
      • 12.css3 有哪些新的属性
      • 13.什么是CSS 预处理器 / 后处理器?大家为什么要使用他们?
      • 14.CSS优化、提高性能的方法有哪些?
      • 15.可能用到的meta标签
      • 16.消除 transition 闪屏
    • JavaScript
      • 推荐的JavaScript经典面试题
      • 1.根据你的理解,请简述JavaScript脚本的执行原理?
      • 2.JS代码规范
      • 3.数据类型
      • 4.用js递归的方式写1到100求和
      • 5.数组去重
      • 6.对Function与Object的理解
      • 7.什么是闭包
      • 8.3==true 打印出什么
      • 9.判断变量是不是数组的几个方法
      • 10.this
      • 11.拷贝
      • 12.Promise
      • 13.Var 、Let 和 const 的区别。
      • 14.[对js垃圾回收机制的理解](https://blog.csdn.net/weixin_45788691/article/details/108381897)
      • 15.js面向对象中继承实现
        • Ⅰ.原型链(prototype chaining)
        • Ⅱ.call()/apply()
        • Ⅲ.混合方式(prototype和call()/apply()结合)
        • Ⅳ .对象冒充
    • 非技术型
      • 1.get、post的区别
      • 2.如何中断ajax请求?
      • 3.三次握手
      • 4.四次挥手
      • 5.从输入url到页面加载完成发生了什么?——前端角度
      • 6.你所知道的http的响应码及含义?
      • 7.描述一下cookies,sessionStorage和localStorage的区别?
      • 8.怎么让Chrome支持小于12px 的文字?
    • 框架通识(vue、uniapp)
      • 1.MVVM
      • 2.路由原理
      • 3.脏数据检测
    • 网络相关
      • 1.AJAX的工作原理
      • 2.强缓存
      • 3.协商缓存
      • 4.选择合适的缓存策略
      • 5.预加载
      • 6.预渲染
    • 性能优化
      • 1.前端性能优化的七大手段
      • 2.防抖
      • 3.节流
      • 4.优化渲染过程
        • 4.1懒执行
        • 4.2懒加载
      • 5.文件优化
        • 5.1图片优化:
        • 5.2图片加载优化:
        • 5.3选择正确的图片格式:
      • 6.其他文件优化
      • 7.CDN
      • 8.使用 Webpack 优化项目
      • 9.监控
      • 10.如何渲染几万条数据并不卡住界面
    • 前端工程化
    • 前端面试中,回答不出的那些问题
    • 前端面试官的套路,你懂吗?
    • 前端面试常考的手写代码不是背出来的!
    • 常规交谈

HTML

1.Doctype

告知浏览器解析器用什么文档标准解析这个文档

2.对语义化的理解

title,header,nav,main,article,h1~h6,ul,ol,address,canvas,dialog,aside,section,figure,details,mark

  • 代码结构清晰,易于阅读,利于开发和维护
  • 提高体验,在样式加载失败时,页面结构清晰
  • 方便其他设备解析(如屏幕阅读器)根据语义渲染网页
  • 有利于搜索引擎优化(SEO),搜索引擎爬虫会根据不同的标签来赋予不同的权重

3.link和@import引入css样式区别

1 属性差别。link属于XHTML标签,而@import完全是CSS提供的语法规则。
2 加载顺序的差别。当一个页面被加载的时候(就是被浏览者浏览的时候),link引用的CSS会同时被加载,而@import引用的CSS会等到页面全部被下载完再被加载。所以有时候浏览@import加载CSS的页面时开始会没有样式(就是闪烁)
3 兼容性的差别。由于@import是CSS2.1提出的所以老的浏览器不支持,@import只有在IE5以上的才能识别,而link标签无此问题。

4.HTML5新特性

4.1常见语义化标签

定义了文档的头部区域

定义了文档的尾部区域

<div class="small">small
<!--<div class="clear">额外标签法
-->
<div class="footer">

优点:符合闭合浮动思想,结构语义化正确
缺点:ie6-7不支持伪元素:after,使用zoom:1触发hasLayout.
使用before和after双伪元素清除浮动

.clearfix:after,.clearfix:before{ content: ""; display: table;    }    .clearfix:after{ clear: both;    }    .clearfix{ *zoom: 1;    }  <div class="fahter clearfix"> <div class="big">big
<div class="small">small <div class="footer">

优点:代码更简洁
缺点:用zoom:1触发hasLayout.

10.1rem、1em、1vh、1px各自代表的含义?

11.消除 transition 闪屏

.css {    -webkit-transform-style: preserve-3d;    -webkit-backface-visibility: hidden;    -webkit-perspective: 1000;}

过渡动画(在没有启动硬件加速的情况下)会出现抖动的现象, 以上的解决方案只是改变视角来启动硬件加速的一种方式;启动硬件加速的另外一种方式:

.css {    -webkit-transform: translate3d(0,0,0);    -moz-transform: translate3d(0,0,0);    -ms-transform: translate3d(0,0,0);    transform: translate3d(0,0,0);}

启动硬件加速
最常用的方式:translate3d、translateZ、transform
opacity 属性/过渡动画(需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态)
will-chang 属性(这个比较偏僻),一般配合opacity与translate使用(而且经测试,除了上述可以引发硬件加速的属性外,其它属性并不会变成复合层)。
弊端:硬件加速会导致 CPU 性能占用量过大,电池电量消耗加大 ;因此尽量避免泛滥使用硬件加速。

12.css3 有哪些新的属性

@font-face {  font-family: 'MyFont';    /* 表示为这种字体起一个名称,可以随意设置,这里用的是MyFont */  src: url('myfont.eot');     /* 这一行表示字体位置,由于ie只支持服务器端的eot字体,所以这一行是ie专用的 */      src: local('myfont.ttf'),  url('myfont.woff') format('woff'),  url('myfont.ttf') format('truetype'); /* local()表示在本机(客户端)查找该字体,如果本机已经安装了,就不用下载了。url()表示字体在服务器上的位置,format()用来说明字体格式。Firefox 3.5支持TrueType和OpenType字体,Firefox 3.6又增加了WOFF字体。其他基于Webkit引擎的浏览器(sarif,opera、chrome),目前好像只支持truetype */}h2{ font-family: "MyFont"; }  /*使用*/

13.什么是CSS 预处理器 / 后处理器?大家为什么要使用他们?

CSS 预处理器为 CSS 增加一些编程的特性,无需考虑浏览器的兼容性问题”,例如你可以在 CSS 中使用变量、简单的逻辑程序、函数(如右侧代码编辑器中就使用了变量$color)等等在编程语言中的一些基本特性,可以让你的 CSS 更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处。
结构清晰,便于扩展。可以方便地屏蔽浏览器私有语法差异。封装对浏览器语法差异的重复处理,减少无意义的机械劳动。可以轻松实现多重继承。完全兼容 CSS 代码,可以方便地应用到老项目中,LESS 只是在 CSS 语法上做了扩展,所以老的 CSS 代码也可以与 LESS 代码一同编译。

预处理器例如:LESS、Sass、Stylus,用来预编译Sass或less,增强了css代码的复用性,还有层级、mixin、变量、循环、函数等,具有很方便的UI组件模块化开发能力,极大的提高工作效率。
后处理器例如:PostCSS,通常被视为在完成的样式表中根据CSS规范处理CSS,让其更有效;目前最常做的是给CSS属性添加浏览器私有前缀,实现跨浏览器兼容性的问题。

14.CSS优化、提高性能的方法有哪些?

15.可能用到的meta标签

<!-- 设置缩放 --><meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" /><!-- 可隐藏地址栏,仅针对IOS的Safari(注:IOS7.0版本以后,safari上已看不到效果) --><meta name="apple-mobile-web-app-capable" content="yes" /><!-- 仅针对IOS的Safari顶端状态条的样式(可选default/black/black-translucent ) --><meta name="apple-mobile-web-app-status-bar-style" content="black" /><!-- IOS中禁用将数字识别为电话号码/忽略Android平台中对邮箱地址的识别 --><meta name="format-detection"content="telephone=no, email=no" /><!-- 启用360浏览器的极速模式(webkit) --><meta name="renderer" content="webkit"><!-- 避免IE使用兼容模式 --><meta http-equiv="X-UA-Compatible" content="IE=edge"><!-- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 --><meta name="HandheldFriendly" content="true"><!-- 微软的老式浏览器 --><meta name="MobileOptimized" content="320"><!-- uc强制竖屏 --><meta name="screen-orientation" content="portrait"><!-- QQ强制竖屏 --><meta name="x5-orientation" content="portrait"><!-- UC强制全屏 --><meta name="full-screen" content="yes"><!-- QQ强制全屏 --><meta name="x5-fullscreen" content="true"><!-- UC应用模式 --><meta name="browsermode" content="application"><!-- QQ应用模式 --><meta name="x5-page-mode" content="app"><!-- windows phone 点击无高光 --><meta name="msapplication-tap-highlight" content="no">

16.消除 transition 闪屏

.css {    -webkit-transform-style: preserve-3d;    -webkit-backface-visibility: hidden;    -webkit-perspective: 1000;}

过渡动画(在没有启动硬件加速的情况下)会出现抖动的现象, 以上的解决方案只是改变视角来启动硬件加速的一种方式;启动硬件加速的另外一种方式:

.css {    -webkit-transform: translate3d(0,0,0);    -moz-transform: translate3d(0,0,0);    -ms-transform: translate3d(0,0,0);    transform: translate3d(0,0,0);}

启动硬件加速:
最常用的方式:translate3d、translateZ、transform
opacity 属性/过渡动画(需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态)
will-chang 属性(这个比较偏僻),一般配合opacity与translate使用(而且经测试,除了上述可以引发硬件加速的属性外,其它属性并不会变成复合层)。
弊端:
硬件加速会导致 CPU 性能占用量过大,电池电量消耗加大 ;因此尽量避免泛滥使用硬件加速。

JavaScript

推荐的JavaScript经典面试题

数据类型到一些隐式转换这些基础知识,看代码说输出,v8底层执行机制、垃圾回收、闭包、作用域、作用域链,原型、原型链,手写代码,如:防抖、节流、bind、call、apply、深拷贝、浅拷贝、Promise、async、await、webpack、框架、http等。

1.根据你的理解,请简述JavaScript脚本的执行原理?

JavaScript是一种动态、弱类型、基于原型的语言,通过浏览器可以直接执行,当浏览器遇到 标记的时候,浏览器会执行之间的javascript代码。嵌入的js代码是顺序执行的,每个脚本定义的全局变量和函数,都可以被后面执行的脚本所调用。 变量的调用,必须是前面已经声明,否则获取的变量值是undefined。

2.JS代码规范

3.数据类型

基本数据类型:string、number、null、undefined、boolean
引用数据类型:Object、Array、Date、RegExp
变量:var关键字来定义变量(ES5)、let命令来声明变量(ES6)、const命令声明一个只读的常量(ES6)

4.用js递归的方式写1到100求和

递归我们经常用到,vue在实现双向绑定进行数据检验的时候用的也是递归,但要我们面试的时候手写一个递归,如果对递归的概念理解不透彻,可能还是会有一些问题。

function add(num1,num2){var num = num1+num2; if(num2+1>100){ return num;}else{  return add(num,num2+1) } }var sum =add(1,2);  

5.数组去重

此题看着简单,但要想面试官给你高分还是有难度的。至少也要写出几种方法

js:

var arr=['12','32','89','12','12','78','12','32'];    // 最简单数组去重法    function unique1(array){ var n = []; //一个新的临时数组 for(var i = 0; i < array.length; i++){ //遍历当前数组     if (n.indexOf(array[i]) == -1)  n.push(array[i]); } return n;    }    arr=unique1(arr);    // 速度最快, 占空间最多(空间换时间)    function unique2(array){ var n = {}, r = [], type; for (var i = 0; i < array.length; i++) {     type = typeof array[i];     if (!n[array[i]]) {  n[array[i]] = [type];  r.push(array[i]);     } else if (n[array[i]].indexOf(type) < 0) {  n[array[i]].push(type);  r.push(array[i]);     } } return r;    }    //数组下标判断法    function unique3(array){ var n = [array[0]]; //结果数组 for(var i = 1; i < array.length; i++) { //从第二项开始遍历     if (array.indexOf(array[i]) == i)   n.push(array[i]); } return n;    }

es6:

//es6方法一数组去重arr=[...new Set(arr)];//es6方法二数组去重,function dedupe(array) {  return Array.from(new Set(array));//Array.from()能把set结构转换为数组}

6.对Function与Object的理解

Function
函数就是对象,代表函数的对象就是函数对象。所有的函数对象是被Function这个函数对象构造出来的。Function是最顶层的构造器。它构造了系统中所有的对象,包括用户自定义对象,系统内置对象,甚至包括它自已。这也表明Function具有自举性(自已构造自己的能力)。这也间接决定了Function的call和constructor逻辑相同。每个对象都有一个constructor 属性,用于指向创建其的函数对象。
a、函数与对象具有相同的语言地位
b、没有类,只有对象
c、函数也是一种对象,所谓的函数对象
d、对象是按引用来传递的
Object
对于Object它是最顶层的对象,所有的对象都将继承Object的原型,但是你也要明确的知道Object也是一个函数对象,所以说Object是被Function构造出来的。
在这里插入图片描述

//定义角度,前者为定义一个js函数,后者为这个函数的名称function Function(){}//用法角度,a也是functionvar a = new Object(function(){});

7.什么是闭包

8.3==true 打印出什么

会打印出false,这里会将true转变成1

9.判断变量是不是数组的几个方法

var a=[];a.constructor===Array //truea instanceof Array === true //true⚠️ 注意:以上方法在跨frame时会有问题,跨frame实例化的对象不共享原型var iframe = document.createElement('iframe');   //创建iframe  document.body.appendChild(iframe);   //添加到body中  xArray = window.frames[window.frames.length-1].Array;     var arr = new xArray(1,2,3); // 声明数组[1,2,3]      alert(arr instanceof Array); // false     alert(arr.constructor === Array); // false   解决:Object.prototype.toString.call(a) // "[object Array]"Array.isArray(a) //true

10.this

function foo() {console.log(this.a)}var a = 1foo()var obj = {a: 2,foo: foo}obj.foo()// 以上两者情况 `this` 只依赖于调用函数前的对象,优先级是第二个情况大于第一个情况// 以下情况是优先级最高的,`this` 只会绑定在 `c` 上,不会被任何方式修改 `this` 指向var c = new foo()c.a = 3console.log(c.a)// 还有种就是利用 call,apply,bind 改变 this,这个优先级仅次于 new
function a() {    return () => { return () => { console.log(this) }    }}console.log(a()()())

箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 this 是 window。并且 this 一旦绑定了上下文,就不会被任何代码改变。

11.拷贝

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
具体在业务中使用哪个,还得根据自己的业务需求。当然,深拷贝的方法也有很多个,这里只列出常用的一种方法,JSON.parse(JSON.stringify())

浅拷贝:定义了一个obj对象,和一个newObj对象,并让newObj的person等于指向obj对象(实则是将newObj的person属性指向obj对象的指针),所以改变newObj.person.name的值,实则是改变obj.name的值。

var obj = {   id: 123,    name: "小三",    address: "china"     }     var newObj = {}     newObj.person = obj;   //浅拷贝  newObj.person.name = "小三"     console.log(obj.name);  //打印小三

深拷贝 :定义obj对象和newObj对象,在给newObje对象的person属性赋值的时候,我们用JSON.parse(JSON.stringify(obj))方法将obj深拷贝了一份,也就是说重新申请了一个空间,与原对象不共享内存,属于完全独立的存在,所以在改变newObj.person.name属性之后,obj.name是不会跟着发生改变的。

var obj = { id:123, name:"小三", address:"china"    }    var newObj = {}    newObj.person = JSON.parse(JSON.stringify(obj));  //深拷贝     newObj.person.name="小三"    console.log(obj.name);// 打印小三

12.Promise

Promise 是 ES6 新增的语法,解决了回调地狱的问题。
可以把 Promise 看成一个状态机。初始是 pending 状态,可以通过函数 resolve 和 reject ,将状态转变为 resolved 或者 rejected 状态,状态一旦改变就不能再次变化。

then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then 调用就失去意义了。
对于 then 来说,本质上可以把它看成是 flatMap

// 三种状态const PENDING = "pending";const RESOLVED = "resolved";const REJECTED = "rejected";// promise 接收一个函数参数,该函数会立即执行function MyPromise(fn) {  let _this = this;  _this.currentState = PENDING;  _this.value = undefined;  // 用于保存 then 中的回调,只有当 promise  // 状态为 pending 时才会缓存,并且每个实例至多缓存一个  _this.resolvedCallbacks = [];  _this.rejectedCallbacks = [];  _this.resolve = function (value) {    if (value instanceof MyPromise) {      // 如果 value 是个 Promise,递归执行      return value.then(_this.resolve, _this.reject)    }    setTimeout(() => { // 异步执行,保证执行顺序      if (_this.currentState === PENDING) { _this.currentState = RESOLVED; _this.value = value; _this.resolvedCallbacks.forEach(cb => cb());      }    })  };  _this.reject = function (reason) {    setTimeout(() => { // 异步执行,保证执行顺序      if (_this.currentState === PENDING) { _this.currentState = REJECTED; _this.value = reason; _this.rejectedCallbacks.forEach(cb => cb());      }    })  }  // 用于解决以下问题  // new Promise(() => throw Error('error))  try {    fn(_this.resolve, _this.reject);  } catch (e) {    _this.reject(e);  }}MyPromise.prototype.then = function (onResolved, onRejected) {  var self = this;  // 规范 2.2.7,then 必须返回一个新的 promise  var promise2;  // 规范 2.2.onResolved 和 onRejected 都为可选参数  // 如果类型不是函数需要忽略,同时也实现了透传  // Promise.resolve(4).then().then((value) => console.log(value))  onResolved = typeof onResolved === 'function' ? onResolved : v => v;  onRejected = typeof onRejected === 'function' ? onRejected : r => throw r;  if (self.currentState === RESOLVED) {    return (promise2 = new MyPromise(function (resolve, reject) {      // 规范 2.2.4,保证 onFulfilled,onRjected 异步执行      // 所以用了 setTimeout 包裹下      setTimeout(function () { try {   var x = onResolved(self.value);   resolutionProcedure(promise2, x, resolve, reject); } catch (reason) {   reject(reason); }      });    }));  }  if (self.currentState === REJECTED) {    return (promise2 = new MyPromise(function (resolve, reject) {      setTimeout(function () { // 异步执行onRejected try {   var x = onRejected(self.value);   resolutionProcedure(promise2, x, resolve, reject); } catch (reason) {   reject(reason); }      });    }));    
if (self.currentState === PENDING) {    return (promise2 = new MyPromise(function (resolve, reject) {      self.resolvedCallbacks.push(function () { // 考虑到可能会有报错,所以使用 try/catch 包裹 try {   var x = onResolved(self.value);   resolutionProcedure(promise2, x, resolve, reject); } catch (r) {   reject(r); }      });      self.rejectedCallbacks.push(function () { try {   var x = onRejected(self.value);   resolutionProcedure(promise2, x, resolve, reject); } catch (r) {   reject(r); }      });    }));  }};// 规范 2.3function resolutionProcedure(promise2, x, resolve, reject) {  // 规范 2.3.1,x 不能和 promise2 相同,避免循环引用  if (promise2 === x) {    return reject(new TypeError("Error"));  }  // 规范 2.3.2  // 如果 x 为 Promise,状态为 pending 需要继续等待否则执行  if (x instanceof MyPromise) {    if (x.currentState === PENDING) {      x.then(function (value) { // 再次调用该函数是为了确认 x resolve 的 // 参数是什么类型,如果是基本类型就再次 resolve // 把值传给下个 then resolutionProcedure(promise2, value, resolve, reject);      }, reject);    } else {      x.then(resolve, reject);    }    return;  }  // 规范 2.3.3.3.3  // reject 或者 resolve 其中一个执行过得话,忽略其他的  let called = false;  // 规范 2.3.3,判断 x 是否为对象或者函数  if (x !== null && (typeof x === "object" || typeof x === "function")) {    // 规范 2.3.3.2,如果不能取出 then,就 reject    try {      // 规范 2.3.3.1      let then = x.then;      // 如果 then 是函数,调用 x.then      if (typeof then === "function") { // 规范 2.3.3.3 then.call(   x,   y => {     if (called) return;     called = true;     // 规范 2.3.3.3.1     resolutionProcedure(promise2, y, resolve, reject);   },   e => {     if (called) return;     called = true;     reject(e);   } );      } else { // 规范 2.3.3.4 resolve(x);      }    } catch (e) {      if (called) return;      called = true;      reject(e);    }  } else {    // 规范 2.3.4,x 为基本类型    resolve(x);  }}

以上就是根据 Promise / A+ 规范来实现的代码,可以通过 promises-aplus-tests 的完整测试

13.Var 、Let 和 const 的区别。

var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会:var 声明变量存在变量提升,let 和 const 不存在变量提升let 和 const 声明形成块作用域同一作用域下 let 和 const 不能声明同名变量,而 var 可以
Const  1、一旦声明必须赋值,不能使用 null 占位。2、声明后不能再修改 3、如果声明的是复合类型数据,可以修改其属性

14.对js垃圾回收机制的理解

15.js面向对象中继承实现

面向对象的基本特征有:封闭、继承、多态。

Ⅰ.原型链(prototype chaining)

function car(price){    this.price = price;}car.prototype.sayPrice = function(){    console.log("Price is "+this.price);}var oCar = new car("100W");oCar.sayPrice(); function toyCar(price){    this.price = price;}toyCar.prototype = new car()var oCar2 = new toyCar("10CNY");oCar2.sayPrice();

Ⅱ.call()/apply()

function useCall(a,b){  this.a = a;  this.b = b;  this.say = function(){      console.log("I'm "+this.a+" You're "+this.b);  } } function callThefunction (){    var args = arguments;    useCall.call(this,args[0],args[1]);   // useCall.apply(this,arguments);  }var testCall1 = new useCall("Not YY","Not TT");testCall1.say(); var testCall2 = new callThefunction("YY","TT");testCall2.say();

Ⅲ.混合方式(prototype和call()/apply()结合)

function house(size,price){    this.size = size;    this.price = price;}house.prototype.showArea=function (){    console.log("面积为"+this.size);}house.prototype.sayPrice=function (){    console.log("价钱为"+this.price);} function maofan(size,price){    house.call(this,size,price);}maofan.prototype = new house(); var newmaofan = new maofan("20Square meters ","1000CNY");newmaofan.showArea();

Ⅳ .对象冒充

function Person(name,age){   this.name = name;   this.age = age;   this.show = function(){console.log(this.name+",  "+this.age);   }}Person.prototype.sayHi = function(){   alert('hi');} function Student(name,age){    this.student = Person;   //将Person类的构造函数赋值给this.student   this.student(name,age);  //js中实际上是通过对象冒充来实现继承的   delete this.student;    //移除对Person的引用} var s = new Student("小明",17);s.show(); var p = new Person("小花",18);p.show();// 小明,  17// 小花,  18

非技术型

1.get、post的区别

2.如何中断ajax请求?

一种是设置超时时间让ajax自动断开,另一种是手动停止ajax请求,其核心是调用XML对象的abort方法,ajax.abort()

3.三次握手

4.四次挥手

中断连接端可以是Client端,也可以是Server端。

5.从输入url到页面加载完成发生了什么?——前端角度

6.你所知道的http的响应码及含义?

7.描述一下cookies,sessionStorage和localStorage的区别?

相同点:都会在浏览器端保存,有大小和同源限制。
在这里插入图片描述

8.怎么让Chrome支持小于12px 的文字?

做移动端的时候,设计师图片上的文字假如是10px,我们实现在网页上之后。往往设计师回来找我们,这个字体能小一些吗?我设计的是10px?为啥是12px?其实我们都知道,谷歌Chrome最小字体是12px,不管你设置成8px还是10px,在浏览器中只会显示12px,那么如何解决这个坑爹的问题呢?

针对谷歌浏览器内核,加webkit前缀,用transform:scale()这个属性进行缩放!

   p span{font-size:10px;-webkit-transform:scale(0.8);display:block;}

haorooms博客测试10px

框架通识(vue、uniapp)

1.MVVM

2.路由原理

前端路由实现起来其实很简单,本质就是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无须刷新。目前单页面使用的路由就只有两种实现方式

www.test.com/##/ 就是 Hash URL,当 ## 后面的哈希值发生变化时,不会向服务器请求数据,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面。
History 模式是 HTML5 新推出的功能,比之 Hash URL 更加美观

3.脏数据检测

当触发了指定事件后会进入脏数据检测,这时会调用 $digest 循环遍历所有的数据观察者,判断当前值是否和先前的值有区别,如果检测到变化的话,会调用 $watch 函数,然后再次调用 $digest 循环直到发现没有变化。循环至少为二次 ,至多为十次。

脏数据检测虽然存在低效的问题,但是不关心数据是通过什么方式改变的,都可以完成任务,但是这在 Vue 中的双向绑定是存在问题的。并且脏数据检测可以实现批量检测出更新的值,再去统一更新 UI,大大减少了操作 DOM 的次数。所以低效也是相对的,这就仁者见仁智者见智了。

网络相关

1.AJAX的工作原理

ajax是一种通过后台与服务器进行少量的数据交换,使页面实现异步更新是一种创建交互式网页应用的网页开发技术。

1、创建ajax对象(XMLHttpRequest/ActiveXObject(Microsoft.XMLHttp))
2、判断数据传输方式(GET/POST)
3、打开链接 open()
4、发送 send()
5、当ajax对象完成第四步(onreadystatechange)数据接收完成,判断http响应状态(status)200-300之间或者304(缓存)执行回调函数

异步请求响应快,用户体验好;页面无刷新、数据局部更新;按需取数据,减少了冗余请求和服务器的负担。
异步回调问题、this指向问题、路由跳转back问题;对搜索引擎的支持比较弱,对于一些手机还不是很好的支持

DNS 解析也是需要时间的,可以通过预解析的方式来预先获得域名所对应的 IP。

<link rel="dns-prefetch" href="//yuchengkai.cn" />

缓存对于前端性能优化来说是个很重要的点,良好的缓存策略可以降低资源的重复加载提高网页的整体加载速度。
通常浏览器缓存策略分为两种:强缓存和协商缓存。

2.强缓存

实现强缓存可以通过两种响应头实现:Expires 和 Cache-Control 。强缓存表示在缓存期间不需要请求,state code 为 200。

Expires: Wed, 22 Oct 2018 08:41:00 GMTCache-control: max-age=30

3.协商缓存

如果缓存过期了,我们就可以使用协商缓存来解决问题。协商缓存需要请求,如果缓存有效会返回 304。
协商缓存需要客户端和服务端共同实现,和强缓存一样,也有两种实现方式。

Last-Modified 和 If-Modified-Since:
Last-Modified 表示本地文件最后修改日期,If-Modified-Since 会将 Last-Modified 的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。
但是如果在本地打开缓存文件,就会造成 Last-Modified 被修改,所以在 HTTP / 1.1 出现了 ETag 。

ETag 和 If-None-Match:
ETag 类似于文件指纹,If-None-Match 会将当前 ETag 发送给服务器,询问该资源 ETag 是否变动,有变动的话就将新的资源发送回来。并且 ETag 优先级比 Last-Modified 高。

4.选择合适的缓存策略

在开发中,可能会遇到这样的情况。有些资源不需要马上用到,但是希望尽早获取,这时候就可以使用预加载。

5.预加载

预加载其实是声明式的 fetch ,强制浏览器请求资源,并且不会阻塞 onload 事件,可以使用以下代码开启预加载.

<link rel="preload" href="http://example.com" />

预加载可以一定程度上降低首屏的加载时间,因为可以将一些不影响首屏但重要的文件延后加载,唯一缺点就是兼容性不好。

6.预渲染

<link rel="prerender" href="http://example.com" />

预渲染虽然可以提高页面的加载速度,但是要确保该页面百分百会被用户在之后打开,否则就白白浪费资源去渲染

性能优化

1.前端性能优化的七大手段

  1. 减少请求数量
  2. 减小资源大小
  3. 优化网络连接
  4. 优化资源加载
  5. 减少重绘回流
  6. 性能更好的API
  7. webpack优化

日常开发中遇到滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。

2.防抖

// 这个是用来获取当前时间戳的function now() {  return +new Date()}/ * 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行 * * @param  {function} func 回调函数 * @param  {number}   wait 表示时间窗口的间隔 * @param  {boolean}  immediate   设置为ture时,是否立即调用函数 * @return {function}      返回客户调用函数 */function debounce (func, wait = 50, immediate = true) {  let timer, context, args  // 延迟执行函数  const later = () => setTimeout(() => {    // 延迟函数执行完毕,清空缓存的定时器序号    timer = null    // 延迟执行的情况下,函数会在延迟函数中执行    // 使用到之前缓存的参数和上下文    if (!immediate) {      func.apply(context, args)      context = args = null    }  }, wait)  // 这里返回的函数是每次实际调用的函数  return function(...params) {    // 如果没有创建延迟执行函数(later),就创建一个    if (!timer) {      timer = later()      // 如果是立即执行,调用函数      // 否则缓存参数和调用上下文      if (immediate) { func.apply(this, params)      } else { context = this args = params      }    // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个    // 这样做延迟函数会重新计时    } else {      clearTimeout(timer)      timer = later()    }  }}

对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为 null,就可以再次点击了。对于延时执行函数来说的实现:清除定时器ID,如果是延迟调用就调用函数。

3.节流

防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

/ * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait * * @param  {function}   func      回调函数 * @param  {number}     wait      表示时间窗口的间隔 * @param  {object}     options   如果想忽略开始函数的的调用,传入{leading: false}。 *    如果想忽略结尾函数的调用,传入{trailing: false} *    两者不能共存,否则函数不能执行 * @return {function}      返回客户调用函数 */_.throttle = function(func, wait, options) {    var context, args, result;    var timeout = null;    // 之前的时间戳    var previous = 0;    // 如果 options 没传则设为空对象    if (!options) options = {};    // 定时器回调函数    var later = function() {      // 如果设置了 leading,就将 previous 设为 0      // 用于下面函数的第一个 if 判断      previous = options.leading === false ? 0 : _.now();      // 置空一是为了防止内存泄漏,二是为了下面的定时器判断      timeout = null;      result = func.apply(context, args);      if (!timeout) context = args = null;    };    return function() {      // 获得当前时间戳      var now = _.now();      // 首次进入前者肯定为 true  // 如果需要第一次不执行函数  // 就将上次时间戳设为当前的      // 这样在接下来计算 remaining 的值时会大于0      if (!previous && options.leading === false) previous = now;      // 计算剩余时间      var remaining = wait - (now - previous);      context = this;      args = arguments;      // 如果当前调用已经大于上次调用时间 + wait      // 或者用户手动调了时间   // 如果设置了 trailing,只会进入这个条件  // 如果没有设置 leading,那么第一次会进入这个条件  // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了  // 其实还是会进入的,因为定时器的延时  // 并不是准确的时间,很可能你设置了2秒  // 但是他需要2.2秒才触发,这时候就会进入这个条件      if (remaining <= 0 || remaining > wait) { // 如果存在定时器就清理掉否则会调用二次回调 if (timeout) {   clearTimeout(timeout);   timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null;      } else if (!timeout && options.trailing !== false) { // 判断是否设置了定时器和 trailing    // 没有的话就开启一个定时器 // 并且不能不能同时设置 leading 和 trailing timeout = setTimeout(later, remaining);      }      return result;    };  };

4.优化渲染过程

4.1懒执行

懒执行就是将某些逻辑延迟到使用时再计算。该技术可以用于首屏优化,对于某些耗时逻辑并不需要在首屏就使用的,就可以使用懒执行。懒执行需要唤醒,一般可以通过定时器或者事件的调用来唤醒。

4.2懒加载

懒加载就是将不关键的资源延后加载。
懒加载的原理就是只加载自定义区域(通常是可视区域,但也可以是即将进入可视区域)内需要加载的东西。对于图片来说,先设置图片标签的 src 属性为一张占位图,将真实的图片资源放入一个自定义属性中,当进入自定义区域时,就将自定义属性替换为 src 属性,这样图片就会去下载资源,实现了图片懒加载。
懒加载不仅可以用于图片,也可以使用在别的资源上。比如进入可视区域才开始播放视频等等。

5.文件优化

5.1图片优化:

减少像素点、减少每个像素点能够显示的颜色

5.2图片加载优化:

5.3选择正确的图片格式:

6.其他文件优化

7.CDN

静态资源尽量使用 CDN 加载,由于浏览器对于单个域名有并发请求上限,可以考虑使用多个 CDN 域名。对于 CDN 加载静态资源需要注意 CDN 域名要与主站不同,否则每次请求都会带上主站的 Cookie。

8.使用 Webpack 优化项目

9.监控

10.如何渲染几万条数据并不卡住界面

语法:
window.requestAnimationFrame(callback);
callback:
下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。该回调函数会被传入DOMHighResTimeStamp参数,该参数与performance.now()的返回值相同,它表示requestAnimationFrame() 开始去执行回调函数的时刻。
返回值:
一个 long 整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。

<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <meta http-equiv="X-UA-Compatible" content="ie=edge" />    <title>Document</title>  </head>  <body>    <ul>      数据条渲染:    </ul>    <script>      setTimeout(() => { // 插入千万条数据 const total = 10000000 // 一次插入 20 条,如果觉得性能不好就减少 const once = 20 // 渲染数据总共需要几次 const loopCount = total / once var countOfRender = 0 var ul = document.querySelector('ul')console.time('loopTime'); function add() {   // 优化性能,插入不会造成回流   const fragment = document.createDocumentFragment()   for (var i = 0; i < once; i++) {     const li = document.createElement('li')     li.innerText = Math.floor(Math.random() * total)     fragment.appendChild(li)   }   ul.appendChild(fragment)   countOfRender += 1   loop() }//获取代码的执行时间console.timeEnd('loopTime'); function loop() {   if (countOfRender < loopCount) {     window.requestAnimationFrame(add)   } } loop()      }, 0)    </script>  </body></html>

前端工程化

前端工程化就是为了让前端开发能够“自成体系”,个人认为主要应该从模块化组件化规范化自动化四个方面思考。

前端面试中,回答不出的那些问题

前端面试官的套路,你懂吗?

前端面试常考的手写代码不是背出来的!

常规交谈

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

前端面试锦集 创作打卡挑战赛 前端面试锦集 赢取流量/现金/CSDN周边激励大奖

网络标签: