前端面试题总结

HTML

1、你是如何理解web语义化的?

Web语义化是指使用恰当语义的html标签、class类名等内容,让页面具有良好的结构与含义,从而让人和机器都能快速理解网页内容。语义化的web页面一方面可以让机器在更快读懂网页的内容,然后将收集汇总的信息进行分析,结果为人类所用;另一方面它可以让开发人员读懂结构和用户以及屏幕阅读器(如果访客有视障)能够读懂内容。 简单来说就是利于 SEO,便于阅读维护理解。

总结起来就是:
正确的标签做正确的事情
页面内容结构化
无CSS样子时也容易阅读,便于阅读维护和理解
便于浏览器、搜索引擎解析。 利于爬虫标记、利于SEO

HTML语义化让机器读懂网页内容,CSS语义化是让人读懂网页内容。

HTML语义化

对于HTML体系而言,Web语义化是指使用语义恰当的标签,使页面有良好的结构,让页面元素有含义,便于被浏览器、搜索引擎解析、利于SEO。
HTML语义化是反对大篇幅使用无语义化的div+span+class,而鼓励使用HTML定义好的语义化标签。
比如用<header>标识页面头部内容,<main>表示中间主要内容,<section>表示一个章节,<nav>表示导航条,<footer>表示页尾,
article 代表一个在文档,页面或者网站中自成一体的内容,其目的是为了让开发者独立开发或重用。
除了它的内容,article会有一个标题(通常会在header里),一个footer页脚。

<article>
    <h1>你好,我是这边文章的标题</h1>
    <p>你好,我是文章的内容</p>
    <footer>
        <p>最终解释权归XXX所有</p>
    </footer>
</article>

CSS语义化

CSS语义就是class和ID命名的语义。指用易于理解的名称对html标签附加的class或id命名。如果说HTML语义化标签是给机器看的,那么CSS命名的语义化就是给人看的。

<div class="site-header">

</div>
<div class="page-main home-main">

</div>
<div class="site-footer">

</div>

URL语义化

url语义化,可以使得搜索引擎或者爬虫更好地理解当前url所在目录所需要表达的内容;而对于用户来说,通过url也可以判断上一级目录或者下一级目录想要表示的内容,可以提高用户体验。

https://mall.midea.com/index.php/category?id=10049&addr_code=440000,440100,440103
https://mall.midea.com/category?id=10049&addr_code=440000,440100,440103

这两个url指向的是同一个资源,但是显然第二个url对于用户和搜索引擎更加友好。

url语义化可以从以下标准来衡量:

  1. url简化,规范化:url里的名词如果包含两个单词,那么就用下划线_ 连接。
  2. 结构化,语义化:此处的品类搜索我们用语义化单词category表示
  3. 采用技术无关的url:第一个链接中的index.php这种就不应该出现在用户侧的url里。

参考链接:https://juejin.im/entry/5ab5f229518825558a069304

meta viewport 是做什么用的,怎么写?

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />

这段代码的意思是,让viewport的宽度等于物理设备上的真实分辨率,不允许用户缩放。
width:控制 viewport 的大小,可以指定的一个值,如果 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。
height:和 width 相对应,指定高度。
initial-scale:初始缩放比例,也即是当页面第一次 load 的时候缩放比例。
maximum-scale:允许用户缩放到的最大比例。
minimum-scale:允许用户缩放到的最小比例。
user-scalable:用户是否可以手动缩放

click在ios上有300ms延迟,原因及如何解决?

(1)粗暴型,禁用缩放
<meta name="viewport" content="width=device-width, user-scalable=no">

(2)利用FastClick,其原理是:
检测到touchend事件后,立刻出发模拟click事件,并且把浏览器300毫秒之后真正出发的事件给阻断掉

你用过哪些 HTML 5 标签?

参考:https://developer.mozilla.org/zh-CN/docs/Web/Guide/HTML/HTML5/HTML5_element_list

