Javascript语言的执行环境是”单线程”(single thread)”单线程”就是一次只完成一件任务,如果有多个任务,就必须排队,等前面一个任务完成,再执行后面一个任务。这种就跟一般人的正常思维一样,也像单行道一样不堵车的时候跑起来很顺畅,但堵起来能堵死…如果其中某一个任务很耗时间的话,后面的任务就只能排队等着。很常见的像浏览器的无响应,也就是所谓的“假死”现象,我们会说先等会再操作,就是让耗时的任务执行完毕,后续任务能够依次被执行。
对这个问题,javascript也有对应的处理,即“同步模式”和“异步模式”,异步模式就显得非常重要了,在某个任务执行完后通过自身的回调函数来实现非顺序任务的执行,在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,如果所有的ajax请求我们都去设置成同步的,那本身javascript语言的“单线程”环境很明显会导致无响应现象.
可以使用 setTimeout
和 setInterval
来计划执行任务:
需要注意的是这种通过上面两个函数完成定时处理并不是ECMAScript
的标准,它们在 DOM
(文档对象模型) 被实现。
两者最主要区别:setTimeout
只运行一次,也就是说设定的时间到后就触发运行指定代码,运行完后即结束。如果运行的代码中再次运行同样的setTimeout
命令,则可循环运行。而 setinterval
是循环运行的,即每到设定时间间隔就触发指定代码。我个人更习惯用setTimeout
.
function testFun(){
alert("setTimeout");
}
setTimeout(testFun,2000); //如果写成testFun()就不会有定时作用,会立即被执行
setTimeout("testFun();",2000);
setTimeout(function(){//自己习惯的书写方式
testFun();
},2000);
上面的第一种调用方式,如果加上括号就会失去定时器作用,因为setTimeout
的第一个参数是函数对象,一个常犯的错误就是这样的 setTimeout(testFun(), 2000)
, 这里回调函数是testFun 的返回值,而不是testFun
本身。 大部分情况下,这是一个潜在的错误,因为如果函数返回 undefined
,setTimeout
也是不会报错。
当 setTimeout
被调用时,它会返回一个ID标识并且计划在将来大约 2000ms后调用testFun
函数。testFun
函数只会被执行一次。基于 JavaScript 引擎的计时策略,以及本质上的单线程运行方式,所以其它代码的运行可能会阻塞此线程。 因此没法确保函数会在 setTimeout
指定的时刻被调用。
作为第一个参数的函数将会在全局作用域中执行,因此下面这个函数内的 this
将会指向这个全局对象。
function TestFun(){ //this是指向全局的
this.value = 20160903;
this.method = function() {
console.log(this.value);
};
setTimeout(this.method, 1000);//undefined
var self = this;
setTimeout(function(){self.method()}, 3000);//20160903
}
new TestFun();
setTimeout
只会执行回调函数一次,不过setInterval
会周期性执行。不太鼓励和建议使用这个函数。当回调函数的执行被阻塞时,setInterval
仍然会发布更多的回调指令。在很小的定时间隔情况下,这会导致回调函数被堆积起来。也就是所谓的setInterval
堆调用了。
function testFun(){
//somecode--阻塞1000ms
}
setInterval(function(){testFun();}, 100);
上面代码中,testFun
会执行一次随后被阻塞了一秒钟。在 testFun
被阻塞的时候,setInterval
仍然在对回调函数调用。 这种情况下当第一次testFun
函数调用结束时,已经有 10 次函数调用在等待执行。处理可能的阻塞调用最简单的方法就是在回调函数内部使用 setTimeout
函数。
function testFun(){
//somecode--阻塞1000ms
setTimeout(function(){testFun();},100);
}
setInterval(function(){testFun();}, 100);
可以通过将定时时产生的 ID 标识传递给 clearTimeout
或者 clearInterval
函数来清除定时, 至于使用哪个函数取决于调用的时候使用的是 setTimeout
还是 setInterval
。
var id = setTimeout(testFun, 1000);
clearTimeout(id);
隐藏使用 eval
的情况:setTimeout
和 setInterval
也接受第一个参数为字符串的情况。 这个特性绝对不要使用,因为它在内部使用了 eval
。如下:
function testFun() {
//会被调用
}
function demo() {
function testFun() {
//不会被调用
}
setTimeout(testFun()', 1000);
}
demo();
由于 eval
在这种情况下不是被直接调用,因此传递到 setTimeout
的字符串会自全局作用域中执行;上面的回调函数使用的不是定义在demo
作用域中的局部变量 testFun
。建议不要在调用定时器函数时,为了向回调函数传递参数而使用字符串的形式。如果要传值,我们可以采用上面说的较常用的方式,通过使用匿名函数完成相同功能:
setTimeout(function(){
testFun(pra,prb,prc);
},200);
对于setTimeout
和setInterval
,一定不要使用字符串作为 setTimeout
或者 setInterval
的第一个参数, 这么写很明显会导致代码质量很差,工作中见到过很多这样的用法,我当时还很诧异,到底怎么用才是合适的,为什么给成字符串竟然也会被正常执行,现在知道了就要尽可能的去避免。当需要向回调函数传递参数时,可以创建一个匿名函数,如上,在函数内执行真实的回调函数。因为setInterval
的定时执行不会被 JavaScript
阻塞,所以在平常的编码中还是建议尽量去避免比较好的。
下来聊一下undefined
和 null
,两者都是javascript的基本数据类型,其余3个是Boolean
,Number
和String
,当然,复杂数据类型Object
就先不提了。
这两个中感觉比较有用的应该算undefined
,很明显它是一个值为undefined
的类型;如果定义一个变量但并不给赋值,那么它的值是 undefined
,这个变量也被称为 undefined
。 但是这个变量不是一个常量,也不是一个关键字。这意味着它的值可以轻易被覆盖。会返回undefined
的情况有以下几种:
- 访问未修改的全局变量 undefined
。
- 由于没有定义 return
表达式的函数隐式返回。
- return
表达式没有显式的返回任何内容。
- 访问不存在的属性。
- 函数参数没有被显式的传递值。
- 任何被设置为 undefined
值的变量。
如果一个变量没有声明就直接去访问,解释器会报错误信息,但是这样的变量如果使用typeof
返回的结果也是”undefined
“。Typeof
很重要,因为javascript
是松散类型的,在变量声明时并没有使用与之类型相对应的关键字,如果在代码中想要获知某个变量的基本数据量,就可以使用typeof
。这里要注意的是typeof
返回的是字符串类型。
1.”undefined”——未声明变量的值为undefined
或未初始化;
2.”boolean” ——如果这变量的值是布尔类型;
3.”string” ——值是字符串类型;
4.”number” ——值是数字类型;
5.”object” ——对象或者值为null
;
6.”function” ——函数。
null
的用处,JavaScript
中的 undefined
的使用场景类似于其它语言中的 null
,它在 JavaScript
内部有一些使用场景(比如声明原型链的终结 testFun.prototype = null
),但是大多数情况下都可以使用 undefined
来代替。Null
也是一个只有一个值的数据类型,任何变量只要给其赋值为null的话这个变量的数据类型就是Null
类型。null
值表示空对象指针,所以声明的变量要是想用来保存对象并且在声明之初还不能确定具体保存哪个对象的时候就可以将其赋值为null
,在使用的时候只要检查该变量是否为null
就可以知道该变量是否保存了对象。