乐趣区

关于前端:前端经典面试题有答案

ajax、axios、fetch 的区别

(1)AJAX Ajax 即“AsynchronousJavascriptAndXML”(异步 JavaScript 和 XML),是指一种创立交互式网页利用的网页开发技术。它是一种在无需从新加载整个网页的状况下,可能更新局部网页的技术。通过在后盾与服务器进行大量数据交换,Ajax 能够使网页实现异步更新。这意味着能够在不从新加载整个网页的状况下,对网页的某局部进行更新。传统的网页(不应用 Ajax)如果须要更新内容,必须重载整个网页页面。其毛病如下:

  • 自身是针对 MVC 编程,不合乎前端 MVVM 的浪潮
  • 基于原生 XHR 开发,XHR 自身的架构不清晰
  • 不合乎关注拆散(Separation of Concerns)的准则
  • 配置和调用形式十分凌乱,而且基于事件的异步模型不敌对。

(2)Fetch fetch 号称是 AJAX 的替代品,是在 ES6 呈现的,应用了 ES6 中的 promise 对象。Fetch 是基于 promise 设计的。Fetch 的代码构造比起 ajax 简略多。fetch 不是 ajax 的进一步封装,而是原生 js,没有应用 XMLHttpRequest 对象

fetch 的长处:

  • 语法简洁,更加语义化
  • 基于规范 Promise 实现,反对 async/await
  • 更加底层,提供的 API 丰盛(request, response)
  • 脱离了 XHR,是 ES 标准里新的实现形式

fetch 的毛病:

  • fetch 只对网络申请报错,对 400,500 都当做胜利的申请,服务器返回 400,500 错误码时并不会 reject,只有网络谬误这些导致申请不能实现时,fetch 才会被 reject。
  • fetch 默认不会带 cookie,须要增加配置项:fetch(url, {credentials: ‘include’})
  • fetch 不反对 abort,不反对超时管制,应用 setTimeout 及 Promise.reject 的实现的超时管制并不能阻止申请过程持续在后盾运行,造成了流量的节约
  • fetch 没有方法原生监测申请的进度,而 XHR 能够

(3)Axios Axios 是一种基于 Promise 封装的 HTTP 客户端,其特点如下:

  • 浏览器端发动 XMLHttpRequests 申请
  • node 端发动 http 申请
  • 反对 Promise API
  • 监听申请和返回
  • 对申请和返回进行转化
  • 勾销申请
  • 主动转换 json 数据
  • 客户端反对抵挡 XSRF 攻打

call apply bind

题目形容: 手写 call apply bind 实现

实现代码如下:

Function.prototype.myCall = function (context, ...args) {if (!context || context === null) {context = window;}
  // 发明惟一的 key 值  作为咱们结构的 context 外部办法名
  let fn = Symbol();
  context[fn] = this; //this 指向调用 call 的函数
  // 执行函数并返回后果 相当于把本身作为传入的 context 的办法进行调用了
  return context[fn](...args);
};

// apply 原理统一  只是第二个参数是传入的数组
Function.prototype.myApply = function (context, args) {if (!context || context === null) {context = window;}
  // 发明惟一的 key 值  作为咱们结构的 context 外部办法名
  let fn = Symbol();
  context[fn] = this;
  // 执行函数并返回后果
  return context[fn](...args);
};

//bind 实现要简单一点  因为他思考的状况比拟多 还要波及到参数合并(相似函数柯里化)

Function.prototype.myBind = function (context, ...args) {if (!context || context === null) {context = window;}
  // 发明惟一的 key 值  作为咱们结构的 context 外部办法名
  let fn = Symbol();
  context[fn] = this;
  let _this = this;
  //  bind 状况要简单一点
  const result = function (...innerArgs) {
    // 第一种状况 : 若是将 bind 绑定之后的函数当作构造函数,通过 new 操作符应用,则不绑定传入的 this,而是将 this 指向实例化进去的对象
    // 此时因为 new 操作符作用  this 指向 result 实例对象  而 result 又继承自传入的_this 依据原型链常识可得出以下论断
    // this.__proto__ === result.prototype   //this instanceof result =>true
    // this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype; //this instanceof _this =>true
    if (this instanceof _this === true) {
      // 此时 this 指向指向 result 的实例  这时候不须要扭转 this 指向
      this[fn] = _this;
      this[fn](...[...args, ...innerArgs]); // 这里应用 es6 的办法让 bind 反对参数合并
    } else {
      // 如果只是作为一般函数调用  那就很简略了 间接扭转 this 指向为传入的 context
      context[fn](...[...args, ...innerArgs]);
    }
  };
  // 如果绑定的是构造函数 那么须要继承构造函数原型属性和办法
  // 实现继承的形式: 应用 Object.create
  result.prototype = Object.create(this.prototype);
  return result;
};

