setTimeout与console.log()执行顺序,局部变量和全局变量,var变量提升,this指向, 看不懂你来打我

53次阅读

共计 6318 个字符,预计需要花费 16 分钟才能阅读完成。

令人心痛的血淋淋教训,再犯这些错误我不是人。
setTimeout 与 console.log() 执行顺序
setTimeout 延时为 0 时,
setTimeout(function(){
console.log(2);
},0);
console.log(1);
// 输出顺序:1,2

setTimeout(function(){
console.log(4);
},0);
setTimeout(function(){
console.log(5);
},0);
console.log(1);
console.log(2);
console.log(3);
// 输出顺序:1,2,3,4,5

原因:(记住喽,记不住打死你!!!):页面中所有由 setTimeout 定义的操作,都将放在同一个队列中依次执行。而这个队列的执行时间需要等到函数调用栈执行完毕后才会执行,也就是等待所有的可执行代码执行完毕,才会轮到 setTimeout 执行其内部操作,并且按照其时延时间长短顺序执行代码!
再来个高深的:瞅着
setTimeout(function(){
console.log(“a:”+a);
},0);
var a = 1;
console.log(“b:”+b);
var b = 2;
var c = 3;
var d = 4;
var e = 5;
function fx(c){
console.log(“c:”+c);
}
function fn(e,d){
console.log(“d:”+d);
setTimeout(function(){
console.log(“e:”+e);
},10);
}
setTimeout(function(){
console.log(“b2:”+b);
},20);
fn(e,d);
fx(c);

输出结果:
原因:
1、console.log() 函数会在 setTimeout 函数之前执行,并且 b 在输出之前未被定义所以最先输出 undefined;
2、之后,会执行 fn 函数和 fx 函数,而 fn 函数内存在 console.log 函数,那么它将会先输出 d 的值 4;
3、然后,在 fx 函数内也存在 console.log 函数,同样会先输出 c 的值 3;
4、再来比较 setTimeout 函数时延长短,依次输出 1,5,2。

3)
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
}
//0 1 2 3 3 3
用到了闭包

4)
for (var i = 0; i < 3; i++) {}
console.log(i);
//3,也就说 i 可以在 for 循环体外访问到。所以是没有块级作用域。

5)
var i = 0;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;

等价于:
var i = 0;
console.log(i);
i++;
console.log(i);
i++;
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
setTimeout(function() {
console.log(i);
}, 0);
setTimeout(function() {
console.log(i);
}, 0); // 弹出 0 1 2 3 3 3

全局变量和局部变量的区别
1)
var a = “Hello”;
function test(){
var a;
alert(a);
a = “World”;
alert(a);
}
test();//undefined world

2)
var a = “Hello”;
function test(){
alert(a);
a = “World”;
alert(a);
}
test();//Hello World

3)
var a =1;
function test(){
alert(a);
var a = 2;
alert(a);
}
test();
alert(a); //undefined 2 1

Javascript 的变量的 scope 是根据方法块来划分的(也就是说以 function 的一对大括号{ }来划分)。是 function 块,而 for、while、if 块并不是作用域的划分标准,可以看看以下几个例子:
function test2(){      
alert (“before for scope:”+i);    
// i 未赋值(并不是未声明!使用未声明的变量或函数全抛出致命错误而中断脚本执行)// 此时 i 的值是 underfined      
for(var i=0;i<3;i++){
alert(“in for scope:”+i);  // i 的值是 0、1、2, 当 i 为 3 时跳出循环      
}     
alert(“after for scope:”+i);  // i 的值是 3,注意,此时已经在 for scope 以外,但 i 的值仍然保留为 3            
while(true){
var j = 1;          
break;      
}      
alert(j);    
// j 的值是 1,注意,此时已经在 while scope 以外,但 j 的值仍然保留为 1        
if(true){
var k = 2;      
}      
alert(k);  // k 的值是 1,注意,此时已经在 if scope 以外,但 k 的值仍然保留为 1  
}  
test2();  
// 若在此时(function scope 之外)再输出只存在于 test2 这个 function scope 里的 i、j、k 变量会发生神马效果呢?
alert(i); //error! 没错,是 error,原因是变量 i 未声明(并不是未赋值,区分 test2 函数的第一行输出),导致脚本错误,程序到此结束!
alert(“ 这行打印还会输出吗?”+i); // 未执行  
alert(j); // 未执行  
alert(k); // 未执行

Javascript 在执行前会对整个脚本文件的声明部分做完整分析 (包括局部变量),从而确定实变量的作用域。怎么理解呢?看下面一个例子:
var a =1;     
function test(){          
alert(a); // a 为 undefined! 这个 a 并不是全局变量,这是因为在 function scope 里已经声明了(函数体倒数第 4 行)一个重名的局部变量,                       
// 所以全局变量 a 被覆盖了,这说明了 Javascript 在执行前会对整个脚本文件的定义部分做完整分析, 所以在函数 test() 执行前,                       
// 函数体中的变量 a 就被指向内部的局部变量. 而不是指向外部的全局变量. 但这时 a 只有声明,还没赋值,所以输出 undefined。
a=4;                 
alert(a);  // a 为 4, 没悬念了吧?这里的 a 还是局部变量哦!
var a;     // 局部变量 a 在这行声明          
alert(a);  // a 还是为 4, 这是因为之前已把 4 赋给 a 了      
}      
test();      
alert(a); // a 为 1,这里并不在 function scope 内,a 的值为全局变量的值 

