js面试常问问题含详细答案

7,612 阅读10分钟

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

总结一些前端js的知识,将笔记整理跟大家分享,有些知识会经常在前端面试的时候会问到,所以做个记录,希望对大家有所帮助,如果有什么问题,可以指出,会积极修正。
如果大家喜欢,可以点赞或留言我再继续更新面试题~~~~,谢谢大家~~~

1、前++、后++、区别?

var i=2 ;
a = i++ //将i的值赋给a , 即a = i,之后再执行i = i + 1;   2
a = ++i //将i+1 的值赋给a,即a = i + 1 ,之后再执行i = i + 1;   3
console.log(a)

【总结】:

前++是先自加后计算、后++是后自加先计算

1:前置++ 是将自身+1 后的值赋给变量,同时自身加1;

2:后置++ 是将自身的值赋给变量,之后自身再加1;

2、JS 有哪些数据类型?

Js基本数据类型 undefined null boolean number string bigint
Js引用类型 object Array function Date Math RegExp
ES6基本数据类型 多了个symbol

3、js判断类型?

1、typeof
检测不出null 和 数组,结果都为object,所以typeof常用于检测基本类型
2、instanceof
不能检测出number、boolean、string、undefined、null、symbol类型,所以instancof常用于检测复杂类型以及级成关系
3、constructor
null、undefined没有construstor方法,因此constructor不能判断undefined和null。但是contructor的指向是可以被改变,所以不安全
4、Object.prototype.toString.call
全类型都可以判断

4、数据类型怎么检测?

1typeof
例:console.log(typeof true) // boolean

2instanceof
例:console.log([1,2] instanceof Array) // true

3、constructor
例: console.log([1, 2].constructor === Array) // ture

4Object.prototype.toString.call
例:Object.prototype.toString.call([1, 2]) // [object Array]

判断是否是数组

var vv = [1, 2];
console.log(vv instanceof Array)
console.log(vv.constructor === Array)
console.log(Array.isArray(vv))
console.log(Object.prototype.toString.call(vv) === '[object Array]')
console.log(vv.__proto__ === Array.prototype)

5、Js数组的方法

join()数组转换成字符串
push()尾部添加
pop()尾部删除
shift() 头部删除
unshift() 头部添加
sort()排序
reverse()反转
concat()链接两个或多个数组
slice()
var arr=[1,2,3,4,5]
console.log(arr.slice(1)) //[2,3,4,5]选择序列号从1到最后的所有元素组成的新数组
console.log(arr.slice(1,3)) //[2,3]不包含序列号,序号为3的元素
splice()
splice(index,howmany,item1,...itemx)
index参数:必须,整数,规定添加或删除的位置,使用负数,从数组尾部规定位置
howmany参数:必须,要删除的数量,如果为0则不删除项目
item1,...itemx参数:可选,向数组添加的新项目
var arr=[1,2,3,4,5]
console.log(arr.splice(2,1,"hello"));//[3]返回的新数组
console.log(arr);//[1,2,"hello",4,5]
indexOf()和 lastIndexOf() (ES5新增)
forEach() (ES5新增)
map() (ES5新增)
filter() (ES5新增)
every() (ES5新增)
some() (ES5新增)
reduce()和 reduceRight() (ES5新增)

6、JS中的Array.splice()和Array.slice()方法有什么区别?

话不多说,来看第一个例子:

var arr=[0,1,2,3,4,5,6,7,8,9];//设置一个数组
console.log(arr.slice(2,7));//2,3,4,5,6
console.log(arr.splice(2,7));//2,3,4,5,6,7,8
//由此我们简单推测数量两个函数参数的意义,
slice(start,end)第一个参数表示开始位置,第二个表示截取到的位置(不包含该位置)
splice(start,length)第一个参数开始位置,第二个参数截取长度

接着看第二个:

var x=y=[0,1,2,3,4,5,6,7,8,9]
console.log(x.slice(2,5));//2,3,4
console.log(x);[0,1,2,3,4,5,6,7,8,9]原数组并未改变
//接下来用同样方式测试splice
console.log(y.splice(2,5));//2,3,4,5,6
console.log(y);//[0,1,7,8,9]显示原数组中的数值被剔除掉了

slice和splice虽然都是对于数组对象进行截取,但是二者还是存在明显区别,函数参数上slice和splice第一个参数都是截取开始位置,slice第二个参数是截取的结束位置(不包含),而splice第二个参数(表示这个从开始位置截取的长度),slice不会对原数组产生变化,而splice会直接剔除原数组中的截取数据!
slice不会改变原数组,splice会改变原数组

7、数值转换

