基础知识

首屏加载有哪些优化?

重点可以说一下 Bigpipe 参考:
https://segmentfault.com/a/1190000006744741
其次是关于图片站点,可以讨论可不可以后端直接返回图片的编码,前端来解码渲染(讨论:每张图片渲染会不会阻塞后边图片渲染,类 base64 等问题),
淘宝暂时采用的是 webp,webp 虽然渲染比 jpg 慢一些,但是他体积小弥补了渲染慢的缺点,具体图片选择参考:
http://www.kuqin.com/shuoit/20160113/350009.html

已经加载到页面了,在切换选项卡的时候,避免再重新向后台发送请求,可以用什么方法解决

可以判断需要重新插入数据的地方是否有数据,如果已经有数据了就不用再调用了

怎么判断 ajax 是否连通

我的回答是判断 length>0 这种方法可行但是不好吧?
如果是用原生 XHR 的话 通过onreadystatechange事件,判断链接状态readyState == 4 (4 写成 complete 也行)和状态码xhr.status >= 200 && xhr.status < 300 || xhr.status == 304 (304 好像是缓存)

不重排会导致重绘吗

会!

position:absolute 是相对谁的定位

生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。
并不会追溯到 position:relative 才定位

防止 XSS 攻击

我说的是转换成 text 形式
他说并不安全还有其他方法?

get 和 post 区别

其实本质上没有区别,都是 TCP 链接并无差异,只是由于HTTP 的规定和浏览器/服务器的限制导致下列差异。
GET 在浏览器回退时是无害的,而 POST 会再次提交请求。
GET 产生的 URL 地址可以被 Bookmark,而 POST 不可以。
GET 请求会被浏览器主动 cache,而 POST 不会,除非手动设置。
GET 请求只能进行 url 编码,而 POST 支持多种编码方式。
GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留。
GET 请求在 URL 中传送的参数是有长度限制的,而 POST 么有。
对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制。
GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。
GET 参数通过 URL 传递,POST 放在 Request body 中。

唯一的重大区别就是GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。
理解:
GET:浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);
POST:浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。

整个 HTTP 请求的过程及渲染页面过程

看脑图及高性能

什么时候 script 放上边

当首屏渲染完毕,但是下边还没有显示完成,但是此时用户已经开始了点击动作,所以这个点击动作的 js 应该放在上边.

新的动画 API

requestAnimationFrame() 按一帧一帧(16.7ms)的自动刷新
如果 setTimeout 的时间为 16.7ms 的话就是一样的了

CMD/AMD/CommonJS 规范区别

  1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。
    不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。

  2. AMD 推崇依赖前置,CMD 推崇依赖就近。

  3. CommonJS 是同步的(只有加载完成才能执行后面的操作),但是 CMD/AMD 是异步的

  4. webpack 对 CommonJS 、 AMD 、ES6 的语法做了兼容,最终采用的都是 CMD 规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// CMD
define(function (require, exports, module) {
var a = require('./a');
a.doSomething();
// 此处略去 100 行
var b = require('./b'); // 依赖可以就近书写
b.doSomething();
// ...
});

// AMD 默认推荐的是
define(['./a', './b'], function (a, b) {
// 依赖必须一开始就写好
a.doSomething();
// 此处略去 100 行
b.doSomething();
// ...
});

tab 事件和点击穿透

tap 就是 touchstart 代替 click
穿透:就是如果点击蒙版的话,蒙版底下有个 a 链接或者 click 事件,300ms 后就会被触发
解决:不用 click 事件
更多解决办法:http://www.tuicool.com/articles/6NfaUnM
[5 步解决移动设备上的 300ms 点击延迟]https://www.cnblogs.com/vanstrict/p/5700957.html

跨页面

方法一:iframe
子页面访问父页面 js 变量(注:父页面的 js 变量需为全局变量)

1
子页面var variable = parent.variableParent   (variableParent为父页面定义的变量)

父页面访问子页面 js 变量

1
这部分目前的解决方案是在父页面设置全局变量,在子页面进行修改后将子页面的变量赋值给父页面

方法二:localStorage
sessionStorage 在新标签页无法访问,且关闭后被清空,但是刷新不清空
localStorage 持久化存储,新标签可以访问,关闭后不被清空,只能通过localStorage.clear();清空

jsonp

