一些面经的小知识(三)杂七杂八篇

289 阅读9分钟

一、JS

1、实现一个订阅模式

const event = {
    eventList: [],
    listen: function(key, fn) {
        if (!this.eventList[key]) {
            this.eventList[key] = [];
        }
        this.eventList[key].push(fn);
    },
    trigger: function() {
        const key = Array.prototype.shift.call(arguments);
        const fns = this.eventList[key];
        if (!fns || !fns.length) {
            return;
        }
        for (let i = 0; i < fns.length; i++) {
            fns[i].apply(this, arguments);
        }
    },
    remove: function(key, fn) {
        const fns = this.eventList[key];
        if (!key || typeof key !== 'string') {
            return;
        }
        if (!fns) {
            return;
        }
        if (!fn) {
            this.eventList[key] = [];
        } else {
            let findIndex = '';
            for (let i = fns.length - 1; i >= 0; i--) {
                if (fns[i] === fn) {
                    findIndex = i;
                    break;
                }  
            }
            if (findIndex) {
                fns.splice(findIndex, 1);
            }
        }
    }
}

2、实现斐波那契数列[1, 1, 2, 3, 5, 8]

用递归可以得到某一位数

function getNum(n) {
    if (n === 1 || n === 2) {
        return 1;
    } else {
        return getNum(n - 2) + getNum(n - 1)    }
}
console.log(getNum(6));


那用for循环怎么实现这个数列呢?

function getNumList(n) {
    let a = 1, b = 1, c = 0, result = [];
    for (let i = 0; i < n; i++) {
        result.push(a);
        c = a; a = b; b = c + b;
    }
    console.log(result);
}
getNumList(6);


3、iframe子页面与父页面

父页面调用iframe子页面: document.getElementById(‘myIframe’).contentWindow.func()

iframe子页面调用父页面:window.parent.func()

但需要在iframe.onload后才能执行

4、是否了解iterator、iterable、generator

iterator迭代器 -> 满足迭代器协议的对象

满足迭代器协议 -> 简单理解就是这个对象的next方法返回一个对象,这个对象包含两个key值:

(1)done:  如果迭代器经历完迭代,那done会为true;如果迭代器还可以返回可迭代对象中下一个值,那done为false。

(2)value:返回可迭代对象里的任何js值。

// 实现一个iterator
function createIterator(items) {
    let i = 0;

    return {
        next: function() {
            const done = i >= items.length;
            const value = !done ? items[i++] : undefined;
            return {
                value,
                done    
            }
        }
    }
}
const myIterator = createIterator([1,2,3]);

console.log(myIterator.next());
console.log(myIterator.next());
console.log(myIterator.next());
console.log(myIterator.next());
console.log('---之后的调用---');
console.log(myIterator.next());


iterable -> 可迭代,是指满足可迭代协议,满足可迭代协议的对象叫可迭代对象。

满足可迭代协议 -> 简单理解是指对象的[Symbol.iterator]值是一个无参函数,该函数返回一个迭代器,这个迭代器可以依次返回可迭代对象里的值。

比如Array、Set、Map、String、arguments都是可迭代对象。

for(xxx of xxx)的原理是获取可迭代对象的[Symbol.iterator]函数返回的迭代器,调用迭代器的next,直到返回对象的done为true

生成器函数是指能返回一个生成器generator的函数,由在function关键字后带有  *  来表示,并能使用yield关键字。

function *aGeneratorfunction(){
  yield 1
  yield 2
  yield 3
};
var aGeneratorObject = aGeneratorfunction()
// 生成器对象
aGeneratorObject.toString()   // "[object Generator]"

生成器对象generator既是迭代器,又是可迭代对象。

function *aGeneratorfunction(){
  yield 1
  yield 2
  yield 3
};
var aGeneratorObject = aGeneratorfunction();
aGeneratorObject[Symbol.iterator]() === aGeneratorObject; // true
[...aGeneratorObject]