JSON.parse() 转json对象
JSON.stringify() 转json字符串
String(),toString() 转字符串类型
Number parseInt()字符串转数值类型
split 字符串转数组
join 数组转字符串

8、什么是跨域,常见跨域

由于浏览器获取数据遵循同源策略,所以当访问非同源资源的时候,就需要跨域,常见的跨域方式有jsonp,a img src cors
同源策略:同协议,端口,域名的安全策略
jsonp原理
动态创建script标签,利用callpack回调函数获取值

function callbackFunction(){
  alert("回滚");
}
var script=document.createElement("script");
script.src="http://frergeoip.net.json/?callback=callbackFunction";

CORS的原理
当传输数据量比较大,get形式搞不定的时候,可以用到cors跨域,cors原理是定义一种跨域访问的机制,可以让ajax实现跨域访问。Cors允许一个域上的网络应用向另一个域提交跨域ajax请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。 Jsonp是get形式,承载的信息量有限,所以信息量较大的时cors是不二选择。

9、http协议:

http协议是定义服务器端和客户端之间文件传输的沟通方式
请求服务器上的资源,请求html css js 图片文件等
请求方法(所有方法全为大写)有多种,各个方法的解释如下:
GET (get) 请求获取Request-URI所标识的资源 --获取资源
POST (post) 在Request-URI所标识的资源后附加新的数据 ---传输资源
HEAD (head) 请求获取由Request-URI所标识的资源的响应消息报头 ---获取报文首部
PUT (put) 请求服务器存储一个资源,并用Request-URI作为其标识 ---更新资源
DELETE (delete) 请求服务器删除Request-URI所标识的资源 ---删除资源
TRACE (trace) 请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT(connect) 保留将来使用
OPTIONS(options) 请求查询服务器的性能,或者查询与资源相关的选项和需求
常见状态码:
200 请求成功
301 资源(网页等)被永久转移到其他url
404 请求的资源不存在
500 内部服务器错误

10、HTTP状态码

100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
200 OK 正常返回信息
201 Created 请求成功并且服务器创建了新的资源
202 Accepted 服务器已接受请求,但尚未处理
301 Moved Permanently 请求的网页已永久移动到新位置。
302 Found 临时性重定向。
303 See Other 临时性重定向,且总是使用 GET 请求新的 URI。
304 Not Modified 自从上次请求后,请求的网页未修改过。
400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。
401 Unauthorized 请求未授权。
403 Forbidden 禁止访问。
404 Not Found 找不到如何与 URI 相匹配的资源。
500 Internal Server Error 最常见的服务器端错误。
503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。

11、说说你对闭包的理解?

使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收

哪些操作会造成闭包内存泄漏?

不正当地使用可能会造成内存泄露(不再用到的内存,没有及时释放,就叫做内存泄露)

  • 内存泄漏指任何对象在您不再拥有或需要它之后仍然存在
  • setTimeout 的第⼀个参数使用字符串而非函数的话 ,会引发内存泄漏
  • 闭包 、控制台日志 、循环  (在两个对象彼此引用且彼此保留时 ,就会产生⼀个循环)

闭包用途
1缓存
设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
2 实现封装
可以先来看一个关于封装的例子,在person之外的地方无法访问其内部的变量,而通过提供闭包的形式来访问:

var person = function(){    
    //变量作用域为函数内部,外部无法访问    
    var name = "default";              
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
}();        
print(person.name);//直接访问,结果为undefined    
print(person.getName());    
person.setName("abruzzi");    
print(person.getName()); 

12、如何阻止事件冒泡?

ie:阻止冒泡ev.cancelBubble = true;
非IE ev.stopPropagation();

13、如何阻止默认事件?

(1)return false;(2) ev.preventDefault();

14、事件代理

事件代理是指,对父盒子绑定事件,然后子盒子触发事件,由于产生事件冒泡,导致父盒子事件也被触发,此时,在父盒子的时间处理函数中,可以通过srcElement或者target属性来获取目标时间,并对其进行相应的处理

15、添加 删除 替换 插入到某个节点的方法?

1)创建新节点
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
 
2)添加、移除、替换、插入
appendChild() //添加
removeChild() //移除
replaceChild() //替换
insertBefore() //插入
 
3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值
getElementById() //通过元素Id,唯一性

16、document load 和document ready的区别?

document.onload 是在结构和样式,外部js以及图片加载完才执行js
document.ready是dom树创建完成就执行的方法,原生种没有这个方法,jquery中有 $().ready(function)

17、Javascript的事件流模型都有什么?

“事件捕捉”:是从上往下,window,document,document.documentelment(获取的html) document,body 、……..目标元素