后台返回的就是一个对象
前台可以通过 jquery 调用
也可以通过动态插入 script 的方法进行插入
参考:http://www.jb51.net/article/42472.htm

log()代理 console.log()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//通常做法
function log(){
console.log.apply(console,argument) //console.log(内部的this)指向了window,需要将他重新指向console
}


//Stack Overflow做法
var log = console.log.bind(console); //bind是基于apply的语法糖


// 理解:
var obj = {
a: 1,
foo: function f() {
console.log(this.a);
}
}

obj.foo(); //1

var g = obj.foo; //this丢失,指向了window,所以后边需要让他重新绑回console
g(); //undefined

g = obj.foo.bind(obj);
g(); //1

伪数组转化为数组

1
var a = [].prototype.slice.apply(argument)    //argument也可以换成要替换的数组

兼容 ready

1
2
3
4
5
6
7
8
9
10
11
//http://www.html5jscss.com/mian_ready.html
//我简单的通过判断浏览器来实现
function domContentLoaded(element, type, handler){
if (element.addEventListener){
element.addEventListener('DOMContentLoaded', handler, false);
} else if (element.attachEvent){
if (document.readyState == "complete") {
handler();
}
}
},

img 和 input 是什么元素

  1. 是 inline 元素但是可以理解为 inlink-block 元素
  2. 可以设置宽高
  3. 宽高可以超过父级元素的边框

z-index

  1. 先比较同级,内部永远不会超出同级的比较
  2. 内部和外部比较:z-index 相同的时候,内层在上
  3. a 内层和 b 比较(b 和 a 的父级是兄弟级),a 内层再大也没用,也是 a 外部和 b 做比较

执行顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function executor(resolve) {
console.log(2);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5);

执行顺序为 2 3 5 4 1
原因:
在 Nodejs 事件循环机制中,有任务两个队列:Macrotask 队列和 Microtask 队列。
在一个事件循环里,这两个队列会分两步执行,第一步会固定地执行一个(且仅一个)Macrotask 任务,第二步会执行整个 Microtask 队列中的所有任务。并且,在执行 Microtask 队列任务的时候,也允许加入新的 Microtask 任务,直到所有 Microtask 任务全部执行完毕,才会结束循环。

Macrotasks 一般包括: setTimeout, setInterval, setImmediate, I/O, UI rendering;
Microtasks 一般包括: process.nextTick, Promises, Object.observe, MutationObserver。

参考资料
参考资料

算法

1.对象属性排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var objectList = [];
var newArr = [];

function Persion(name, age) {
this.name = name;
this.age = age;
}
objectList.push(new Persion('jack', 20));
objectList.push(new Persion('tony', 25));
objectList.push(new Persion('stone', 26));
objectList.push(new Persion('mandy', 23));

var newArr = objectList.sort(function(a,b){
return a.age - b.age
})
console.log(newArr);

2.单词反序

1
2
3
4
5
6
7
8
9
10
var str = 'we are champion';
var aStr = str.split(" ");
var newArr = [];
for (i = 0; i < aStr.length; i++) {
var singleArr = aStr[i].split('');
var newSingleArr = singleArr.reverse().join('');
newArr.push(newSingleArr);
}
var result = newArr.join(' ');
console.log(result)

3.冒泡算法

4.快排

5.给一个 8 位的数字让客户很好辨认是多少位

例如:1234567890 转化为 1,234,567,890
思路:
方法一:用正则(从后边开始每三位添加一个逗号–还没有实现不知道对不对)
方法二:数组的 splice 方法

1
2
3
4
5
'12334565632'.replace(/(\d)(?=(\d{3})+$)/g, "$1,");      //没有小数点的方法
'12334565632.55'.replace(/(\d)(?=(\d{3})+\.)/g, "$1,"); //小数点后保留的方法
//★★★★★ 重点说一下零宽断言 主要是那个+立了大功
//\d{3})+$ 这个式子展开就是 334,565,632 取到了这三组数,前边那两位是取不到的,(\d)只去到了2,把2替换成了2,
//还有就是如果数字字符是2的话,因为是+而不是*所以是匹配失败的,匹配失败的话就还是显示2

6.让 12345 转换成一万两千三百四十五

1
var a = [零,一,二,三,四];

7.点击循环显示 index

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<ul id="content">
<li class="a"></li>
<li class="a"></li>
<li class="a"></li>
</ul>

