乐趣区

关于前端:高级前端二面常见面试题总结

介绍一下 Connection:keep-alive

什么是 keep-alive

咱们晓得 HTTP 协定采纳“申请 - 应答”模式,当应用一般模式,即非 KeepAlive 模式时,每个申请 / 应答客户和服务器都要新建一个连贯,实现 之后立刻断开连接(HTTP 协定为无连贯的协定);

当应用 Keep-Alive 模式(又称长久连贯、连贯重用)时,Keep-Alive 性能使客户端到服 务器端的连贯继续无效,当呈现对服务器的后继申请时,Keep-Alive 性能防止了建设或者从新建设连贯。

为什么要应用 keep-alive

keep-alive 技术的创立目标,能在屡次 HTTP 之前重用同一个 TCP 连贯,从而缩小创立 / 敞开多个 TCP 连贯的开销(包含响应工夫、CPU 资源、缩小拥挤等),参考如下示意图

客户端如何开启

在 HTTP/1.0 协定中,默认是敞开的,须要在 http 头退出 ”Connection: Keep-Alive”,能力启用 Keep-Alive;

Connection: keep-alive

http 1.1 中默认启用 Keep-Alive,如果退出 ”Connection: close“,才敞开。

Connection: close

目前大部分浏览器都是用 http1.1 协定,也就是说默认都会发动 Keep-Alive 的连贯申请了,所以是否能实现一个残缺的 Keep- Alive 连贯就看服务器设置状况。

单例模式

用意:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点。

次要解决:一个全局应用的类频繁地创立与销毁。

何时应用:当您想管制实例数目,节俭系统资源的时候。

如何解决:判断零碎是否曾经有这个单例,如果有则返回,如果没有则创立。

实现

var Singleton = (function() {
    // 如果在外部申明 SingletonClass 对象,则无奈在内部间接调用
    var SingletonClass = function() {}; 
    var instance;
    return function() {
        // 如果已存在,则返回 instance
        if(instance) return instance;
        // 如果不存在,则 new 一个 SingletonClass 对象
        instance = new SingletonClass();
        return instance;
    }
})();

// 测试
var a = new Singleton();
var b = new Singleton();
console.log(a === b);  // true

Promise.any

形容 :只有 promises 中有一个fulfilled,就返回第一个fulfilledPromise实例的返回值。

实现

Promise.any = function(promises) {return new Promise((resolve, reject) => {if(Array.isArray(promises)) {if(promises.length === 0) return reject(new AggregateError("All promises were rejected"));
            let count = 0;
            promises.forEach((item, index) => {Promise.resolve(item).then(value => resolve(value),
                    reason => {
                        count++;
                        if(count === promises.length) {reject(new AggregateError("All promises were rejected"));
                        };
                    }
                );
            })
        }
        else return reject(new TypeError("Argument is not iterable"));
    });
}

代码输入后果