“事件冒泡”:是从下往上:反之

“DOM事件流”:三个阶段:事件捕捉、目标阶段、事件冒泡

image.png
Dom事件类:

Dom0  element.onclick=function(){}

DOM2 element.addEventlistener(‘click’,function(){},flase)

DOM3 element.addEventlistener(‘keyup’,function(){},flase)

Event.preventdefault()  阻止默认事件

Event.stoppropagation()  阻止冒泡

Event.currenTtarget()事件代理 

Event.target 当前被点击元素

18、Js事件循环机制/event loop?

用简单的流程解释时间循环机制
在了解什么是事件循环之前我们需要先行了解 javascript是一个单线程语言 和 javascript的事件分类

1. javascript是一个单线程语言

什么是单线程。举个例子:这就好像食堂打饭,如果食堂开设多个窗口,那么就可以有多个同学同时打饭。但如果食堂只有一个窗口,那么所有的同学都要在这一个窗口后面一个个排队打饭。单线程,就属于后者这个情况。同理,javascript中的所有任务都只有一条线程在处理。

显然,这种机制存在很大的弊端。如果我们有一个任务卡死了。那么后面所有的任务都无法被执行。或者有某个任务耗时很长,那么就会导致后面所有的任务都被延迟执行。在这样的环境下,javascript诞生了两个任务种类:同步任务和异步任务

2. javascript的任务分类

接上文,javascript中的所有任务被分为同步任务和异步任务两大类。

同步任务介绍:就是只要被扫描到,就可以被主线程马上执行的任务。(优先于所有异步任务)

异步任务介绍:即使被扫描到,也不会马上执行,异步任务会被压入异步任务队列中,等待主线程中的任务全部清空了,再被召唤执行。

常见的异步任务有如下几种

Promise.then() ---微任务

async/await ---Promise的语法糖 ---微任务

setTimeout() ---宏任务

setInterval() ---宏任务

...(还有更多,不常见的)

19、null和undefined的区别?

null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。
当声明的变量还未被初始化时,变量的默认值为undefined。 null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。

20、call() 和 .apply() 的区别和作用?

相同点:两个方法产生的作用是完全一样的,都是改变this指向的

不同点:方法传递的参数不同

Object.call(this,obj1,obj2,obj3)

Object.apply(this,arguments)

Apply()接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。

Call()方法的第一个参数与apply()相同,但传递的参数必须列举出来。

一、call,apply,bind的相同点:

  • 都是改变this指向的;
  • 第一个参数都是this要指向的对象;
  • 都可以利用后续参数传参;

二、call,apply,bind的区别:

  • call和bind的参数是依次传参,一一对应的;
  • 但apply只有两个参数,第二个参数为数组
  • call和apply都是对函数进行直接调用,而bind方法返回的仍是一个函数;

image.png

21、mvc和mvvm模式原理:

image.png

22、JS为什么要区分微任务和宏任务?

(1)js是单线程的,但是分同步异步
(2)微任务和宏任务皆为异步任务,它们都属于一个队列
(3)宏任务一般是:script,setTimeout,setInterval、setImmediate
(4)微任务:原生Promise
(5)遇到微任务,先执行微任务,执行完后如果没有微任务,就执行下一个宏任务,如果有微任务,就按顺序一个一个执行微任务

23、setTimeout和setInterval

//setTimeout是3秒后执行
 setTimeout(function(){
 alert(123)
 },3000)

//setInterval是每隔三秒执行一次,不断的重复执行
 setInterval(function(){
 alert(1121222)
 },3000)
//两个执行都是异步进行的

24、深拷贝浅拷贝

深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。

假设B复制了A,修改A的时候,看B是否发生变化:

如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)

如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值)
浅拷贝实现:

  var a = [1, 2, 3, 4, 5];
  var b = a;
  a[0] = 2
  console.log(a);//[2,2,3,4,5]
  console.log(b);//[2,2,3,4,5]  ////b会随着a的变化而变化

深拷贝实现:

  var a = [{"name":"weifeng"},{"name":"boy"}];
  var a_copy =[].concat(JSON.parse(JSON.stringify(a)));//深拷贝
  a_copy[1].name = "girl"
  console.log(a);//[{"name":"weifeng"},{"name":"boy"}]
  console.log(a_copy );//[{"name":"weifeng"},{"name":"girl"}]  