//jquery 写法
//$( "#content li" ).each(function( index ) {
// $(this).on('click',function(){
// console.log( index );
// })
//});

//原生
var li = document.querySelectorAll("#content li")
li.forEach(function(el, index) { //这个index是参数
el.addEventListener('click', function() {
console.log(index)
}, false)
})

//最复杂写法
// for (i = li.length; i--;) {
// (function(){
// li[i].index = i; //给添加一个index索引
// li[i].addEventListener('click', function () { //事件监听里的function的this是指向window的,所以需要给他重新bind对象
// console.log(this.index)
// }.bind(li[i]), false)
// })(i);
// }

8.通过正则替换多个变量

1
2
3
4
5
6
7
var name = 'zhang peng';
name.replace(/(\w+)\s+(\w+)/g, function ($1, $2) { //每个()算一个表达式,分别用$1 $2···以此类推
$1 = 'hehe';
$2 = 'gaga';
return $2 + 'wo' + $1
})
//"gagawohehe"

9.实现拖动效果

1
2


参考文献:

Daily-Interview-Question
Weekly-FE-Interview

箭头函数有哪些特点?

没有 this
不能使用 new 构造函数
不绑定 arguments,用 rest 参数…解决
捕获其所在上下文的 this 值,作为自己的 this 值
箭头函数没有原型属性
箭头函数不能当做 Generator 函数,不能使用 yield 关键字
箭头函数不能换行
箭头函数和普通函数的区别

promise 和 async await 区别

Async/Await:比 Promise 更好的 6 个理由

将对象下所有 key 由驼峰/Pascal 转成下划线分割

change({
UserName: ‘toutiao’,
Group: {
groupName: ‘douyin’
}
}) => {
user_name: ‘toutiao’,
group: {
group_name: ‘douyin’
}
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 深拷贝转驼峰
function change(obj) {
function dp(ob) {
let newObj = {};
Object.keys(ob).forEach((item) => {
const key = 数组.join('_');
if (typeof ob[item] == 'object') {
newObj[key] = dp(ob[item]);
} else {
newObj[key] = ob[item];
}
});
return newObj;
}
return dp(obj);
}
  1. 串行实现一个带有重试功能的 get 方法,参数为请求的 url 和重试的次数

getWithRetry(url: string, time: number): Promise

getWithRetry(‘/username’, 3).then((res) => {
console.log(res)
}, () => {
console.error(‘’)
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function getWithRetry(url, time) {
let count = 0;
async function getApi() {
if (count >= time) return '达到最大值';
let result = await this.$get({ url })
.then((res) => {
return res;
})
.catch(() => {
return false;
});
if (result) {
return result;
} else {
count++;
getApi();
}
}
getApi();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function myGetData(getData, times, delay) {
return new Promise(function (resolve, reject) {
function attempt() {
getData()
.then(resolve)
.catch(function (erro) {
console.log(`还有 ${times} 次尝试`);
if (0 == times) {
reject(erro);
} else {
times--;
setTimeout(attempt(), delay);
}
});
}
attempt();
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 查找左右相同的索引
function search(arr, target){
let l = 0
let lWhile = true
let r = arr.length-1
let rWhile = true
while(l<=r&&(lWhile||rWhile)){
if(arr[l] != target){
l++
}else{
lWhile=false
}
if(arr[r] != target){
r--
}else{
rWhile=false
}
}
if(l==r){
return [l]
}
return [l, r]
}
console.log(search([5,7,7,8,10], 8))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 奇偶排序
function sort(arr){
let one = []
let two = []
let newArr = []
arr.forEach((item)=>{

if(item%2 == 0){
two.push(item)
}else{
one.push(item)
}
})
while(one.length&&two.length){
if(two.length>=one.length){
newArr.push(two.shift())
}else{
newArr.push(one.shift())
}
}
return newArr.concat(one, two)
}
console.log(sort([4,2,5,7]))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 执行顺序 1 2 4 undefined timerStart timerEnd success
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});
promise.then((res) => {
console.log(res);
});
console.log(4);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 三张图片一起加载
function concurrency(limit) {
let count = 0;
function dp() {
if (count > limit) return;
count++;
const img = new Image();
img.onload = function () {
if (count == limit) {
count--;
dp();
}
};
dp();
}
dp();
}
← Prev Next →