// 用法如下

// function Person(name, age) {//   console.log(name); //'我是参数传进来的 name'
//   console.log(age); //'我是参数传进来的 age'
//   console.log(this); // 构造函数 this 指向实例对象
// }
// // 构造函数原型的办法
// Person.prototype.say = function() {//   console.log(123);
// }
// let obj = {
//   objName: '我是 obj 传进来的 name',
//   objAge: '我是 obj 传进来的 age'
// }
// // 一般函数
// function normalFun(name, age) {//   console.log(name);   //'我是参数传进来的 name'
//   console.log(age);   //'我是参数传进来的 age'
//   console.log(this); // 一般函数 this 指向绑定 bind 的第一个参数 也就是例子中的 obj
//   console.log(this.objName); //'我是 obj 传进来的 name'
//   console.log(this.objAge); //'我是 obj 传进来的 age'
// }

// 先测试作为结构函数调用
// let bindFun = Person.myBind(obj, '我是参数传进来的 name')
// let a = new bindFun('我是参数传进来的 age')
// a.say() //123

// 再测试作为一般函数调用
// let bindFun = normalFun.myBind(obj, '我是参数传进来的 name')
//  bindFun('我是参数传进来的 age')

代码输入问题

function A(){}
function B(a){this.a = a;}
function C(a){if(a){this.a = a;}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;

console.log(new A().a);
console.log(new B().a);
console.log(new C(2).a);

输入后果:1 undefined 2

解析:

  1. console.log(new A().a),new A()为构造函数创立的对象,自身没有 a 属性,所以向它的原型去找,发现原型的 a 属性的属性值为 1,故该输入值为 1;
  2. console.log(new B().a),ew B()为构造函数创立的对象,该构造函数有参数 a,但该对象没有传参,故该输入值为 undefined;
  3. console.log(new C(2).a),new C()为构造函数创立的对象,该构造函数有参数 a,且传的实参为 2,执行函数外部,发现 if 为真,执行 this.a = 2, 故属性 a 的值为 2。

代码输入后果

var x = 3;
var y = 4;
var obj = {
    x: 1,
    y: 6,
    getX: function() {
        var x = 5;
        return function() {return this.x;}();},
    getY: function() {
        var y = 7;
        return this.y;
    }
}
console.log(obj.getX()) // 3
console.log(obj.getY()) // 6

输入后果:3 6

解析:

  1. 咱们晓得,匿名函数的 this 是指向全局对象的,所以 this 指向 window,会打印出 3;
  2. getY 是由 obj 调用的,所以其 this 指向的是 obj 对象,会打印出 6。

代码输入后果

async function async1 () {await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {return new Promise((resolve, reject) => {console.log('async2')
    reject('error')
  })
}
async1().then(res => console.log(res))

输入后果如下:

async2
Uncaught (in promise) error

能够看到,如果 async 函数中抛出了谬误,就会终止谬误后果,不会持续向下执行。

如果想要让谬误不足之处前面的代码执行,能够应用 catch 来捕捉:

async function async1 () {await Promise.reject('error!!!').catch(e => console.log(e))
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')

这样的输入后果就是:

script start
error!!!
async1
async1 success

代码输入后果

var friendName = 'World';
(function() {if (typeof friendName === 'undefined') {
    var friendName = 'Jack';
    console.log('Goodbye' + friendName);
  } else {console.log('Hello' + friendName);
  }
})();

输入后果:Goodbye Jack

咱们晓得,在 JavaScript 中,Function 和 var 都会被晋升(变量晋升),所以下面的代码就相当于:

var name = 'World!';
(function () {
    var name;
    if (typeof name === 'undefined') {
        name = 'Jack';
        console.log('Goodbye' + name);
    } else {console.log('Hello' + name);
    }
})();

这样,答案就高深莫测了。

参考 前端进阶面试题具体解答

代码输入后果

console.log(1);

setTimeout(() => {console.log(2);
  Promise.resolve().then(() => {console.log(3)
  });
});

new Promise((resolve, reject) => {console.log(4)
  resolve(5)
}).then((data) => {console.log(data);
})

setTimeout(() => {console.log(6);
})

console.log(7);

代码输入后果如下:

1
4
7
5
2
3
6

代码执行过程如下:

  1. 首先执行 scrip 代码,打印出 1;
  2. 遇到第一个定时器 setTimeout,将其退出到宏工作队列;
  3. 遇到 Promise,执行外面的同步代码,打印出 4,遇到 resolve,将其退出到微工作队列;
  4. 遇到第二个定时器 setTimeout,将其退出到红工作队列;
  5. 执行 script 代码,打印出 7,至此第一轮执行实现;
  6. 指定微工作队列中的代码,打印出 resolve 的后果:5;
  7. 执行宏工作中的第一个定时器 setTimeout,首先打印出 2,而后遇到 Promise.resolve().then(),将其退出到微工作队列;
  8. 执行完这个宏工作,就开始执行微工作队列,打印出 3;
  9. 继续执行宏工作队列中的第二个定时器,打印出 6。

什么是 XSS 攻打?

(1)概念

XSS 攻打指的是跨站脚本攻打,是一种代码注入攻打。攻击者通过在网站注入歹意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。

XSS 的实质是因为网站没有对恶意代码进行过滤,与失常的代码混合在一起了,浏览器没有方法分辨哪些脚本是可信的,从而导致了恶意代码的执行。

攻击者能够通过这种攻击方式能够进行以下操作:

  • 获取页面的数据,如 DOM、cookie、localStorage;
  • DOS 攻打,发送正当申请,占用服务器资源,从而使用户无法访问服务器;
  • 毁坏页面构造;
  • 流量劫持(将链接指向某网站);

(2)攻打类型

XSS 能够分为存储型、反射型和 DOM 型:

  • 存储型指的是歹意脚本会存储在指标服务器上,当浏览器申请数据时,脚本从服务器传回并执行。
  • 反射型指的是攻击者诱导用户拜访一个带有恶意代码的 URL 后,服务器端接收数据后处理,而后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终实现 XSS 攻打。
  • DOM 型指的通过批改页面的 DOM 节点造成的 XSS。

1)存储型 XSS 的攻打步骤:

  1. 攻击者将恶意代码提交到⽬标⽹站的数据库中。
  2. ⽤户关上⽬标⽹站时,⽹站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
  3. ⽤户浏览器接管到响应后解析执⾏,混在其中的恶意代码也被执⾏。
  4. 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。

这种攻打常⻅于带有⽤户保留数据的⽹站性能,如论坛发帖、商品评论、⽤户私信等。

2)反射型 XSS 的攻打步骤:

  1. 攻击者结构出非凡的 URL,其中蕴含恶意代码。
  2. ⽤户关上带有恶意代码的 URL 时,⽹站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  3. ⽤户浏览器接管到响应后解析执⾏,混在其中的恶意代码也被执⾏。
  4. 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。

反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库⾥,反射型 XSS 的恶意代码存在 URL ⾥。

反射型 XSS 破绽常⻅于通过 URL 传递参数的性能,如⽹站搜寻、跳转等。因为须要⽤户被动关上歹意的 URL 能力⽣效,攻击者往往会联合多种⼿段诱导⽤户点击。

3)DOM 型 XSS 的攻打步骤:

  1. 攻击者结构出非凡的 URL,其中蕴含恶意代码。
  2. ⽤户关上带有恶意代码的 URL。
  3. ⽤户浏览器接管到响应后解析执⾏,前端 JavaScript 取出 URL 中的恶意代码并执⾏。
  4. 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。

DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻打中,取出和执⾏恶意代码由浏览器端实现,属于前端 JavaScript ⾃身的安全漏洞,⽽其余两种 XSS 都属于服务端的安全漏洞。

常见的图片格式及应用场景

(1)BMP,是无损的、既反对索引色也反对间接色的点阵图。这种图片格式简直没有对数据进行压缩,所以 BMP 格局的图片通常是较大的文件。

(2)GIF是无损的、采纳索引色的点阵图。采纳 LZW 压缩算法进行编码。文件小,是 GIF 格局的长处,同时,GIF 格局还具备反对动画以及通明的长处。然而 GIF 格局仅反对 8bit 的索引色,所以 GIF 格局实用于对色调要求不高同时须要文件体积较小的场景。

(3)JPEG是有损的、采纳间接色的点阵图。JPEG 的图片的长处是采纳了间接色,得益于更丰盛的色调,JPEG 非常适合用来存储照片,与 GIF 相比,JPEG 不适宜用来存储企业 Logo、线框类的图。因为有损压缩会导致图片含糊,而间接色的选用,又会导致图片文件较 GIF 更大。

(4)PNG-8是无损的、应用索引色的点阵图。PNG 是一种比拟新的图片格式,PNG- 8 是十分好的 GIF 格局替代者,在可能的状况下,应该尽可能的应用 PNG- 8 而不是 GIF,因为在雷同的图片成果下,PNG- 8 具备更小的文件体积。除此之外,PNG- 8 还反对透明度的调节,而 GIF 并不反对。除非须要动画的反对,否则没有理由应用 GIF 而不是 PNG-8。