浅拷贝方法

  • 1.Object.assign()方法
    const obj2 = Object.assign({},obj1)
    
    缺点:这里就会发现使用Object.assign()只能深拷贝一级属性,二级以上的属性(引用类型)就是浅拷贝了
  • 2.扩展运算符...
    const obj2 = {...obj1}
    
    缺点:这个方法和上面使用Object.assign()方法使用的结果都是一样的。只能深拷贝一级属性,二级以上属性(引用类型)就是浅拷贝了
  • 3.Array.prototype.concat()
  • 4.Array.prototype.slice()

深拷贝方法

  • 1.写一个递归(推荐,比较完美的解决方案)
      function deepClone(oldData) {
          // Object 是对象或数组或null
          if(typeof oldData === 'object' && oldData !== null){
              let res = Array.isArray(oldData) ? [] : {};
              for (let k in oldData) {
                  if (oldData.hasOwnProperty(k)) {
                      res[k] = deepClone(oldData[k])
                  }
              }
              return res;
          }else {
              return oldData;
          }
      }
    
  • 2.JSON转换方法JSON.parse(JSON.stringify())
    const obj2 = JSON.parse(JSON.stringify(obj1)) 
    
    缺点:你看到这里就会发现数据类型是 function 和 undefined 情况下无法复制,其他的都可以进行深层次的拷贝
  • 3.jQuery.extend()
      const $ = require('jquery');
      var obj = { a: { a: 'july', b: 10 }, b: 2 };
      const obj1 = $.extend(true, {}, obj);
      console.log(obj.a.a == obj1.a.a); // false
    
  • 4.使用函数库lodash
      var _ = require('lodash');
      var obj = { a: { a: 'july', b: 10 }, b: 2 };
      var obj1 = _.cloneDeep(obj);
      console.log(obj.a.a == obj1.a.a); //false
    

25、重排重绘

回流(重排):
当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。
重绘:
当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
区别:
回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流 当页面布局和几何属性改变时就需要回流 比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变。

26、防抖和节流?

在前端开发的过程中,我们经常会需要绑定一些持续触发的事件,如 resize、scroll、mousemove keyup 等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数。

通常这种情况下我们怎么去解决的呢?一般来讲,防抖和节流是比较好的解决方案。
1、防抖:
指触发事件后在n秒后函数执行,如果在n秒内又触发了事件,则会重新计算函数执行时间。应用场景(适合多次事件只响应一次的情况):给按钮加防抖函数防止表单多次提交;判断scroll是否滑到底部;对于输入框连续输入进行AJAX验证时,用函数防抖能有效减少请求次数。
现给一个场景:现监听一个输入框,文字变化后触发change事件。若直接用keyup事件,则会频繁触发change事件。加了防抖后,用户输入结束或暂停时,才会触发change事件。
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text" id="input1">
</body>
<script>
    const input1 = document.getElementById('input1')
//1、不加防抖 ,会一直触发change事件
    input1.addEventListener('keyup', function(){
            console.log(input1.value)
    })

//2、简单实现防抖
    let timer = null
    input1.addEventListener('keyup', function(){
        if(timer){
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            //模拟触发change事件
            console.log(input1.value)
            //清空定时器
            timer = null
        }, 1000)
    })

//3、将防抖函数这个工具进行封装
    function debounce(fn, delay = 50){
        //timer是闭包中的,不能被别人修改
        let timer = null
        return function(){
            if(timer){
                clearTimeout(timer)
            }
            timer = setTimeout(() => {
                fn.apply(this, arguments)
                timer = null
            }, delay)
        }
    }
    input1.addEventListener('keyup', debounce(function(){ 
        console.log(input1.value)
    }, 600))
</script>
</html>

则封装后的防抖函数为:

function debounce(fn, delay = 50){
        let timer = null  //timer是闭包中的,不能被别人修改
        return function(){
            if(timer){
                clearTimeout(timer)
            }
            timer = setTimeout(() => {
                fn.apply(this, arguments)
                timer = null
            }, delay)
        }
    }

2、节流:
连续发送的事件在n秒内只执行一次函数。应用场景(适合大量事件按时间做平均分配触发):DOM元素拖拽;Canvas画笔功能。
现给一个场景:拖拽一个元素,要随时拿到该元素被拖拽的位置。若直接用drag事件,则会频繁触发,很容易导致卡顿。加了节流后,无论拖拽速度多快,都会每隔固定时间触发一次。
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #div1{
            border: 1px solid #ccc;
            width: 200px;
            height: 100px;
        }
    </style>
