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

介绍一下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;

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理