首先html5为了更好的实践web语义化,增加了header,footer,nav,aside,section等语义化标签,在表单方面,为了增强表单,为input增加了color,emial,data ,range等类型,在存储方面,提供了sessionStorage,localStorage,和离线存储(已废弃),通过这些存储方式方便数据在客户端的存储和获取,在多媒体方面规定了音频和视频元素audio和vedio,另外还有地理定位,canvas画布,拖放,多线程编程的web worker和websocket协议

响应式布局常用解决方案

参考:https://github.com/forthealllight/blog/issues/13

CSS

link标签和@import标签的区别

link属于html标签,而@import是css提供的
页面被加载时,link会同时被加载,而@import引用的css会等到页面加载结束后加载。
link是html标签,因此没有兼容性,而@import只有IE5以上才能识别。
link方式样式的权重高于@import的。

两种盒模型分别说一下

IE模型和标准模型唯一的区别是内容计算方式的不同:


IE模型元素宽度=width + margin(左右)(即width已经包含了padding和border值),高度计算相同

标准模型元素宽度=width + margin(左右) + padding(左右) + border(左右),高度计算相同

通过css3新增的属性 box-sizing: content-box | border-box分别设置盒模型为标准模型(content-box)和IE模型(border-box)。

.content-box {
  box-sizing:content-box;
  width: 100px;
  height: 50px;
  padding: 10px;
  border: 5px solid red;
  margin: 15px;
}
.content-box设置为标准模型,它的元素宽度width=130px。
.border-box {
  box-sizing: border-box;
  width: 100px;
  height: 50px;
  padding: 10px;
  border: 5px solid red;
  margin: 15px;
}
.border-box设置为IE模型,它的元素宽度width=content + 2 padding + 2 border = 70px + 2 10px + 2 5px = 100px。

总之:IE模型border-box你在css中width和height设置了多大,这个盒子宽高就有多大,这其中已经包括了padding和border。所以这里盒子的宽度是70+10+10+5+5=100,content+2个padding+2个border
而标准模型content-box你在css中width和height设置的只是content的大小,不包括padding和border。所以这里盒子的宽度是100+10+10+5+5=130,content+2个padding+2个border
就是表示width的方式不一样,标准盒子width表示的就是content,不包括padding和border;IE盒子width表示了整个盒子的宽度,这其中已经包括了padding和border

BFC

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。
可以把它理解成是一个独立的容器,并且这个容器的里box的布局,与这个容器外的毫不相干

BFC规则:
1、内部的Box会在垂直方向,一个接一个地放置。
2、Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
3、每个元素的左外边缘(margin-left), 与包含块的左边(contain box left)相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。除非这个元素自己形成了一个新的BFC。
4、BFC的区域不会与float box重叠。
5、BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
6、计算BFC的高度时,浮动元素也参与计算

怎样才能形成BFC
1、根元素或其它包含它的元素
2、浮动 (元素的 float 不是 none)
3、绝对定位的元素 (元素具有 position 为 absolute 或 fixed)
4、非块级元素具有 display: inline-block,table-cell, table-caption, flex, inline-flex
5、块级元素具有overflow ,且值不是 visible

BFC作用
1.清除浮动

<div class="wrap">
<section>1</section>
<section>2</section>
</div>
.wrap {
  border: 2px solid yellow;
  width: 250px;
}
section {
  background-color: pink;
  float: left;
  width: 100px;
  height: 100px;
}

可以看到,由于子元素都是浮动的,受浮动影响,边框为黄色的父元素的高度塌陷了。


图片.png

解决方案:为 .wrap 加上 overflow: hidden;使其形成BFC,根据BFC规则第六条,计算高度时就会计算float的元素的高度,达到清除浮动影响的效果。


图片.png

2.布局:自适应两栏布局
<div>
<aside></aside>
<main>我是好多好多文字会换行的那种蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤蛤</main>
</div>
div {width: 200px;}
aside {
  background-color: yellow;
  float: left;
  width: 100px;
  height: 50px;
}
main {
  background-color: pink;
}

可以看到右侧元素的一部分跑到了左侧元素下方。


图片.png

解决方案:为main设置 overflow: hidden; 触发main元素的BFC,根据规则第4、5条,BFC的区域是独立的,不会与页面其他元素相互影响,且不会与float元素重叠,因此就可以形成两列自适应布局