</head>
<body>
    <div id = "div1" draggable="true">可拖拽</div>
    <script>
        const div1 = document.getElementById('div1')
  
  //1、简单实现节流
        let timer = null
        div1.addEventListener('drag', function(e){
            if(timer){
                return
            }
            timer = setTimeout(() => {
                console.log(e.offsetX, e.offsetY)
                timer = null //定时器执行了,才让timer为空
            }, 1000)            
        })

  //2、将节流函数这个工具进行封装
        function throttle(fn, delay = 100){
            let timer = null
            return function(){ 
                if(timer){
                    return
                }
                timer = setTimeout(() => {
                    fn.apply(this, arguments)
                    timer = null
                },delay)
            }
        }
        div1.addEventListener('drag', throttle(function(e){ //形参e会传给throttle函数运行后返回的函数
                console.log(e.offsetX, e.offsetY)   
        },200))
    </script>
</body>
</html>

则封装后的节流函数为:

function throttle(fn, delay = 100){
            let timer = null
            return function(){ 
                if(timer){
                    return
                }
                timer = setTimeout(() => {
                    fn.apply(this, arguments)
                    timer = null
                },delay)
            }
        }

27、一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?

分为4个步骤:
(1),当发送一个URL请求时,不管这个URL是Web页面的URL还是Web页面上每个资源的URL,浏览器都会开启一个线程来处理这个请求,同时在远程DNS服务器上启动一个DNS查询。这能使浏览器获得请求对应的IP地址。
(2), 浏览器与远程Web服务器通过TCP三次握手协商来建立一个TCP/IP连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,而后服务器应答并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。
(3),一旦TCP/IP连接建立,浏览器会通过该连接向远程服务器发送HTTP的GET请求。远程服务器找到资源并使用HTTP响应返回该资源,值为200的HTTP响应状态表示一个正确的响应。
(4),此时,Web服务器提供资源服务,客户端开始下载资源。
请求返回后,便进入了我们关注的前端模块
简单来说,浏览器会解析HTML生成DOM Tree,其次会根据CSS生成CSS Rule Tree,而javascript又可以根据DOM API操作DOM
详情:从输入 URL 到浏览器接收的过程中发生了什么事情?

28、说说TCP传输的三次握手策略

为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送 后的情况置之不理,它一定会向对方确认是否成功送达。握手过程中使用了TCP的标志:SYN和ACK。 发送端首先发送一个带SYN标志的数据包给对方。接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。最后,发送端再回传一个带ACK标志的数据包,代表“握手”结束 若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包。

29、说说你对语义化的理解?

1,去掉或者丢失样式的时候能够让页面呈现出清晰的结构
2,有利于SEO:和搜索引擎建立良好沟通,有助于爬虫抓取更多的有效信息:爬虫依赖于标签来确定上下文和各个关键字的权重;
3,方便其他设备解析(如屏幕阅读器、盲人阅读器、移动设备)以意义的方式来渲染网页;
4,便于团队开发和维护,语义化更具可读性,是下一步吧网页的重要动向,遵循W3C标准的团队都遵循这个标准,可以减少差异化

30、你如何对网站的文件和资源进行优化?

期待的解决方案包括:
文件合并
文件最小化/文件压缩
使用 CDN 托管
缓存的使用(多个域名来提供缓存)
其他

31、请说出三种减少页面加载时间的方法?

1、压缩css、js文件
2、合并js、css文件,减少http请求
3、外部js、css文件放在最底下
4、减少dom操作,尽可能用变量替代不必要的dom操作

32、js延迟加载的方式有哪些?

defer和async、动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)、按需异步载入js

33、你有哪些性能优化的方法?

(详情请看雅虎14条性能优化原则)。
(1) 减少http请求次数:CSS Sprites, JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。
(2) 前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数
(3) 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。
(4) 当需要设置的样式很多时设置className而不是直接操作style。
(5) 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。
(6) 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。
(7) 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。

34、如何进行网站性能优化?

content 方面

  • 减少 HTTP 请求:合并文件 、  CSS 精灵 、   inline Image
  • 减少 DNS 查询:   DNS 缓存 、将资源分布到恰当数量的主机名
  • 减少 DOM 元素数量

Server 方面

  • 使用 CDN
  • 配置 ETag
  • 对组件使用 Gzip 压缩

Cookie 方面

  • 减⼩  cookie 大⼩

css 方面

  • 将样式表放到页面顶部
  • 不使用 CSS 表达式
  • 使用 <link> 不使用 @import

Javascript 方面

  • 将脚本放到页面底部
  • 将 javascript 和 css 从外部引⼊
  • 压缩 javascript 和 css
  • 删除不需要的脚本
  • 减少 DOM 访问

图片⽅面

  • 优化图片:根据实际颜色需要选择色深 、压缩
  • 优化 css 精灵
  • 不要在 HTML 中拉伸图片

35、异步加载和延迟加载