想了解更多可以参考github这篇文章:

github.com/yueshuiniao…

二、网络请求

1、http1.0、http1.1、http2.0的区别

http1.0和http1.1的区别主要是:

(1)http1.0缓存主要是使用If-Modified-Since、Expires来判断,http1.1则引入了更多策略如Entity Tag、If-Unmodified-Since、If-Match、If-None-Match等

(2)带宽优化

(3)错误状态管理

(4)host处理

(5)http1.1支持长连接,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在http1.1中默认开启Connection: keep-alive,一定程度上弥补了http1.0每次请求都要创建连接的缺点

http2.0相比http1.x的新特性:

(1)采用新的二进制格式,弥补文本传输解析的缺陷

(2)多路复用:http2.0的传输是基于二进制帧的。每一个TCP连接中承载了多个双向流通的流,每一个流都有一个独一无二的标识和优先级,而流就是由二进制帧组成的。二进制帧的头部信息会标识自己属于哪一个流,所以这些帧是可以交错传输,然后在接收端通过帧头的信息组装成完整的数据。这样就解决了线头阻塞的问题,同时也提高了网络速度的利用率

(3)header的压缩,http2.0采用encoder来减少传输header的大小,且通讯双方各持有一份header filed,避免重复header的传输

(4)服务端推送

2、缓存

缓存分为强缓存和协商缓存

(1)header的参数:

强缓存主要是expires、cache-control

协商缓存主要是last-modify/if-modify-since和etag/if-none-match

(2)稍微讲下协商缓存:

浏览器第一次请求资源时,服务器返回的header会带有last-modify,last-modify是一个时间标示该资源最后修改时间;当浏览器再次请求该资源时,请求的header会带上if-modify-since,它的值就是缓存上次返回的last-modify的值,服务器根据文件最后修改时间判断是否命中缓存。

服务器对于资源的首次请求返回的header也可能带有etag,etag可以看作是资源在服务器的唯一标识;如果资源过期没命中强缓存时,如果上次返回header中带有etag,则这次向服务器请求该资源时,会带上if-none-match,值则是上次返回的etag,服务器比对if-none-match和文件资源的校验串决定是否命中协商缓存。

(3)last-modify和etag的区别:

last-modify是秒级的,也就是说文件如果在1秒内发生多次修改的话,last-modify值无法体现,etag不同,它每次都会改变确保了精度。

性能上last-modify优于etag,etag是服务器根据文件的Inode、MTime和size,然后通过算法得出的hex格式的值。

服务器检验优先级:cache-control>expires,etag>last-modify

因为负载均衡,不同服务器生成的Inode可能不同,所以etag也可能不同

参考文章:www.cnblogs.com/wonyun/p/55…

                   www.cnblogs.com/tiwlin/arch…

3、请求method

get、post就不再赘述了

put:主要是文件传输,但是http1.1put只会在不带校验rest标准的web才用

head:只获取报文首部、URT有效性和资源的更新时间

delete:主要是删除文件

options:询问服务器支持的请求方法

trace:让服务器将请求通信环回给客户端(通过max-forwards计数,通过一个服务器就 -1),不安全,因为容易引发XST,跨站追踪

connect:隧道协议

4、https

包含非对称加密以及对称加密


参考:juejin.cn/post/684490…

5、跨域处理

什么算跨域?非同源请求,均为跨域。

如果两个页面拥有相同的协议(protocol),端口(port)和主机(host),那么这两个页面就属于同一个源(origin)

img01

jsonp(只支持get请求)、iframe、cors(服务器设置响应header中Access-Control-Allow-Origin为*或允许跨域的域名)。

跨域浏览器默认请求不携带cookie,若想带上cookie,要设置Access-Control-Allow-Credentials为true,如果用的是axios,则是加上withCredentials: true。