图片.png
  1. 防止垂直margin合并(外边距折叠)
<section class="top">1</section>
<section class="bottom">2</section>
section {
  background-color: pink;
  margin-bottom: 100px;
  width: 100px;
  height: 100px;
}
.bottom {
  margin-top: 100px;
}
图片.png

可以看到,明明.top和.bottom中间加起来有200px的margin值,但是我们只能看到100px。这是因为他们的外边距相遇发生了合并。

怎样解决:为其中一个元素的外面包裹一层元素。并为这个外层元素设置 overflow: hidden;,使其形成BFC。因为BFC内部是一个独立的容器,所以不会与外部相互影响,可以防止margin合并。


图片.png
<section class="top">1</section>
<div class="wrap">
<section class="bottom">2</section>
</div>
.wrap {
  overflow: hidden;
}

参考:https://segmentfault.com/a/1190000009545742
https://juejin.im/post/59b73d5bf265da064618731d

css3动画

https://juejin.im/post/5a424a796fb9a045023be66c

清除浮动

 .clearfix:after{
     content: '';
     display: block; /*或者 table*/
     clear: both;
 }
 .clearfix{
     zoom: 1; /* IE 兼容*/
 }
/* slightly enhanced, universal clearfix hack */
.clearfix:after {
    visibility: hidden;
    display: block;
    font-size: 0;
    content: " ";
    clear: both;
    height: 0;
}
.clearfix { display: inline-block; }
/* start commented backslash hack \*/
* html .clearfix { height: 1%; }
.clearfix { display: block; }
/* close commented backslash hack */

inline-block、inline和block的区别;为什么img是inline还可以设置宽高

block元素会独占一行,多个block元素会各自新起一行。默认情况下,block元素宽度自动填满其父元素宽度。
block元素可以设置width,height属性。块级元素即使设置了宽度,仍然是独占一行。
block元素可以设置margin和padding属性。

inline元素不会独占一行,多个相邻的行内元素会排列在同一行里,直到一行排列不下,才会新换一行,其宽度随元素的内容而变化。
inline元素设置width,height属性无效。
inline元素的margin和padding属性,水平方向的padding-left, padding-right, margin-left, margin-right都产生边距效果;但竖直方向的padding-top, padding-bottom, margin-top, margin-bottom不会产生边距效果。

inline-block:简单来说就是将对象呈现为inline对象,但是对象的内容作为block对象呈现。之后的内联对象会被排列在同一行内。比如我们可以给一个link(a元素)inline-block属性值,使其既具有block的宽度高度特性又具有inline的同行特性。

垂直居中

<div class="parent">
    <div class="child">
      一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
    </div>
 </div>

1、flex

.parent{
  height: 600px;
  border: 3px solid red;
  display: flex;
  justify-content: center;
  align-items: center;
}
.child{
  border: 3px solid green;
  width: 300px;
}
图片.png

2、块级元素知道高度

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  height: 100px;
  margin-top: -50px; /* account for padding and border if not using box-sizing: border-box; */
}

3、块级元素不知道高度

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

4、absolute margin auto