(5)PNG-24是无损的、应用间接色的点阵图。PNG-24 的长处在于它压缩了图片的数据,使得同样成果的图片,PNG-24 格局的文件大小要比 BMP 小得多。当然,PNG24 的图片还是要比 JPEG、GIF、PNG- 8 大得多。

(6)SVG是无损的矢量图。SVG 是矢量图意味着 SVG 图片由直线和曲线以及绘制它们的办法组成。当放大 SVG 图片时,看到的还是线和曲线,而不会呈现像素点。这意味着 SVG 图片在放大时,不会失真,所以它非常适合用来绘制 Logo、Icon 等。

(7)WebP是谷歌开发的一种新图片格式,WebP 是同时反对有损和无损压缩的、应用间接色的点阵图。从名字就可以看进去它是为 Web 而生的,什么叫为 Web 而生呢?就是说雷同品质的图片,WebP 具备更小的文件体积。当初网站上充斥了大量的图片,如果可能升高每一个图片的文件大小,那么将大大减少浏览器和服务器之间的数据传输量,进而升高拜访提早,晋升拜访体验。目前只有 Chrome 浏览器和 Opera 浏览器反对 WebP 格局,兼容性不太好。

  • 在无损压缩的状况下,雷同品质的 WebP 图片,文件大小要比 PNG 小 26%;
  • 在有损压缩的状况下,具备雷同图片精度的 WebP 图片,文件大小要比 JPEG 小 25%~34%;
  • WebP 图片格式反对图片透明度,一个无损压缩的 WebP 图片,如果要反对透明度只须要 22% 的分外文件大小。

代码输入问题

window.number = 2;
var obj = {
 number: 3,
 db1: (function(){console.log(this);
   this.number *= 4;
   return function(){console.log(this);
     this.number *= 5;
   }
 })()}
var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number);     // 15
console.log(window.number);  // 40

这道题目看清起来有点乱,然而实际上是考查 this 指向的:

  1. 执行 db1()时,this 指向全局作用域,所以 window.number 4 = 8,而后执行匿名函数,所以 window.number 5 = 40;
  2. 执行 obj.db1(); 时,this 指向 obj 对象,执行匿名函数,所以 obj.numer * 5 = 15。

iframe 有那些长处和毛病?

iframe 元素会创立蕴含另外一个文档的内联框架(即行内框架)。

长处:

  • 用来加载速度较慢的内容(如广告)
  • 能够使脚本能够并行下载
  • 能够实现跨子域通信

毛病:

  • iframe 会阻塞主页面的 onload 事件
  • 无奈被一些搜索引擎索辨认
  • 会产生很多页面,不容易治理

对 BFC 的了解,如何创立 BFC

先来看两个相干的概念:

  • Box: Box 是 CSS 布局的对象和根本单位,⼀个⻚⾯是由很多个 Box 组成的,这个 Box 就是咱们所说的盒模型。
  • Formatting context:块级高低⽂格式化,它是⻚⾯中的⼀块渲染区域,并且有⼀套渲染规定,它决定了其⼦元素将如何定位,以及和其余元素的关系和互相作⽤。

块格式化上下文(Block Formatting Context,BFC)是 Web 页面的可视化 CSS 渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其余元素的交互限定区域。

艰深来讲:BFC 是一个独立的布局环境,能够了解为一个容器,在这个容器中依照肯定规定进行物品摆放,并且不会影响其它环境中的物品。如果一个元素合乎触发 BFC 的条件,则 BFC 中的元素布局不受内部影响。

创立 BFC 的条件:

  • 根元素:body;
  • 元素设置浮动:float 除 none 以外的值;
  • 元素设置相对定位:position (absolute、fixed);
  • display 值为:inline-block、table-cell、table-caption、flex 等;
  • overflow 值为:hidden、auto、scroll;