6、每个浏览器每个域名下cookie的个数限制不同,webkit没有限制,IE50,firefox50,总大小大概4000多字节

三、前端安全

1、XSS

跨站脚本攻击

主要是通过在web页面插入未经处理的可被页面解析的恶意代码来执行某些操作,比如获取用户的cookie。XSS分为存储型、反射型以及DOM XSS。

防范:前端输入校验、后端存储校验;cookie设置http-only;浏览器自带的x-xss-protection;

还有一种叫csp安全策略,就是在html的head中加入<meta http-equiv="content-security-policy" content="default-src 'self';script-src 'xxx.com'">

可参考:developer.mozilla.org/zh-CN/docs/…

2、CSRF

跨站请求伪造

主要是通过伪装成受信任用户的请求来利用受信任的网站。

CSRF是源于Web的隐式身份验证机制。Web的身份验证机制大致就是说为了防止用户每次发送请求的时候都需要登录,在进行一次登录验证通过后,之后发向该域名的请求都会自动带上cookie。虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。

防范:token和referer校验、验证码等

四、CSS

1、实现水平垂直居中

css实现水平垂直居中的方法真的挺多的:

(1)  大家应该很快能想到绝对定位

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .parent-box {
            background-color: #CCC;
            height: 600px;
            position: relative;
        }
        .child-box {
            position: absolute;
            width: 200px;
            height: 200px;
            top: 50%;
            left: 50%;
            margin-top: -100px;
            margin-left: -100px;
            background-color: #FF0000;
        }
    </style>
</head>
<body>
    <div class="parent-box">
        <div class="child-box"></div>
    </div>
</body>
</html>

绝对定位加上margin是可以实现水平垂直居中的,但是这种方法前提要给子元素定宽高


(2)还是用绝对定位,但是这种不需要给子元素定宽高

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .parent-box {
            background-color: #CCC;
            height: 600px;
            position: relative;
        }
        .child-box {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: #FF0000;
        }
    </style>
</head>
<body>
    <div class="parent-box">
        <div class="child-box">
            这里有很多东西
        </div>
    </div>
</body>
</html>


(3)用flex布局

.parent-box {
    background-color: #CCC;
    height: 600px;
    display: flex;
    justify-content: center;
    align-items: center;
}
.child-box {
    background-color: #FF0000;
}

(4)用table-cell

.parent-box {
    background-color: #CCC;
    width: 1000px;
    height: 600px;
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
.child-box {
    display: inline-block;
    background-color: #FF0000;
}

(5)grid

.parent-box {
    background-color: #CCC;
    height: 600px;
    display: grid;
}
.child-box {
    background-color: #FF0000;
    align-self: center;
    justify-self: center;
}

更多方法可参考:juejin.cn/post/684490…

2、实现 x 符号

<style>
    .cross {
        margin-top: 100px;
    }
    .cross::before {
        content: '';
        display: block;
        height: 2px;
        width: 100px;
        background-color: #000000;
        transform: rotate(45deg);
    } 
    .cross::after {
        content: '';
        display: block;
        height: 2px;
        width: 100px;
        background-color: #000000;
        transform: rotate(135deg);
    }
</style>

<div class="cross"></div>


3、移动端适配

大概的方案有百分比,flexible,vw、vh布局

flexible的原理是运用了rem,如下

// set 1rem = viewWidth / 10
function setRemUnit () {
    var rem = docEl.clientWidth / 10
    docEl.style.fontSize = rem + 'px'
}
setRemUnit();

rem 是相对于html节点的font-size来做计算的。所以在页面初始话的时候给根元素设置一个font-size,接下来的元素就根据rem来布局,这样就可以保证在页面大小变化时,布局可以自适应。

rem的好处是它既是相对的又是绝对的,可以避免产生字体大小逐层复合的连锁反应。

更多可参考:

juejin.cn/post/684490…

github.com/flashlizi/c…

juejin.cn/post/684490…