.parent{
  height: 600px;
  border: 1px solid red;
  position: relative;
}
.child{
  border: 1px solid green;
  position: absolute;
  width: 300px;
  height: 200px;
  margin: auto;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

参考:https://css-tricks.com/centering-css-complete-guide/

圣杯布局和双飞翼布局

参考:https://www.jianshu.com/p/81ef7e7094e8

flex怎么用

参考:https://juejin.im/post/58e3a5a0a0bb9f0069fc16bb

CSS 选择器优先级

!impotant>行内样式>id样式>class样式
class样式中最越靠下的优先级越高,和class="i1 i2 i3"这里顺序没关系

JS

ES 6 语法知道哪些,分别怎么用?

https://fangyinghang.com/es-6-tutorials/
https://es6.ruanyifeng.com/#docs/let

DOM

事件级别

DOM0 element.onclick=function(){}
DOM2 element.addEventListener('click',function(){},false)
true:捕获事件 false:冒泡事件
DOM3 element.addEventListener('keyup',function(){},false)

事件委托

简介:事件委托指的是,不在事件的发生地(直接dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素DOM的类型,来做出不同的响应。
举例:最经典的就是ul和li标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在li标签上直接添加,而是在ul父元素上添加。
好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。

事件模型

捕获和冒泡

事件流

事件流

DOM事件流

点击一个目标元素,响应的顺序是从window到document到html....最后从到目标元素
时间捕获过程:

    var ev = document.getElementById('ev')

    window.addEventListener('click', function() {
        console.log('windown')
    }, true)   //true是捕获,false是冒泡

    document.addEventListener('click', function() {
        console.log('document')
    }, true)

    document.documentElement.addEventListener('click', function() {
        console.log('html')
    }, true)

    document.body.addEventListener('click', function() {
        console.log('body')
    }, true)

    ev.addEventListener('click', function () {  
        console.log('ev')
    },true)
响应顺序

事件冒泡

当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window ,冒泡过程把上面true改成false

阻止事件冒泡:

function preventBubble(event){
            var e=arguments.callee.caller.arguments[0]||event; //若省略此句,下面的e改为event,IE运行可以,但是其他浏览器就不兼容
            if (e && e.stopPropagation) {
                e.stopPropagation();
            } else if (window.event) {
                window.event.cancelBubble = true;
            }
        }

DOM事件类

Event对象常见应用
event.preventDefault() 阻止默认事件
event.stopPropagation 阻止冒泡
event.stopImmediatePropagation() 事件响应优先级,优先触发哪个事件
event.currentTarget 获取到的是发起事件的标签元素
event.target 当前被点击的元素

自定义事件

var eve = new Event('custome');
ev.addEventListener('custome',function(){
    console.log('custome');
});
ev.dispatchEvent(eve);

mouseover和mouseenter的区别

mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave

JS中的垃圾回收机制

https://segmentfault.com/a/1190000018605776?utm_source=tag-newest

Promise、Promise.all、Promise.race 分别怎么用

Promise用法
function fn(){
     return new Promise((resolve, reject)=>{
         成功时调用 resolve(数据)
         失败时调用 reject(错误)
     })
 }
 fn().then(success, fail).then(success2, fail2)
Promise.all 用法
Promise.all([promise1, promise2]).then(success1, fail1)

promise1和promise2都成功才会调用success1

Promise.race 用法
Promise.race([promise1, promise2]).then(success1, fail1)

promise1和promise2只要有一个成功就会调用success1

函数防抖和函数节流

防抖

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。等于就是动作操作完再执行回调

function debounce(fn, delay) {
    let timer; // 维护一个 timer
    return function () {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments); // 用apply指向调用debounce的对象,相当于this.fn(arguments);
        }, delay)
    }
}

//测试用例
// test
function testDebounce(e, content) {
    console.log(e, content);
}
let testDebounceFn = debounce(testDebounce, 1000); // 防抖函数
document.onmousemove = function (e) {
    testDebounceFn(e, 'debounce'); // 给防抖函数传参
}
函数防抖的应用场景

连续的事件,只需触发一次回调的场景有:
搜索框搜索输入。只需用户最后一次输入完,再发送请求
手机号、邮箱验证输入检测
窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

节流

每隔一段时间,只执行一次函数。

function throttle(fn, delay) {
    let timer;
    return function () {
        if (timer) {
            return;
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            timer = null; // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器
        }, delay)
    }
}

//测试用例
function testThrottle(e, content) {
    console.log(e, content);
}
let testThrottleFn = throttle(testThrottle, 1000); // 节流函数
document.onmousemove = function (e) {
    testThrottleFn(e, 'throttle'); // 给节流函数传参
}
函数节流的应用场景

间隔一段时间执行一次回调的场景有:
滚动加载,加载更多或滚到底部监听
谷歌搜索框,搜索联想功能
高频点击提交,表单重复提交
参考:https://segmentfault.com/a/1190000018445196

手写ajax

