头条内推面补坑-更新中
老样子,答的好的这里就不写了。只记录答的不好的的。
这次暴露出来的问题就是要多做项目,多实践。
1.改这个代码
//目标代码
const obj = {
name: " jsCoder",
skill: ["es6", "react", "angular"],
say: function () {
for (var i = 0, len = this.skill.length; i < len; i++) {
setTimeout(function(){
console.log(i);
console.log(this.skill[i]);
}, 0)
console.log(i);
}
}
};
obj.say();
第一种:
const obj = {
name: " jsCoder",
skill: ["es6", "react", "angular"],
say: function ()
{
var that = this;
for (var i = 0, len = this.skill.length; i < len; i++)
{
setTimeout((function()
{
console.log(i);
console.log(that.skill[i]);
})(i), 0)
console.log(i);
}
}
};
obj.say();
结果:
面试官当时就让我写在浏览器里的,但是我当时使用的webstorm,用的node环境,就报错了,结果就很慌。。。。。搞得最后不敢交。。这个代码可以在浏览器中执行的。
第二种:
同时在Node和浏览器(chrome)中都可以实现的:
const obj = {
name: " jsCoder",
skill: ["es6", "react", "angular"],
say: function ()
{
var that = this;
for (let i = 0, len = this.skill.length; i < len; i++)
{
(function () {
setTimeout(function () {
console.log(i);
console.log(that.skill[i])
})
})(i)
console.log(i);
}
}
};
obj.say();
结果是这样
两种结果虽然取到了我们想要的结果,但是和外层的console.log(i)出现了顺序的差异。第二种写法先打印完这个,再依次打印()()自执行函数的。
应该是执行第一个script宏任务的时候,遇到了三个()(),把这三个推进了微任务,当第一个宏任务结束之后,依次执行完本次遇到的所有的微任务。
第一种顺序打印,是因为每个setTimeout即是一次宏任务。但如果把setTimeout包括起来就是微任务了。
其他更多骚方法我都写在这里:
const obj = {
name: " jsCoder",
skill: ["es6", "react", "angular"],
say: function () {
this.skill.forEach(function(item,index){
setTimeout(function(){
console.log(index)
console.log(item);
},0)
})
},
say2:function(){
for (var i = 0, len = this.skill.length; i < len; i++) {
setTimeout(function(i,item){
console.log(i);
console.log(item);
},0,i,this.skill[i]);
}
},
say3:function () {
let i = 0;
const arr = this.skill.slice();
function self_shift() {
//shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。空返回undefined
const item = arr.shift();
if(item) {
console.log(i);
console.log(item);
i++;
setTimeout(self_shift, 0);
}
}
setTimeout(self_shift,0);
}
};
obj.say();
obj.say2();
obj.say3();
// 闭包了,里面的i跟外面的i不是同一个,加上自执行
const obj = {
name: " jsCoder",
skill: ["es6", "react", "angular"],
say: function () {
for (var i = 0, len = this.skill.length; i < len; i++) {
setTimeout(((j) => {
return () => {
console.log(j);
console.log(this.skill[j]);
}
})(i), 0)
console.log(i);
}
}
};
obj.say();
const obj = {
name: " jsCoder",
skill: ["es6", "react", "angular"],
say: function () {
var that = this;
for (let i = 0, len = this.skill.length; i < len; i++) {
setTimeout(function(){
console.log(i);
console.log(that.skill[i]);
}, 0)
console.log(i);
}
}
};
obj.say();
2.手写bind,bind还有其他什么作用?
这个内容较多,我另开了一篇博客好好说这个:http://www.cnblogs.com/zhangmingzhao/p/8660985.html
3.什么时候304什么时候200,相关浏览器缓存问题?
-
在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端最后被修改的时间,:
Last-Modified: Fri, 12 May 2006 18:53:33 GMT -
客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过:
If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
服务器端的程序先取得这个字段的值,然后与服务器上的数据最后修改时间对比,如果服务器端的资源没有变化,就直接返回 304 Not Modified 状态码,然后停止。这样就节省了传输数据量,达到节省带宽的目的。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。(发送200?)从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。 -
但是Last-Modified不是全能的,后来在Http1.1加入了Etag来标记,Etag的精确度更高
想象在这个一个情景——客户端上某个资源保存的缓存时间过期了,但这时候其实服务器并没有更新过这个资源,如果这个资源数据量很大,客户端要求服务器再把这个东西重新发一遍过来,是否非常浪费带宽和时间呢?
所以就有了Etag:
当你第一次请求一个资源的时候,server会同时生成并返回一个字符串在响应头里,叫Etag。
浏览器接到资源后,缓存资源的同时也会把这个Etag保存下来,成为If-None_Match 。Etag可以看作是一个资源的唯一标识,当你第二次请求这个资源的时候,请求头里会带着这个Etag,server会拿server该资源现在的Etag跟请求头中的If-None_Match做对比,然后看看If-Modified-Since过没过期,如果一样,直接告诉他:你就用本地的吧,我没变,就不给你返回了。所以返回了304,304就是这样。 -
200 From Cache
这个虽然是200,但他根本就没有跟server做交互,直接拉的本地缓存。 -
与浏览器缓存相关的还有Expires字段,这个字段存储的时间是相对服务器而言,无法保证和客户端时间统一”的问题,http1.1新增了 Cache-Control 来定义缓存过期时间,若报文中同时出现了 Pragma、Expires 和 Cache-Control,会以 Cache-Control 为准。Last-Modified之后的Etag就是为了解决,因为如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为Last-Modified时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)。