当全局变量跟局部变量重名时,局部变量的 scope 会覆盖掉全局变量的 scope,当离开局部变量的 scope 后,又重回到全局变量的 scope,而当全局变量遇上局部变量时,怎样使用全局变量呢?用 window.globalVariableName。
var a =1;      
function test(){             
alert(window.a);  // a 为 1, 这里的 a 是全局变量哦!
var a=2;     // 局部变量 a 在这行定义          
alert(a);  // a 为 2, 这里的 a 是局部变量哦!
}      
test();      
alert(a); // a 为 1,这里并不在 function scope 内,a 的值为全局变量的值

** 总结:(每个例子慢慢看,就全懂了)1、js 有两级作用域,全局和局部,局部也叫函数作用域 2、全局作用域的变量局部可以使用,局部作用域的变量只能在函数体内使用 3、var 和 function 声明的变量都声明提前,赋值留在原地 4、如果局部和全局变量重名,优先使用局部变量 5、第 3 条和第 4 条,解释了全局和局部都有相同变量名的时候,而在函数体内打印的变量是 undefined**
var 变量提升(这个问题我一直一知半解,现在懂了)
一般情况下,是可以省略 var 的,但有两点值得注意:
1、var a=1 与 a=1,这两条语句一般情况下作用是一样的。但是前者不能用 delete 删除。不过,绝大多数情况下,这种差异是可以忽略的。
2、在函数内部,如果没有用 var 进行申明,则创建的变量是 ** 全局变量,而不是局部变量 ** 了。所以,建议变量申明加上 var 关键字。

瞅着:1)
var t = 1;
function a(){
console.log(t);
var t=2;
}
a();//undefined;
输出 undefined,原因:function()中相当于
var t;
console.log(t);
t = 2;
表示变量 t 已声明,但还未赋值, 输出 undefined。

2)
但是变量提升只对 var 命令声明的变量有效,如果一个变量不是用 var 命令声明的,就不会发生变量提升。
console.log(aa);
aa =1;
以上代码将会报错:ReferenceError: aa is not defined。

3)
var t = 1;
function a(){
console.log(t);
t=2;
}
a();//1
4)
函数声明变量提升
foo();
function foo(){
console.log(“aaa”);
}
// 输出 aaa

原因:函数声明提升(函数声明提升直接把整个函数提到执行环境的最顶端)
它相当于
function foo(){
console.log(“aaa”);
}
foo();

5)
foo();
var foo = function(){
console.log(“aaa”);
}
它相当于:
var foo;
console.log(foo); //undefined
foo(); //foo is not a function
foo = function(){
console.log(“aaa”);
}
上面代码输出 undefined 是因为变量提升后并没有赋值因此输出 undefined

输出 foo is not a function 原因是:js 解析遇到 foo() 时会默认当做函数来解析

6)最厉害的一个

console.log(foo);
var foo=10;
console.log(foo);
function foo(){
console.log(10);
}
console.log(foo);

他相当于:
function foo(){
console.log(10);
}
var foo;
console.log(foo);
console.log(foo);
console.log(foo);

函数提升在变量提升上面,第一个 console.log(foo); 为什么会输出函数题呢,原因在于 var foo; 并未有赋值只是声明,因此他会调用上面的值.
this 指向

全局环境中,this 指向 window
console.log(this.document === document); // true

// 在浏览器中,全局对象为 window 对象:
console.log(this === window); // true

this.a = 37;
console.log(window.a); // 37

函数调用 非严格模式下,this 默认指向全局对象 window
function f1(){
return this
f1() === window; // true

而严格模式下,this 为 undefined
function f2(){
“use strict”; // 这里是严格模式
return this;
}
f2() === undefined; // true

1)
对象中的 this
var o = {
user:” 李钢铁 ”,
fn:function(){
console.log(this.user); // 李钢铁
}
}
o.fn();

这里的 this 指向的是对象 o,因为你调用这个 fn 是通过 o.fn() 执行的,那自然指向就是对象 o,这里再次强调一点,this 的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个。

2).
var o = {
user:” 李钢铁 ”,
fn:function(){
console.log(this.user); // 李钢铁
}
}
window.o.fn();
this 指向 o

3).
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //12
}
}
}
o.b.fn();
总结:如果一个函数中有 this,这个函数有被上一级的对象所调用,那么 this 指向的就是上一级的对象。
4).
var o = {
a:10,
b:{
// a:12,
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();
this 指向 b, 因为 this 只会指向它的上一级对象,不管这个对象中有没有 this 要的东西。

5).
特殊情况
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();

这里 this 指向的是 window,,this 永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子 4 中虽然函数 fn 是被对象 b 所引用,但是在将 fn 赋值给变量 j 的时候并没有执行所以最终指向的是 window。

4.
构造函数中 this
function Fn(){
this.user = “ 李钢铁 ”;
}
var a = new Fn();
console.log(a.user); // 李钢铁
指向实例化对象 a

当 this 遇到 return 时,如果返回值是一个对象,那么 this 指向的就是那个返回的对象,如果返回值不是一个对象那么 this 还是指向函数的实例。
function fn()
{
this.user = ‘ 李钢铁 ’;
return undefined;
}
var a = new fn;
console.log(a);
//fn {user: “ 李钢铁 ”}

function fn()
{
this.user = ‘ 李钢铁 ’;
return 1;
}
var a = new fn;
console.log(a.user);
// 李钢铁

特殊情况:虽然 null 也是对象,但是在这里 this 还是指向那个函数的实例,因为 null 比较特殊。
function fn()
{
this.user = ‘ 李钢铁 ’;
return null;
}
var a = new fn;
console.log(a.user); // 李钢铁

正文完
 0