function ajax(url, successFn){
  const xhr= new XMLHttpRequest()
  xhr.open('GET', url, true);
  xhr.onreadystatechange = function () {
   if(xhr.readyState === 4 && xhr.status === 200) {
       successFn(xhr.responseText);
   }
  }
  xhr.send();
}


// 结合Promise
function ajax(url) {
  const p = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText));
        } else if (xhr.status === 404) {
          reject(new Error("404 NOT FOUND"));
        }
      }
    }
    xhr.send(null)
  })
  return p
}

这段代码里的 this 是什么

fn()
this => window/global

obj.fn()
this => obj

fn.call(xx)
this => xx

fn.apply(xx)
this => xx

fn.bind(xx)
this => xx

new Fn()
this => 新的对象

fn = ()=> {}
this => 外面的 this
有参数的this就是参数

func(p1, p2) 等价于
func.call(undefined, p1, p2)

调用函数用func.call(context, p1, p2)
里面的context就是this

function func(){
  console.log(this)
}

func()

按理说打印出来的 this 应该就是 undefined 了吧,但是浏览器里有一条规则:
如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)
因此上面的打印结果是 window。

var obj = {
  foo: function(){
    console.log(this)
  }
}

var bar = obj.foo
obj.foo() // 转换为 obj.foo.call(obj),this 就是 obj
bar() 
// 转换为 bar.call()
// 由于没有传 context
// 所以 this 就是 undefined
// 最后浏览器给你一个默认的 this —— window 对象

参考:https://zhuanlan.zhihu.com/p/23804247

闭包

function f1(){
  var n=999;
  function f2(){
    alert(n);
  }
  return f2;
}
var result=f1();
result(); // 999

闭包就是能够读取其他函数内部变量的函数。

var f1 = (function(){
       var n=999;
  function f2(){
    alert(n);
  }
  return f2;
})()
f1();//999
    var Conf = (function () {
        var conf = {
            NAME: 'iwen',
            AGE: 18
        }
        return {
            get: function (name) {
                return conf[name] ? conf[name] : null;
            }
        }
    })();
    console.log(Conf.get('NAME'));
        

    var Ftr = (() =>{
        var conf = {
            NAME: 'iwen',
            AGE: 18
        }
        return {
            get: (name) => {
                return conf[name] ? conf[name] : null;
            }
        }
    })()
    console.log(Ftr.get('NAME'));

立即执行函数

(function(){
  alert('这是匿名函数!')
})()

立即执行函数作用就是创建一个独立作用域,不污染全局变量

什么是JSONP,什么是CORS,什么是跨域

同源:协议、域名、端口,三者必须一致
JSONP
请求方:frank.com 的前端程序员(浏览器)
响应方:jack.com 的后端程序员(服务器)

1、请求方创建 script,src 指向响应方,同时传一个查询参数 ?callbackName=yyy
2、响应方根据查询参数callbackName,构造形如
yyy.call(undefined, '你要的数据')
yyy('你要的数据')
这样的响应
3、浏览器接收到响应,就会执行 yyy.call(undefined, '你要的数据')
4、那么请求方就知道了他要的数据

这就是 JSONP
因为<script>可绕过跨域限制,服务器可以任意动态拼接数据返回,所以<script>可以获得跨域的数据,只有服务器端愿意返回。

约定:

1.callbackName -> callback
2.yyy -> 随机数 frank12312312312321325()
3.jQuery 用法

$.ajax({
 url: "http://jack.com:8002/pay",
 dataType: "jsonp",
 success: function( response ) {
     if(response === 'success'){
     amount.innerText = amount.innerText - 1
     }
 }
 })

CORS
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。
什么是跨域
简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象,跨域就是为了实现这个需求。
参考:https://www.cnblogs.com/hustskyking/archive/2013/03/31/CDS-introduce.html
跨域参考:https://segmentfault.com/a/1190000015597029
https://segmentfault.com/a/1190000011145364

深拷贝与浅拷贝

参考:https://segmentfault.com/a/1190000008637489

如何用正则实现 trim()

trim()的作用是去除字符串头尾的空格

String.prototype.trim = function(){
    return this.replace(/^\s+|\s+$/g, '')
}
//或者 
function trim(string){
    return string.replace(/^\s+|\s+$/g, '')
}