BFC 的特点:

  • 垂直方向上,自上而下排列,和文档流的排列形式统一。
  • 在 BFC 中高低相邻的两个容器的 margin 会重叠
  • 计算 BFC 的高度时,须要计算浮动元素的高度
  • BFC 区域不会与浮动的容器产生重叠
  • BFC 是独立的容器,容器外部元素不会影响内部元素
  • 每个元素的左 margin 值和容器的左 border 相接触

BFC 的作用:

  • 解决 margin 的重叠问题:因为 BFC 是一个独立的区域,外部的元素和内部的元素互不影响,将两个元素变为两个 BFC,就解决了 margin 重叠的问题。
  • 解决高度塌陷的问题:在对子元素设置浮动后,父元素会产生高度塌陷,也就是父元素的高度变为 0。解决这个问题,只须要把父元素变成一个 BFC。罕用的方法是给父元素设置overflow:hidden
  • 创立自适应两栏布局:能够用来创立自适应两栏布局:右边的宽度固定,左边的宽度自适应。
.left{
     width: 100px;
     height: 200px;
     background: red;
     float: left;
 }
 .right{
     height: 300px;
     background: blue;
     overflow: hidden;
 }

<div class="left"></div>
<div class="right"></div>

左侧设置float:left,右侧设置overflow: hidden。这样左边就触发了 BFC,BFC 的区域不会与浮动元素产生重叠,所以两侧就不会产生重叠,实现了自适应两栏布局。

并发与并行的区别?

  • 并发是宏观概念,我别离有工作 A 和工作 B,在一段时间内通过工作间的切换实现了这两个工作,这种状况就能够称之为并发。
  • 并行是宏观概念,假如 CPU 中存在两个外围,那么我就能够同时实现工作 A、B。同时实现多个工作的状况就能够称之为并行。

同步和异步的区别

  • 同步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,那么这个过程会始终期待上来,直到音讯返回为止再持续向下执行。
  • 异步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,这个时候过程会持续往下执行,不会阻塞期待音讯的返回,当音讯返回时零碎再告诉过程进行解决。

代码输入后果

function foo(something){this.a = something}

var obj1 = {foo: foo}

var obj2 = {}

obj1.foo(2); 
console.log(obj1.a); // 2

obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3

var bar = new obj1.foo(4)
console.log(obj1.a); // 2
console.log(bar.a); // 4

输入后果:2 3 2 4

解析:

  1. 首先执行 obj1.foo(2); 会在 obj 中增加 a 属性,其值为 2。之后执行 obj1.a,a 是右 obj1 调用的,所以 this 指向 obj,打印出 2;
  2. 执行 obj1.foo.call(obj2, 3) 时,会将 foo 的 this 指向 obj2,前面就和下面一样了,所以会打印出 3;
  3. obj1.a 会打印出 2;
  4. 最初就是考查 this 绑定的优先级了,new 绑定是比隐式绑定优先级高,所以会输入 4。

对 Service Worker 的了解

Service Worker 是运行在浏览器背地的 独立线程,个别能够用来实现缓存性能。应用 Service Worker 的话,传输协定必须为 HTTPS。因为 Service Worker 中波及到申请拦挡,所以必须应用 HTTPS 协定来保障平安。

Service Worker 实现缓存性能个别分为三个步骤:首先须要先注册 Service Worker,而后监听到 install 事件当前就能够缓存须要的文件,那么在下次用户拜访的时候就能够通过拦挡申请的形式查问是否存在缓存,存在缓存的话就能够间接读取缓存文件,否则就去申请数据。以下是这个步骤的实现:

// index.js
if (navigator.serviceWorker) {
  navigator.serviceWorker
    .register('sw.js')
    .then(function(registration) {console.log('service worker 注册胜利')
    })
    .catch(function(err) {console.log('servcie worker 注册失败')
    })
}
// sw.js
// 监听 `install` 事件,回调中缓存所需文件
self.addEventListener('install', e => {
  e.waitUntil(caches.open('my-cache').then(function(cache) {return cache.addAll(['./index.html', './index.js'])
    })
  )
})
// 拦挡所有申请事件
// 如果缓存中曾经有申请的数据就间接用缓存,否则去申请数据
self.addEventListener('fetch', e => {
  e.respondWith(caches.match(e.request).then(function(response) {if (response) {return response}
      console.log('fetch source')
    })
  )
})

