/*
*SetTimeout()的用法
*回调函数
*/
var learingSetOneTime = null;
var learingSetSecondTime = null;
function learningSetTimeout(){
console.log(a);
learingSetOneTime = setTimeout(function(){
console.log(a);
},0);
learingSetSecondTime = setTimeout(function(){
console.log(a);
},1000);
var a = 2;
}
输出结果:undefined 2 2
还是那句话 同步 异步 回调
/*
*在这讨论下之前在知乎看到的 80% 应聘者都不及格的 JS 面试题setTimeout
*因为涉及到我还比较感兴趣的ES6,所以我也来谈一谈
*/
function showSetResult(){
for(var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}
}
showSetResult();
//输出结果:在这里提个建议:凡是看到代码,在情况允许的条件下还是在电脑上跑一下比较好,自己看下结果 == > 隔1秒一次性输出5个5;
至于原因还是那句话:回调函数;
/*
*回调函数的执行:这里涉及到javascript单线程如何处理回调的?
*
*javascript的同步代码在JavaScript同步的代码是在堆栈中顺序执行的,而setTimeout回调会先放到消息队列,for循环每执行一次,就会放一个setTimeout到消息队列排队等候,当同步的代码执行完了,再去调用消息队列的回调方法。i是showSetResult()函数的局部变量,没有记忆的过程,所以当同步函数执行完了之后for循环已经完成了,此时i的值为5.
*/
这里提到了堆栈的问题:
就简单提一下堆栈的问题:
什么是栈?存储原始数据类型和引用数据类型的实体的起始地址
原始数据类型:undefined、null、string、number、boolean,存储在栈(stack)中的简单段,占用空间小,大小固定,属于被频繁属于数据
什么是堆:存储引用类型实体
引用类型:Array、Object、function,占用空间大,大小不固定。通过起始地址的指针访问实体数据位置
当解析时,会先检索其再栈中的地址,取得地址之后再从堆中获取实体数据
/*
*进一步改进,输出0,1,2,3,4
*在javascript中没有块级作用域的问题(ES6有),通过立即执行函数可以实现块级作用域
*/
function showSetResult(){
for(var i = 0; i < 5; i++){
(function(i){
setTimeout(function(){
console.log(i);
},1000);
}(i))
}
}
showSetResult();
/*
*立即执行函数:最外层的大括号是将括号里的函数作为函数表达式来解析,将i作为参数传给立即执行函数。相当于将i做了记忆,此时是隔一秒一次性输出0,1,2,3,4,我如果想要每隔一秒输出一个呢?
*/
function showSetResult(){
for(var i = 0; i < 5; i++){
(function(i){
setTimeout(function(){
console.log(i);
},1000 * i);
}(i))
}
}
showSetResult();
/*
*此时输出就是每个一秒输出1个 数,0,1,2,3,4
*/
那ES6是怎么解决的呢?
上边提到了块级作用域:ES6的let定义的就是用来解决这个问题的
let:声明的变量只在块级作用域中有效
const:声明一个常量,一旦声明,常量的值就不能改变,只有在声明所在的块级作用域中有效
使用let声明的变量就不存在变量提升的问题了,在该变量声明之前,该变量是不可用的。
function showSetResult(){
for(let i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
},1000 * i);
}
}
showSetResult();
/*
*此时你可以运行看看,结果是Error
*/
这就是let,let的作用域是当前块。也就是for循环。在for中声明的let在setTimeout中是不可用的。在这里let本身就形成了一个闭包。外部是不能访问的。
其实这个就相当于:
function showSetResult(){
for(var i = 0; i < 5; i++){
showI(i);
}
}
function showI(i){
setTimeout(function(){
console.log(i);
},1000 * i);
}
showSetResult();
所以说:let其实就是个语法糖,那使用ES6怎么完成呢?
怎么改呢?
const tasks = [];
(function(j){
tasks.push(new Promise(function(resolve,reject){
setTimeout(function(){
console.log(j);
resolve(); //调用resove,不调用的话就不会异步调用到then
}, 1000 * j);
}));
}(i))
Promise.all(tasks).then(function(){
setTimeout(function(){
console.log(i);
}, 1000); // 注意这里只需要把超时设置为 1 秒
});
/*
*promise.all();
*参数数组的顺序一致,而不是按时间顺序排序
*如果数组中所有的Promise对象都resolve了,那么这些resolve的值将作为一个数组作为Promise.all这个方法的返回值的(Promise对象)的resolve值,之后可以被then方法处理。如果数组中任意的Promise被reject,那么该reject的值就是Promise.all方法的返回值的reject值.
*Promise.race()
*不是等待所有的promise被resolve或reject,而是所有的promise中只要有一个执行结束,它就会触发
*/
promises的真正强大之处在于多重的链接 == >
特性:
1)只有来自上一层的异常可以被捕捉
2)错误可以被恢复 == > 在一个错误回调函数中,如果你不重新抛出错误,promise会假设你已经从错误中恢复,并且反转成为已解决状态
3)Promises可以被暂停
4)Resolved Promises并不会立刻执行
两种情形:
1)假设传入的参数没有一个.then方法,那么这个返回的Promise对象变成了resolve状态,其resolve的值就是这个对象本身。
2)假设传入的参数带有一个then方法(称为thenable对象), 那么将这个对象的类型变为Promise,其then方法变成Promise.prototype.then方法。