不用 class 如何实现继承?用 class 又如何实现?

不用class

function Animal(color){
     this.color = color
 }
 Animal.prototype.move = function(){} // 动物可以动
 function Dog(color, name){
     Animal.call(this, color) // 或者 Animal.apply(this, arguments)
     this.name = name
 }
 // 下面三行实现 Dog.prototype.__proto__ = Animal.prototype
 function temp(){}
 temp.prototye = Animal.prototype
 Dog.prototype = new temp()

 Dog.prototype.constuctor = Dog // 这行看不懂就算了,面试官也不问
 Dog.prototype.say = function(){ console.log('汪')}

 var dog = new Dog('黄色','阿黄')

用class

class Animal{
     constructor(color){
         this.color = color
     }
     move(){}
 }
 class Dog extends Animal{
     constructor(color, name){
         super(color)
         this.name = name
     }
     say(){}
 }

如何实现数组去重

//使用indexOf
var array = [1, 1,1,1,1,1, '1'];
    function unique(array) {
        var res=[]
        for(var i=0;i<array.length;i++){
            if(res.indexOf(array[i]) === -1){//一开始没有就为-1,后面有了就返回索引位置0
                res.push(array[i])
            }
        }
        return res
    }
    console.log(unique(array));
//排序后去重
var array = [100, 9,1,5,1,1,3,1, '1'];
    function unique(array) {
        var res = [];
        var sortedArray = array.concat().sort();
        res.push(sortedArray[0])
        console.log(sortedArray)
        for (var i = 0, len = sortedArray.length-1; i < len; i++) {
            if (sortedArray[i+1] !== sortedArray[i]) {
                console.log(sortedArray[i+1])
                res.push(sortedArray[i+1])
            }
        }
        return res;
    }
    console.log(unique(array));

//es6
var array = [1,1,1,1,'1']
function unique(array){
    return Array.from(new Set(array))
}
console.log(unique(array))

//简化
var unique = (a) => [...new Set(a)]

手写promise

    function Promise(executor){
        let self = this
        self.status = 'pending'
        self.value = undefined //接受成功的值
        self.reason = undefined //接受失败回调传递的值
        function resolve(value){
            if(self.status === 'pending'){
                self.value = value //保存成功原因
                self.status = 'fulfilled'
            }
        }
        function reject(reason) {  
            if(self.status === 'pending'){
                self.reason = reason //保存失败结果
                self.status = 'rejected' 
            }
        }
        try{
            executor(resolve,reject)
        }catch(e){
            reject(e) // 捕获时发生异常,就直接失败
        }
          
    }
    Promise.prototype.then = function (onFufiled, onRejected) {
        let self = this;
        if(self.status === 'resolved'){
            onFufiled(self.value);
        }
        if(self.status === 'rejected'){
            onRejected(self.reason);
        }
    }
    module.exports = Promise;

事件委托

事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素;

ul.addEventListener('click', function(e){
     if(e.target.tagName.toLowerCase() === 'li'){
         fn() // 执行某个函数
     }
 })

HTTP

在地址栏里输入一个URL,到这个页面呈现出来,中间会发生什么

1、首先需要找到这个URL的服务器ip,为了寻找这个ip,浏览器首先会寻找缓存,查看缓存中是否有记录,缓存的查找记录为:浏览器缓存-》系统缓存-》路由器缓存,缓存中没有则查找系统的hosts文件中是否有记录,如果没有则查询DNS服务器,得到服务器的ip地址
2、浏览器根据这个ip以及相应的端口号,构造一个http请求,这个请求报文会包括这次请求的信息,主要是请求方法,请求说明和请求附带的数据,并将这个http请求封装在一个tcp包中,这个tcp包会依次经过传输层,网络层,数据链路层,物理层到达服务器,服务器解析这个请求来作出响应,返回相应的html给浏览器
3、浏览器根据这个html来构建DOM树,根据css来构建CSSOM树,最后渲染页面
4、在这些所有的请求中我们还需要关注的就是缓存,缓存一般通过Cache-Control、Last-Modify、Expires等首部字段控制。 Cache-Control和Expires的区别在于Cache-Control使用相对时间,Expires使用的是基于服务器 端的绝对时间,因为存在时差问题,一般采用Cache-Control,在请求这些有设置了缓存的数据时,会先 查看是否过期,如果没有过期则直接使用本地缓存,过期则请求并在服务器校验文件是否修改,如果上一次 响应设置了ETag值会在这次请求的时候作为If-None-Match的值交给服务器校验,如果一致,继续校验 Last-Modified,没有设置ETag则直接验证Last-Modified,再决定是否返回304(过期无改动返回304)。