关上页面,能够在开发者工具中的 Application 看到 Service Worker 曾经启动了:在 Cache 中也能够发现所需的文件已被缓存:

文档申明(Doctype)和 <!Doctype html> 有何作用? 严格模式与混淆模式如何辨别?它们有何意义?

文档申明的作用: 文档申明是为了通知浏览器,以后 HTML 文档应用什么版本的 HTML 来写的,这样浏览器能力依照申明的版本来正确的解析。

的作用:<!doctype html> 的作用就是让浏览器进入规范模式,应用最新的 HTML5 规范来解析渲染页面;如果不写,浏览器就会进入混淆模式,咱们须要防止此类情况产生。

严格模式与混淆模式的辨别:

  • 严格模式 :又称为规范模式,指浏览器依照W3C 规范解析代码;
  • 混淆模式:又称怪异模式、兼容模式,是指浏览器用本人的形式解析代码。混淆模式通常模仿老式浏览器的行为,以避免老站点无奈工作;

辨别 :网页中的DTD,间接影响到应用的是严格模式还是浏览模式,能够说DTD 的应用与这两种形式的区别非亲非故。

  • 如果文档蕴含严格的 DOCTYPE,那么它个别以严格模式出现( 严格 DTD ——严格模式);
  • 蕴含过渡 DTDURIDOCTYPE,也以严格模式出现,但有过渡 DTD 而没有 URI(对立资源标识符,就是申明最初的地址)会导致页面以混淆模式出现(有 URI 的过渡 DTD ——严格模式;没有 URI 的过渡 DTD ——混淆模式);
  • DOCTYPE 不存在或模式不正确会导致文档以混淆模式出现(DTD 不存在或者格局不正确——混淆模式);
  • HTML5 没有 DTD,因而也就没有严格模式与混淆模式的区别,HTML5 有绝对宽松的 法,实现时,曾经尽可能大的实现了向后兼容(HTML5 没有严格和混淆之分)。

总之,严格模式让各个浏览器对立执行一套标准兼容模式保障了旧网站的失常运行。

代码输入后果

const async1 = async () => {console.log('async1');
  setTimeout(() => {console.log('timer1')
  }, 2000)
  await new Promise(resolve => {console.log('promise1')
  })
  console.log('async1 end')
  return 'async1 success'
} 
console.log('script start');
async1().then(res => console.log(res));
console.log('script end');
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .catch(4)
  .then(res => console.log(res))
setTimeout(() => {console.log('timer2')
}, 1000)

输入后果如下:

script start
async1
promise1
script end
1
timer2
timer1

代码的执行过程如下:

  1. 首先执行同步带吗,打印出 script start;
  2. 遇到定时器 timer1 将其退出宏工作队列;
  3. 之后是执行 Promise,打印出 promise1,因为 Promise 没有返回值,所以前面的代码不会执行;
  4. 而后执行同步代码,打印出 script end;
  5. 继续执行上面的 Promise,.then 和.catch 冀望参数是一个函数,这里传入的是一个数字,因而就会产生值浸透,将 resolve(1)的值传到最初一个 then,间接打印出 1;
  6. 遇到第二个定时器,将其退出到微工作队列,执行微工作队列,按程序顺次执行两个定时器,然而因为定时器工夫的起因,会在两秒后先打印出 timer2,在四秒后打印出 timer1。

代码输入后果

const promise = Promise.resolve().then(() => {return promise;})
promise.catch(console.err)

输入后果如下:

Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

这里其实是一个坑,.then.catch 返回的值不能是 promise 自身,否则会造成死循环。

display:none 与 visibility:hidden 的区别

这两个属性都是让元素暗藏,不可见。两者区别如下:

(1)在渲染树中

  • display:none会让元素齐全从渲染树中隐没,渲染时不会占据任何空间;
  • visibility:hidden不会让元素从渲染树中隐没,渲染的元素还会占据相应的空间,只是内容不可见。

(2)是否是继承属性

  • display:none是非继承属性,子孙节点会随着父节点从渲染树隐没,通过批改子孙节点的属性也无奈显示;
  • visibility:hidden是继承属性,子孙节点隐没是因为继承了 hidden,通过设置visibility:visible 能够让子孙节点显示;
    (3)批改惯例文档流中元素的 display 通常会造成文档的重排,然而批改 visibility 属性只会造成本元素的重绘;

(4)如果应用读屏器,设置为 display:none 的内容不会被读取,设置为 visibility:hidden 的内容会被读取。

退出移动版