1.异步加载的方案: 动态插入script标签
2.通过ajax去获取js代码,然后通过eval执行
3.script标签上添加defer或者async属性
4.创建并插入iframe,让它异步执行js
5.延迟加载:有些 js 代码并不是页面初始化的时候就立刻需要的,而稍后的某些情况才需要的。

36、GET和POST的区别,何时使用POST?

GET:一般用于信息获取,使用URL传递参数,对所发送信息的数量也有限制,一般在2000个字符
POST:一般用于修改服务器上的资源,对所发送的信息没有限制。
GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值,也就是说Get是通过地址栏来传值,而Post是通过提交表单来传值。
然而,在以下情况中,请使用 POST 请求:
无法使用缓存文件(更新服务器上的文件或数据库)
向服务器发送大量数据(POST 没有数据量限制)
发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

37、平时如何管理你的项目?

先期团队必须确定好全局样式(globe.css),编码模式(utf-8) 等;
编写习惯必须一致(例如都是采用继承式的写法,单样式都写成一行);
标注样式编写人,各模块都及时标注(标注关键样式调用的地方);
页面进行标注(例如 页面 模块 开始和结束);
CSS跟HTML 分文件夹并行存放,命名都得统一(例如style.css);
JS 分文件夹存放 命名以该JS功能为准的英文翻译。

38、你如何优化自己的代码?

代码重用
避免全局变量(命名空间,封闭空间,模块化mvc…)
拆分函数避免函数过于臃肿
注释

39、什么是 FOUC(无样式内容闪烁)?你如何来避免 FOUC? 

FOUC - Flash Of Unstyled Content 文档样式闪烁
<style type="text/css" media="all">@import "../fouc.css";</style>

而引用CSS文件的@import就是造成这个问题的罪魁祸首。IE会先加载整个HTML文档的DOM,然后再去导入外部的CSS文件,因此,在页面DOM加载完成到CSS导入完成中间会有一段时间页面上的内容是没有样式的,这段时间的长短跟网速,电脑速度都有关系。

解决方法简单的出奇,只要在<head>之间加入一个<link>或者<script>元素就可以了。

40、网站重构的理解?

网站重构:在不改变外部行为的前提下,简化结构、添加可读性,而在网站前端保持一致的行为。也就是说是在不改变UI的情况下,对网站进行优化,在扩展的同时保持一致的UI。
对于传统的网站来说重构通常是:
表格(table)布局改为DIV+CSS
使网站前端兼容于现代浏览器(针对于不合规范的CSS、如对IE6有效的)
对于移动平台的优化
针对于SEO进行优化
深层次的网站重构应该考虑的方面
减少代码间的耦合
让代码保持弹性
严格按规范编写代码
设计可扩展的API
代替旧有的框架、语言(如VB)
增强用户体验
通常来说对于速度的优化也包含在重构中
压缩JS、CSS、image等前端资源(通常是由服务器来解决)
程序的性能优化(如数据读写)
采用CDN来加速资源加载
对于JS DOM的优化
HTTP服务器的文件缓存

41、什么叫优雅降级和渐进增强?

优雅降级:Web站点在所有新式浏览器中都能正常工作,如果用户使用的是老式浏览器,则代码会检查以确认它们是否能正常工作。由于IE独特的盒模型布局问题,针对不同版本的IE的hack实践过优雅降级了,为那些无法支持功能的浏览器增加候选方案,使之在旧式浏览器上以某种形式降级体验却不至于完全失效.
渐进增强:从被所有浏览器支持的基本功能开始,逐步地添加那些只有新式浏览器才支持的功能,向页面增加无害于基础浏览器的额外样

42、栈和队列的区别?

栈的插入和删除操作都是在一端进行的,而队列的操作却是在两端进行的。
队列先进先出,栈先进后出。
栈只允许在表尾一端进行插入和删除,而队列只允许在表尾一端进行插入,在表头一端进行删除

43、栈和堆的区别?

栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。
堆(数据结构):堆可以被看成是一棵树,如:堆排序;
栈(数据结构):一种先进后出的数据结构。

44、对前端界面工程师这个职位是怎么样理解的?它的前景会怎么样?

前端是最贴近用户的程序员,比后端、数据库、产品经理、运营、安全都近。
1、实现界面交互
2、提升用户体验
3、有了Node.js,前端可以实现服务端的一些事情
前端是最贴近用户的程序员,前端的能力就是能让产品从 90分进化到 100 分,甚至更好,
参与项目,快速高质量完成实现效果图,精确到1px;
与团队成员,UI设计,产品经理的沟通;
做好的页面结构,页面重构和用户体验;
处理hack,兼容、写出优美的代码格式;
针对服务器的优化、拥抱最新前端技术。