缓存过期无改动,服务器返回304,浏览器继续使用缓存文件
缓存过期有改动,重新下载服务器文件
缓存没过期无改动,浏览器继续使用缓存文件
缓存没过期有改动,重新下载服务器文件

简单说就是:
DNS解析
TCP连接
发送HTTP请求
服务器处理请求并返回HTTP报文
浏览器解析渲染页面
连接结束

HTTP状态码

200 OK - 请求成功
301 Moved Permanently - 资源(网页等)被永久转移到其它URL
400 Bad Request - 客户端请求的语法错误,服务器无法理解
401 Unauthorized - 请求要求用户的身份认证
403 Forbidden - 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found - 请求的资源(网页等)不存在
500 Internal Server Error - 内部服务器错误
501 Not Implemented - 服务器不支持请求的功能,无法完成请求
502 Bad Gateway - 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应

GET 和 POST 的区别

  1. GET使用URL或Cookie传参,而POST将数据放在BODY中,这个是因为HTTP协议用法的约定。并非它们的本身区别。
  2. GET方式提交的数据有长度限制,则POST的数据则可以非常大
  3. POST比GET安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。
  4. GET和POST最大的区别主要是GET请求是幂等性的,POST请求不是。(幂等性:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。)
  5. get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留。

就一个区别:语义——GET 用于获取资源,POST 用于提交资源。

Cookie V.S. LocalStorage V.S. SessionStorage V.S. Session

Cookie V.S. LocalStorage
主要区别是 Cookie 会被发送到服务器,而 LocalStorage 不会
Cookie 一般最大 4k,LocalStorage 可以用 5Mb 甚至 10Mb(各浏览器不同)

LocalStorage V.S. SessionStorage
LocalStorage 一般不会自动过期(除非用户手动清除),而 SessionStorage 在回话结束时过期(如关闭浏览器)

Cookie V.S. Session
Cookie 存在浏览器的文件里,Session 存在服务器的文件里
Cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
Session 是基于 Cookie 实现的,具体做法就是把 SessionID 存在 Cookie 里
Session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。
单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

HTTP请求方法

https://www.runoob.com/http/http-methods.html

HTTP 缓存有哪几种

ETag 是通过对比浏览器和服务器资源的特征值(如MD5)来决定是否要发送文件内容,如果一样就只发送 304(not modified)
Expires 是设置过期时间(绝对时间),但是如果用户的本地时间错乱了,可能会有问题
CacheControl: max-age=3600 是设置过期时长(相对时间),跟本地时间无关。
参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ

框架 Vue

watch 和 computed 和 methods 区别是什么?

computed是计算属性,methods是方法,watch是监听
computed 和 methods 相比,最大区别是 computed 有缓存:如果 computed 属性依赖的属性没有变化,那么 computed 属性就不会重新计算。methods 则是看到一次计算一次。
watch 和 computed 相比,computed 是计算出一个属性(废话),而 watch 则可能是做别的事情(如上报数据)

Vue 有哪些生命周期钩子函数?分别有什么用?

vue生命周期

vue生命周期钩子

一般是在mounted请求数据
参考:https://juejin.im/entry/5aee8fbb518825671952308c

Vue 如何实现组件间通信?

父子组件:使用 v-on 通过事件通信
爷孙组件:使用两次 v-on 通过爷爷爸爸通信,爸爸儿子通信实现爷孙通信
任意组件:使用 eventBus = new Vue() 来通信,eventBus.on 和 eventBus.emit 是主要API
任意组件:使用 Vuex 通信