const first = () => (new Promise((resolve, reject) => {console.log(3);
    let p = new Promise((resolve, reject) => {console.log(7);
        setTimeout(() => {console.log(5);
            resolve(6);
            console.log(p)
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {console.log(arg);
    });
}));
first().then((arg) => {console.log(arg);
});
console.log(4);

输入后果如下:

3
7
4
1
2
5
Promise{<resolved>: 1}

代码的执行过程如下:

  1. 首先会进入 Promise,打印出 3,之后进入上面的 Promise,打印出 7;
  2. 遇到了定时器,将其退出宏工作队列;
  3. 执行 Promise p 中的 resolve,状态变为 resolved,返回值为 1;
  4. 执行 Promise first 中的 resolve,状态变为 resolved,返回值为 2;
  5. 遇到 p.then,将其退出微工作队列,遇到 first().then,将其退出工作队列;
  6. 执行里面的代码,打印出 4;
  7. 这样第一轮宏工作就执行完了,开始执行微工作队列中的工作,先后打印出 1 和 2;
  8. 这样微工作就执行完了,开始执行下一轮宏工作,宏工作队列中有一个定时器,执行它,打印出 5,因为执行曾经变为 resolved 状态,所以 resolve(6) 不会再执行;
  9. 最初 console.log(p) 打印出Promise{<resolved>: 1}

代码输入后果

function Person(name) {this.name = name}
var p2 = new Person('king');
console.log(p2.__proto__) //Person.prototype
console.log(p2.__proto__.__proto__) //Object.prototype
console.log(p2.__proto__.__proto__.__proto__) // null
console.log(p2.__proto__.__proto__.__proto__.__proto__)//null 前面没有了,报错
console.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__)//null 前面没有了,报错
console.log(p2.constructor)//Person
console.log(p2.prototype)//undefined p2 是实例,没有 prototype 属性
console.log(Person.constructor)//Function 一个空函数
console.log(Person.prototype)// 打印出 Person.prototype 这个对象里所有的办法和属性
console.log(Person.prototype.constructor)//Person
console.log(Person.prototype.__proto__)// Object.prototype
console.log(Person.__proto__) //Function.prototype
console.log(Function.prototype.__proto__)//Object.prototype
console.log(Function.__proto__)//Function.prototype
console.log(Object.__proto__)//Function.prototype
console.log(Object.prototype.__proto__)//null

这道义题目考查原型、原型链的根底,记住就能够了。

代码输入后果

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

var obj1 = {}

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

var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3

输入后果:2 2 3

这道题目和下面题目差不多,次要都是考查 this 绑定的优先级。记住以下论断即可:this 绑定的优先级:new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定。

实现模板字符串解析

形容 :实现函数使得将 template 字符串中的{{}} 内的变量替换。

外围:应用字符串替换办法 str.replace(regexp|substr, newSubStr|function),应用正则匹配代换字符串。

实现

function render(template, data) {// 模板字符串正则 /\{\{(\w+)\}\}/, 加 g 为全局匹配模式, 每次匹配都会调用前面的函数
    let computed = template.replace(/\{\{(\w+)\}\}/g, function(match, key) {
        // match: 匹配的子串;  key:括号匹配的字符串
        return data[key];
    });
    return computed;
}

// 测试
let template = "我是{{name}},年龄{{age}},性别{{sex}}";
let data = {
  name: "张三",
  age: 18
}
console.log(render(template, data)); // 我是张三,年龄 18,性别 undefined

对 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 的区域不会与浮动元素产生重叠,所以两侧就不会产生重叠,实现了自适应两栏布局。

title 与 h1 的区别、b 与 strong 的区别、i 与 em 的区别?

  • strong 标签有语义,是起到减轻语气的成果,而 b 标签是没有的,b 标签只是一个简略加粗标签。b 标签之间的字符都设为粗体,strong 标签增强字符的语气都是通过粗体来实现的,而搜索引擎更偏重 strong 标签。
  • title 属性没有明确意义只示意是个题目,H1 则示意档次明确的题目,对页面信息的抓取有很大的影响
  • i 内容展现为斜体,em 示意强调的文本

懒加载的实现原理

图片的加载是由 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>

正向代理和反向代理的区别

  • 正向代理:

客户端想取得一个服务器的数据,然而因为种种原因无奈间接获取。于是客户端设置了一个代理服务器,并且指定指标服务器,之后代理服务器向指标服务器转交申请并将取得的内容发送给客户端。这样实质上起到了对实在服务器暗藏实在客户端的目标。实现正向代理须要批改客户端,比方批改浏览器配置。

  • 反向代理:

服务器为了可能将工作负载分不到多个服务器来进步网站性能 (负载平衡)等目标,当其受到申请后,会首先依据转发规定来确定申请应该被转发到哪个服务器上,而后将申请转发到对应的实在服务器上。这样实质上起到了对客户端暗藏实在服务器的作用。
个别应用反向代理后,须要通过批改 DNS 让域名解析到代理服务器 IP,这时浏览器无奈察觉到真正服务器的存在,当然也就不须要批改配置了。

正向代理和反向代理的构造是一样的,都是 client-proxy-server 的构造,它们次要的区别就在于两头这个 proxy 是哪一方设置的。在正向代理中,proxy 是 client 设置的,用来暗藏 client;而在反向代理中,proxy 是 server 设置的,用来暗藏 server。

代码输入后果

function runAsync(x) {
  const p = new Promise(r =>
    setTimeout(() => r(x, console.log(x)), 1000)
  );
  return p;
}
function runReject(x) {const p = new Promise((res, rej) =>
    setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
  );
  return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log("result:", res))
  .catch(err => console.log(err));

输入后果如下:

0
Error: 0
1
2
3

能够看到在 catch 捕捉到第一个谬误之后,前面的代码还不执行,不过不会再被捕捉了。

留神:allrace 传入的数组中如果有会抛出异样的异步工作,那么只有最先抛出的谬误会被捕捉,并且是被 then 的第二个参数或者前面的 catch 捕捉;但并不会影响数组中其它的异步工作的执行。

首屏和白屏工夫如何计算

首屏工夫的计算,能够由 Native WebView 提供的相似 onload 的办法实现,在 ios 下对应的是 webViewDidFinishLoad,在 android 下对应的是 onPageFinished 事件。

白屏的定义有多种。能够认为“没有任何内容”是白屏,能够认为“网络或服务异样”是白屏,能够认为“数据加载中”是白屏,能够认为“图片加载不进去”是白屏。场景不同,白屏的计算形式就不雷同。

办法 1:当页面的元素数小于 x 时,则认为页面白屏。比方“没有任何内容”,能够获取页面的 DOM 节点数,判断 DOM 节点数少于某个阈值 X,则认为白屏。办法 2:当页面呈现业务定义的错误码时,则认为是白屏。比方“网络或服务异样”。办法 3:当页面呈现业务定义的特征值时,则认为是白屏。比方“数据加载中”。

浏览器乱码的起因是什么?如何解决?

产生乱码的起因:

  • 网页源代码是 gbk 的编码,而内容中的中文字是 utf-8 编码的,这样浏览器关上即会呈现 html 乱码,反之也会呈现乱码;
  • html网页编码是 gbk,而程序从数据库中调出出现是utf-8 编码的内容也会造成编码乱码;
  • 浏览器不能自动检测网页编码,造成网页乱码。

解决办法:

  • 应用软件编辑 HTML 网页内容;
  • 如果网页设置编码是gbk,而数据库贮存数据编码格局是UTF-8,此时须要程序查询数据库数据显示数据后退程序转码;
  • 如果浏览器浏览时候呈现网页乱码,在浏览器中找到转换编码的菜单进行转换。

JavaScript 中如何进行隐式类型转换?

首先要介绍 ToPrimitive 办法,这是 JavaScript 中每个值隐含的自带的办法,用来将值(无论是根本类型值还是对象)转换为根本类型值。如果值为根本类型,则间接返回值自身;如果值为对象,其看起来大略是这样:

/*** @obj 须要转换的对象 * @type 冀望的后果类型 */
ToPrimitive(obj,type)

type的值为 number 或者string

(1)当 typenumber时规定如下:

  • 调用 objvalueOf办法,如果为原始值,则返回,否则下一步;
  • 调用 objtoString办法,后续同上;
  • 抛出TypeError 异样。

(2)当 typestring时规定如下:

  • 调用 objtoString办法,如果为原始值,则返回,否则下一步;
  • 调用 objvalueOf办法,后续同上;
  • 抛出TypeError 异样。

能够看出两者的次要区别在于调用 toStringvalueOf的先后顺序。默认状况下:

  • 如果对象为 Date 对象,则 type 默认为string
  • 其余状况下,type默认为number

总结下面的规定,对于 Date 以外的对象,转换为根本类型的大略规定能够概括为一个函数:

var objToNumber = value => Number(value.valueOf().toString())
objToNumber([]) === 0
objToNumber({}) === NaN

而 JavaScript 中的隐式类型转换次要产生在 +、-、*、/ 以及 ==、>、< 这些运算符之间。而这些运算符只能操作根本类型值,所以在进行这些运算前的第一步就是将两边的值用 ToPrimitive 转换成根本类型,再进行操作。

以下是根本类型的值在不同操作符的状况下隐式转换的规定(对于对象,其会被 ToPrimitive 转换成根本类型,所以最终还是要利用根本类型转换规定):

  1. +操作符 + 操作符的两边有至多一个 string 类型变量时,两边的变量都会被隐式转换为字符串;其余状况下两边的变量都会被转换为数字。
1 + '23' // '123'
 1 + false // 1 
 1 + Symbol() // Uncaught TypeError: Cannot convert a Symbol value to a number
 '1' + false // '1false'
 false + true // 1
  1. -*\操作符

NaN也是一个数字

1 * '23' // 23
 1 * false // 0
 1 / 'aa' // NaN
  1. 对于 == 操作符

操作符两边的值都尽量转成number

3 == true // false, 3 转为 number 为 3,true 转为 number 为 1
'0' == false //true, '0' 转为 number 为 0,false 转为 number 为 0
'0' == 0 // '0' 转为 number 为 0
  1. 对于 <>比拟符

如果两边都是字符串,则比拟字母表程序:

'ca' < 'bd' // false
'a' < 'b' // true

其余状况下,转换为数字再比拟:

'12' < 13 // true
false > -1 // true

以上说的是根本类型的隐式转换,而对象会被 ToPrimitive 转换为根本类型再进行转换:

var a = {}
a > 2 // false

其比照过程如下:

a.valueOf() // {}, 下面提到过,ToPrimitive 默认 type 为 number,所以先 valueOf,后果还是个对象,下一步
a.toString() // "[object Object]",当初是一个字符串了
Number(a.toString()) // NaN,依据下面 < 和 > 操作符的规定,要转换成数字
NaN > 2 //false,得出比拟后果

又比方:

var a = {name:'Jack'}
var b = {age: 18}
a + b // "[object Object][object Object]"

运算过程如下:

a.valueOf() // {},下面提到过,ToPrimitive 默认 type 为 number,所以先 valueOf,后果还是个对象,下一步
a.toString() // "[object Object]"
b.valueOf() // 同理
b.toString() // "[object Object]"
a + b // "[object Object][object Object]"

代码输入问题

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。

代码输入后果

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

输入后果如下:

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

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

浏览器是如何对 HTML5 的离线贮存资源进行治理和加载?

  • 在线的状况下,浏览器发现 html 头部有 manifest 属性,它会申请 manifest 文件,如果是第一次拜访页面,那么浏览器就会依据 manifest 文件的内容下载相应的资源并且进行离线存储。如果曾经拜访过页面并且资源曾经进行离线存储了,那么浏览器就会应用离线的资源加载页面,而后浏览器会比照新的 manifest 文件与旧的 manifest 文件,如果文件没有产生扭转,就不做任何操作,如果文件扭转了,就会从新下载文件中的资源并进行离线存储。
  • 离线的状况下,浏览器会间接应用离线存储的资源。

行内元素有哪些?块级元素有哪些?空 (void) 元素有那些?

  • 行内元素有:a b span img input select strong
  • 块级元素有:div ul ol li dl dt dd h1 h2 h3 h4 h5 h6 p

空元素,即没有内容的 HTML 元素。空元素是在开始标签中敞开的,也就是空元素没有闭合标签:

  • 常见的有:<br><hr><img><input><link><meta>
  • 鲜见的有:<area><base><col><colgroup><command><embed><keygen><param><source><track><wbr>

z-index 属性在什么状况下会生效

通常 z-index 的应用是在有两个重叠的标签,在肯定的状况下管制其中一个在另一个的上方或者下方呈现。z-index 值越大就越是在下层。z-index 元素的 position 属性须要是 relative,absolute 或是 fixed。

z-index 属性在下列状况下会生效:

  • 父元素 position 为 relative 时,子元素的 z -index 生效。解决:父元素 position 改为 absolute 或 static;
  • 元素没有设置 position 属性为非 static 属性。解决:设置该元素的 position 属性为 relative,absolute 或是 fixed 中的一种;
  • 元素在设置 z -index 的同时还设置了 float 浮动。解决:float 去除,改为 display:inline-block;
退出移动版