45、浏览器缓存

一、为什么需要缓存

在前端开发中,我们主要追求的是性能和用户体验。对于一个网站查看性能最简单的方式就是打开网站的速度。而一个好的缓存策略可以大大提升网站的性能。使得已经下载后的资源被重复利用。减少客户端和服务器之间的请求次数,减少带宽,减少网络负荷。

二、什么是缓存

对于web缓存,主要是针对一些web资源(html、 js、图片、数据等),就是介于web服务器和浏览器之间的文件数据副本。当我们第一次打开某一个网页时,浏览器向服务器发起请求,请求所需要的资源。如果我们使用了web缓存,当我们下一次再次访问该网站页面的时候,我们可以根据一些缓存策略,来决定是否直接使用缓存中的一些资源,还是再次向服务端发起请求,从而避免再次向服务器发起请求,减少客户端和服务器之间通信的时延。

三、缓存的作用

减少网络带宽的消耗 降低服务器压力 减少网络延时,加快页面打开速度。

浏览器缓存处理

1.强缓存(本地缓存) 浏览器本地在有效时间内强制缓存

使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。 强缓存策略可以通过两种方式来设置,分别是 http 头信息中的 Expires 属性和 Cache-Control 属性

2.弱缓存(协商缓存) 和服务器沟通,由服务器告知浏览器是否使用缓存

如果命中强制缓存,我们无需发起新的请求,直接使用缓存内容,如果没有命中强制缓存,设置了协商缓存,这个时候协商缓存就会发挥作用了。 上面已经说到了,命中协商缓存的条件有两个:max-age=xxx 过期了值为 no-store 使用协商缓存策略时,会先向服务器发送一个请求,如果资源没有发生修改,则返回一个304状态,让浏览器使用本地的缓存副本。如果资源发生了修改,则返回修改后的资源。 弱缓存也可以通过两种方式来设置,分别是http头信息中的Etag 和 Last-Modified 属性。

http标准其实是并没有强缓存、弱缓存名词,只是为了方便理解而构建出来的名词而已

在http里所谓的缓存,本质上只是浏览器和业务,根据不同的报文字段,做出不同的缓存动作而已,四种缓存字段如下 http缓存字段 1.Cache-Control 2.Expires 3.ETag / If-None-Match 4.Last-Modified / If-Modified-Since