Vue响应式原理

在Javascript中,侦测一个对象的变化有两种办法:使用Object.defineProperty和ES6的Proxy,这就是进行数据劫持或数据代理
Vue 通过Object.defineProperty来将对象的key转换成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性。如果是删除属性,我们可以用vm.$delete实现。
新增属性,该怎么办呢?
1)可以使用 Vue.set(location, a, 1) 方法向嵌套对象添加响应式属性;
2)也可以给这个对象重新赋值,比如data.location = {...data.location,a:1}
参考:https://blog.fundebug.com/2019/07/10/responsive-vue/
总结:
在 new Vue() 后, Vue 会调用_init 函数进行初始化,也就是init 过程,在 这个过程Data通过Observer转换成了getter/setter的形式,来对数据追踪变化,当被设置的对象被读取的时候会执行getter 函数,而在当被赋值的时候会执行 setter函数。
当外界通过Watcher读取数据时,会触发getter从而将Watcher添加到依赖中。
在修改对象的值的时候,会触发对应的setter, setter通知之前依赖收集得到的 Dep 中的每一个 Watcher,告诉它们自己的值改变了,需要重新渲染视图。这时候这些 Watcher就会开始调用 update 来更新视图。

JS面试题:

1、以下代码输出什么? 为什么?

var a = {n:1}; 
var b = a;  
a = {n:2}; 
a.x = a ;
console.log(a.x);
console.log(b.x);
a.x ---> {n:2,x:a};
b.x ---> undefined;

解答:a的值很清晰了,a第二次赋值以后变成了{n:2},随后添加了x属性指向自身。而对于b,在a第二次赋值以后,由于js中给变量赋值为object类型时,变量中存储的是对这个object的引用。此时,a指向{n:2} ,而b指向了{n:1} ,a和b指向不同的对象,因此,在a上添加属性对于b无影响,b.x自然就是undefined。

var a = {n:1}; 
var b = a;  
a.x = a = {n:2}; 
console.log(a.x);
console.log(b.x);
a.x ---> undefined ;
b.x ---> {n:2};

解答:第三句里的主要难点在js运算符的优先级,访问属性、调用方法运算符"."的优先级高于赋值运算符。因此执行顺序是

1、给a添加属性x,此时a,b都是 { n:1,x:undefined }a.x 运算后的结果即为这个object(可以说也就是b)的x属性值。
2、把{n:2}赋值给a,此时a是 {n:2} ,b是{ n:1,x:undefined }。这个时候a.x 已经运算完了,不会再与a产生任何关系,a.x依旧代表那个n为1对象的x属性值,和a已经没关系了。
3、把{n:2}赋值给a.x 也就是 { n:1,x:undefined }这个对象的x属性,这个时候b依旧指向这个object,因此此时,a是{n:2},b是{n:1,x:{n:2}}

2、只有声明本身会被提升

a = 2;
var a;
console.log( a ); //2
// 这里只有var a被提升了,a=2还是在console.log后面,所以读不到
console.log( a );
var a = 2;  //undefined

3、函数优先

foo(); // 1
var foo;
function foo() {
  console.log( 1 );
}
foo = function() {
  console.log( 2 );
};

会输出 1 而不是 2 !这个代码片段会被引擎理解为如下形式:

function foo() {
  console.log( 1 );
}
foo(); // 1
foo = function() {
  console.log( 2 );
};

可参考牛客前端面试题汇总:https://www.nowcoder.com/tutorial/96/f5212664ab664984882b00635066ded2
4、同步任务和异步任务

for(var i=0; i<4 ;i++){
    setTimeout(function() {
          console.log(i);
    },1000);
}
/*
4
4
4
4
*/

这里for循环是同步任务,setTimeout是异步任务,同步任务执行完毕才会执行异步任务,这里的任务队列是这样:
for循环i++
setTimeout
for循环i++
setTimeout
for循环i++
setTimeout
for循环i++
setTimeout
这里只会执行for循环i++,当i=4时跳出循环,然后再执行任务队列中排在后面的setTimeout,排了4个,就要执行4次

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容