1.通过响应头设置catch-control和max-age,指定该请求需要在浏览器缓存多久,在有效时间内重复请求,则无需再次访问服务器,直接从浏览器访问结果 模拟3秒请求数据 async cacheControlTest(){ //模拟3秒请求数据 await new Promise(resolve => { setTimeout(() => { resolve(); }, 3000); }) ctx.set('Cache-Control','public','max-age=10')//设置Cache-Control,内容是max-age=10 10秒 我们期望10秒内不会再重复访问服务端 10秒内都不会访问真实的数据,而是访问disk cache一毫秒,十秒后再重新访问真实的数据 }

2.Expires则是通过响应头设置Expires字段,指定缓存过期时间,在过期时间之前重复请求,则无需再次访问服务器,直接从浏览器获取结果, async cacheControlTest(){ //模拟3秒请求数据 await new Promise(resolve => { setTimeout(() => { resolve(); }, 3000); }) ctx.set('Expires',new Date(Date.now() * 10 * 1000).toUTCString())//在返回头设置Expires,过期时间设置为10秒后 我们期望10秒内不会再重复访问服务端 10秒内都不会访问真实的数据,而是访问disk cache一毫秒,十秒后再重新访问真实的数据 }

代码算法之类

46、数组去重indexOf

var arr=[1,1,2,2,3,4,5,7,8,9,6,4,6,2,]
var arr2=[]
for(var i=0;i<arr.length;i++){
    if(arr2.indexOf(arr[i])<0){
        arr2.push(arr[i])
    }
}
console.log(arr2) 

47、es6方法数组去重

let a = [1,5,6,3,8,0,5,7,0,4,2,7,5,4,5,9,22]
let b=new Set([...a])
console.log(b);
b=[...b];
console.log(b)

48、冒泡排序

var arr=[1,3,4,6,8,0,2,5,7,4,9,2];
var temp=0;
for (var i=0;i<arr.length;i++) {
    for(var j=0;j<arr.length-i;j++){
        if(arr[j]<arr[j+1]){
            temp=arr[j+1];
            arr[j+1]=arr[j];
            arr[j]=temp;
        }
    }
}
console.log(arr)

49、获取url中的参数

//测试地址:http://www.runobb.com/jqur/dfev.html?name=xiaohong&age=22
   function showWindowHref(){
        var sHref=window.location.href;
        var args=sHref.split('?');
        if(args[1]==sHref){
            return '';
        }
        var aa=args[1].split('&');
        var obj={}
        for (var i=0;i<aa.length;i++) {
            var bb=aa[i].split('=')
            obj[bb[0]]=bb[1]
        }
        return obj;             
    }

50、降维数组

//利用[].concat.apply实现降维  
    var arr=[[1,2],[3,4]];
    function Jw(obj){
        console.log(Array.prototype.concat.apply([],obj))
            return Array.prototype.concat.apply([],obj);
    }
    Jw(arr);
//如果concat方法的参数是一个元素,该元素会直接插入新数组中;如果参数是一个数组,该数组的各个元素将被插入到新数组中
    function reduceDimension(arr){
        let ret = [];
        for(let i=0;i<arr.length;i++){
            ret=ret.concat(arr[i])
        }
        console.log(ret)
        return ret;
    }
    reduceDimension(arr)  
//递归
    function reduceDimension(arr){
        let ret = [];
        let toArr = function(arr){
            arr.forEach(function(item){
                item instanceof Array ? toArr(item) : ret.push(item);
                //Array.isArray(item) ? toArr(item) : ret.push(item);
            });
        }
        toArr(arr);
        console.log(ret)
        return ret;
    }
    reduceDimension([1, 2, [3, 4, [5, 6]]])   
 //flat
     let list = [1, 2, 3, [4, [5]]];
     let res = list.flat(Infinity)
     console.log(res) // [1, 2, 3, 4, 5]

51、js判断一个字符串中出现次数最多的字符,统计这个次数

var str = 'asdfssaaasasasasaa';
var json = {};
for (var i = 0; i < str.length; i++) {
    if(!json[str.charAt(i)]){
       json[str.charAt(i)] = 1;
    }else{
       json[str.charAt(i)]++;
    }
};
var iMax = 0;
var iIndex = '';
for(var i in json){
    if(json[i]>iMax){
         iMax = json[i];
         iIndex = i;
    }
}
console.log('出现次数最多的是:'+iIndex+'出现'+iMax+'次');  //出现次数最多的是:a出现 9次
let string = 'kapilalipak';
const table={}; 
for(let char of string) {
    table[char]=table[char]+1 || 1;
}
// 输出
console.log(table)// {k: 2, a: 3, p: 2, i: 2, l: 2}

52、写一个function,清除字符串前后的空格。(兼容所有浏览器)

function trim(str) {
    if (str & typeof str === "string") {
        return str.replace(/(^s*)|(s*)$/g,""); //去除前后空白符
    }
}

53、如何用jquery禁用浏览器的前后进退按钮?

<script type="text/javascript" language="javascript">
$(document).ready(function() { 
     window.history.forward(1);
     //OR  
     window.history.forward(-1);  
});  
</script>

54、获取页面中所有的checkbox怎么做?(不使用第三方框架)

var inputs = document.getElementsByTagName("input");//获取所有的input标签对象
var checkboxArray = [];//初始化空数组,用来存放checkbox对象。
for(var i=0;i<inputs.length;i++){
  var obj = inputs[i];
  if(obj.type=='checkbox'){
     checkboxArray.push(obj);
  }
}

55、程序中捕获异常的方法?

try{
  
}catch(e){
  
}finally{
  
}

56、js排序

升序降序排序函数sortNumber

const arr1 = [6,1,2,3,4];
function sortNumber(a,b){
     return b-a;
}
arr1.sort(sortNumber);
console.log(arr1)
// [6, 4, 3, 2, 1]

按照flag排序,为true的在前面显示

const arr2 = [
            { id: 10, flag: true },
            { id: 5, flag: false },
            { id: 6, flag: true },
            { id: 9, flag: false }
];
const r = arr2.sort((a, b) => b.flag - a.flag);
console.log(r);
// [
//     { id: 10, flag: true },
//     { id: 6, flag: true },
//     { id: 5, flag: false },
//     { id: 9, flag: false }
// ]

喜欢的小伙伴点个赞吧,感觉对身边人有帮助的,麻烦动动手指,分享一下。感谢各位花时间阅读完,同时也感谢各位的点赞和分享。

其他热门文章

  1. es6相关总结
  2. JS数组reduce()方法
  3. js数组sort()方法
  4. 设置token到请求头
  5. es6扩展运算符三个点(...)

后续会持续更文

  1. HTML面试总结
  2. css面试总结
  3. vue面试总结
  4. es6面试相关总结
  5. 小程序面试总结

最后会整合整理到一起~