您的位置:首页 > Web前端 > JavaScript

2016.3.23__ JavaScript基础_3__第十四天

2016-03-23 10:27 246 查看
最近感冒了,身体太疲惫,同时我也发现每天更新一篇确实不能保证我的文章的质量,与其每天写一些水文,不如静下心来把一些知识梳理好再呈现给大家。

所以很抱歉的通知大家,这个系列从今天起,更新日期由每日一更改为3~5日一更,实际时间只会更短,不会更长。

同时也很感谢很多小伙伴这么长时间的陪伴,谢谢大家。

我的文章简书专题连接:http://www.jianshu.com/collection/134d5825d813



1. 定时器

在我们的日常开发中,经常会需要让某一个效果等待多少秒之后才去触发,这也就引出了我们今天要学习的内容,定时器

1.1 setTimeout()

setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。

提示: 1000 毫秒 = 1 秒.

setTimeout(code,millisec,lang)


参数描述
code必需。要调用的函数后要执行的 JavaScript 代码串。
millisec必需。在执行代码前需等待的毫秒数。
lang可选。脚本语言可以是:JScript
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<p>点击按钮,在等待 3 秒后弹出 "Hello"。</p>
<button onclick="myFunction()">点我</button>

<script>
function myFunction()
{
setTimeout(function(){alert("Hello")},3000);
}
</script>

</body>
</html>


清除定时器:cleanTimeout

cleanTimeout(myFunction);


1.2 setInterval()

setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。

setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。

由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。

提示: 1000 毫秒= 1 秒。

setInterval(code,millisec,lang)


参数描述
code必需。要调用的函数后要执行的 JavaScript 代码串。
millisec必需。在执行代码前需等待的毫秒数。
lang可选。脚本语言可以是:JScript
<html>
<body>

<input type="text" id="clock" />
<script type="text/javascript">
var int=self.setInterval("clock()",1000);
function clock()
{
var d=new Date();
var t=d.toLocaleTimeString();
document.getElementById("clock").value=t;
}
</script>

<button onclick="int=window.clearInterval(int)">停止</button>

</body>
</html>


清除定时器:clearInterval

clearInterval(clock);


1.3 setTimeout 和 setInterval 的区别?

在上方的两个模块中,大家都看见了,这两个方法全部都是定时器,但是咱们发现两个除了名字不同,貌似没什么区别,那为什么还要有两个方法呢?

因为setTimeout(表达式,延时时间)在执行时,是在载入后延迟指定时间后,去执行一次表达式,记住,次数是一次 。

而setInterval(表达式,交互时间)则不一样,它从载入后,每隔指定的时间就执行一次表达式 。

所以,完全是不一样的

很多人习惯于将setTimeout包含于被执行函数中,然后在函数外再次使用setTimeout来达到定时执行的目的 。

这样,函数外的setTimeout在执行函数时再次触发setTimeout从而形成周而复始的定时效果 。

使用的时候各有各的优势,使用setInterval,需要手动的停止tick触发。

而使用方法中嵌套setTimeout,可以根据方法内部本身的逻辑不再调用setTimeout就等于停止了触发。

其实两个东西完全可以相互模拟,具体使用那个,看当时的需要而定了。

就像for可以模拟所有的循环包括分支,而还提供了do、while一样。

2.类型转换

JS 中类型转换分为强制转换和隐式转换。

2.1 强制转换

var a = "50";       //字符串 String
var b = 10;     //数字 Number

//字符串-->数字
var a = Number(a);

//提示 Number
alert(typeof a);

//数字-->字符串
var b = String(b);

//提示 String
alert(typeof b);


补充:

parsetInt() : 从左到右提取字符串中的整数,遇到非数字结束。(14.2312 取出来就是14)

parsetFloat(): 提取字符串中的小数

Number() : 将字符串转化为数字

String() : 将数字转换为字符串

2.2 隐式转换

//隐式类型转换
var a = "5"; //字符串类型
var b = 10 ; //数字类型
alert(a-b); //隐式转换 将字符串“5”转换为数字 5


3. 函数

函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。

或者说,计算机编程语言中的函数是指可以完成某些功能的一段固定代码。

3.1 如何定义函数

3.1.1 有名函数(别问我这个是什么鬼,我也不知道,23333)

函数就是包裹在花括号中的代码块,前面使用了关键词 function:

function functionname()
{
这里是要执行的代码
}


当调用该函数时,会执行函数内的代码。

可以在某事件发生时直接调用函数(比如当用户点击按钮时),并且可由 JavaScript 在任何位置进行调用。

提示:JavaScript 对大小写敏感。关键词 function 必须是小写的,并且必须以与函数名称相同的大小写来调用函数。

3.1.2 匿名函数

var MR_LP = function(x){
if(x > 0){
return x;
}
else{
return -x;
}
};


咱们发现上面的函数,并没有具体的函数名,但是它将函数赋值给了变量 MR_LP,所以我们的 MR_LP就可以调用该函数。

3.1.3 总结

这两种定义实际上完全等价,只是需要注意第二种方式按照完整的语法,需要在函数体末尾加一个 ” ; “.表示赋值语句结束。

3.2 调用带参数的函数

在调用函数时,您可以向其传递值,这些值被称为参数。

这些参数可以在函数中使用。

您可以发送任意多的参数,由逗号 (,) 分隔:

myFunction(argument1,argument2)

//当您声明函数时,请把参数作为变量来声明:
function myFunction(var1,var2)
{
这里是要执行的代码
}


变量和参数必须以一致的顺序出现。第一个变量就是第一个被传递的参数的给定的值,以此类推。

3.3 带有返回值的函数

有时,我们会希望函数将值返回调用它的地方。

通过使用 return 语句就可以实现。

在使用 return 语句时,函数会停止执行,并返回指定的值。

function myFunction()
{
var x=5;
return x;
}


上面的函数会返回值 5。

注释:整个 JavaScript 并不会停止执行,仅仅是函数。JavaScript 将继续执行代码,从调用函数的地方。

3.4 如何调用函数

我们调用函数只需要通过
函数名 + 参数
即可完成调用,注意当函数有参数的时候,一定要按照顺序传入参数。

大家还记得刚才的匿名函数么?我们可以直接通过调用
MR_LP
,就可以直接调用函数,例如这样:

MR_LP(100); //返回100
MR_LP(-99); //返回99,函数内取反了


而且由于 JavaScript 允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数。

MR_LP(100,'13115235');  //返回100
MR_LP(-99,'asdasd','aqweqvx');  //返回99,函数内取反了


当然,传入的参数比定义的要少也没有问题。

MR_LP(); 返回 NaN


此时函数的参数将受到 undefined,计算结果为 NaN (Not a Number)。

所以我们在日常的开发中,一定要进行参数的预判断操作。

var MR_LP = function(x){

//判断参数是否是 Number 类型
if(typeof x !== 'number'){
//抛出异常
throw 'Not a number';
}
if(x > 0){
return x;
}
else{
return -x;
}
};


3.5 return 的大坑

在 JavaScript 引擎中有一个在行末自动添加分号的机制,但是这个机制也有可能会让你栽一个大跟头。(参考苹果公司的那个好多年的 return 大坑)

function lol(){
return{ name : 'lol'};
}

lol();  //{name : 'lol'}


但如果我们如果把 return 语句分成了两句。

function lol(){
return;         //return 的结尾被自动添加上了分号,相当于 return undefined;
{ name : 'lol'};    //永远不会被执行到
}


所以我们平常书写的时候,可以采用这种写法:

function lol(){
return{     //这时候不会自动添加分号,因为 {} 代表语句未结束
name : 'lol'
};
}


4. arguments

JavaScript 还有一个免费赠送的关键字
arguments
,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments 类似 Array,但实际并不是。

function foo(x){
alert(x);   //10
for(var i = 0 ; i < arguments.length ; i ++){
alert(arguments[i]);    //10 , 20 , 30
}
}
foo(10, 20, 30);


利用 arguments,你可以获得调用者传入的所有参数。

也就是说函数不定义任何参数,还是可以拿到参数的值。

function MR_LP(){
if(arguments.length === 0 ){
return 0;
}
var x = arguments[0];
return x >= 0 ? x : -x;
}
MR_LP(0);       //0
MR_LP(100); //100
MR_LP(-99); //99


实际上 arguments 最常用于判断传入参数的个数。你可能会看见这样的写法:

// foo(a,,c)
//接收2~3个参数,b 是可选参数,如果只传入2个参数,b 默认为 null;
function foo(a,b,c){
if(arguments.length === 2){
//实际拿到的参数是 a 和 b,c 为 undefined
c = b;  //把 b 赋值给 c
b = null;   //b 变为默认值
}
}


要把中间的参数 b 变为“可选”参数,就只能通过 arguments 判断,然后重新调整参数并赋值。

5. rest 参数

由于 JavaScript 函数允许接收任意个参数,于是我们就不得不用 arguments 来获取所有参数:

function foo(a,b){
var i,rest = [];

if(arguments.length > 2){
for(i = 2; i < arguments.length; i++){
rest.push(arguments[i]);
}
}
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}


为了获取除了已定义参数 a , b 之外的参数,我们不得不用 arguments , 并且循环要从索引2 开始以便排除前两个参数,这种写法很别扭,只是为了获得额外的 rest 参数,那我们有没有更好的写法?

ES6 标准引入的 rest 参数,上面的函数可以改写为:

function foo(a, b ... rest){
console.log('a = ' + a);
console.log('b = ' + b);
sonsole.log(rest);
}

foo(1,2,3,4,5);
//结果:
//  a = 1
//  b = 2
//  Array [ 3, 4, 5]

foo(1);
//结果:
//  a = 1
//  b = undefined
//  Array []


rest 参数只能写在最后,前面用
...
标识,从运行结果可知,传入的参数先绑定 a , b,多余的参数以数组的形式交给变量 rest, 所以不再需要 arguments 我们就获取了全部参数。

如果传入的参数连正常定义的参数都没填完,也不要紧,rest 参数会接收一个空数组(注意:不是 undefined)。

因为 rest 参数是 ES6 新标准,所以我们在使用时需要注意兼容性的问题。

6.变量及作用域

在 JavaScript 中,用 var 声明的变量实际上是有作用域的。

如果一个变量在你函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可以用该变量。

function foo(){
var x = 1;
x = x + 1;
}

x = x + 2;  //referrenceError ! 无法再函数体外引用变量 x


如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。

换句话说,不同函数内容的同名变量相互独立,互不影响:

function foo(){
var x = 1;
x = x + 1;
}

function bar(){
var x = 'A';
x = x + 'B';
}


由于 JavaScript 的函数可以嵌套,此时内部函数可以访问外部函数定义的变量,反过来却不行。

function foo(){
var x = 1;

function bar (){
var y = x + 1;  //bar 可以访问 foo 的变量 x!
}
var z = y + 1;  //ReferenceError! foo 不可以访问 bar 的变量 y !
}


如果内部函数和外部函数的变量名重名怎么办?

function foo(){
var x = 1;

function bar(){
var x = 'A';
alert(' x in bar() = ' + x);    //'A'
}
alert('x in foo() = ' + x); // 1
bar();
}


这说明 JavaScript 的函数在查找变量时从自身函数定义开始,从“内”向“外”依次查找。

如果内部函数定义的函数名与外部函数有重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。

6.1 变量提升

JavaScript 的函数定义有个特点,它会先扫描整个函数体中的语句,把所有申明的变量“提升”至函数的顶部;

function foo(){
var x = 'hello, ' + y;
alert(x);
var y = 'larra';
}

foo();


语句
var x = 'hello' + y;
并不会报错,原因是变量
y
在稍后直接声明了,但是我们的 alert 会提示
hello, undefined
,这说明我们的变量
y
的值是 undefined。

[b]这正是因为 JavaScript 引擎自动提升了变量 y 的声明,但是不会提升变量 y 的赋值。


所以我们在函数内部定义变量的时候,请严格遵守函数内部首先声明所有变量的原则,最常见的就是用一个 var 声明函数内部所有用到的变量。

function foo(){
var
x = 1;          //x 初始值为 1
y = x + 1;  //y 初始值为 2
z,i;            //z 和 i 为 undefined

//其他语句
for(i = 0; i<100; i++){
...
}
}


6.2 全局变量

不在任何函数内定义的变量就具有全局作用域。

实际上,JavaScript 默认有一个全局对象 window (也就是浏览器对象),全局作用域的变量实际上被绑定到 window 的一个属性:

var course = 'learn JavaScript';
alert(course);
alert(window.course);


因此直接访问全局变量 course 和访问 window.course 是完全一样的。

由于函数的定义有两种方式,以变量的方式
var foo = function(){}
定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到 window 对象:

'use strict';

function foo(){
alert('foo');
}

foo();              //直接调用 foo();
window.foo();       //通过 window.foo()调用


由此我们也可以做一个更大胆的猜测,我们每次调通的 alert() 函数其实也应该是 window 的一个变量。

注意:

JavaScript 实际上之哟偶一个全局作用域。任何函数(函数也视为变量),如果没有在当前函数作用域中找到,那么就会继续向上层去查找,如果最后在全局中也没有找到,就直接报 ReferenceError 的错误。

6.3 局部变量

由于 JavaScript 的变量作用域实际上是函数内部,,我们在 for 循环等语句块中是无法定义具有局部作用域的变量。

function foo(){
for(var i = 0;i < 100; i++){
//
}
i +=100;    //仍然可以引用变量 i
}


所以为了解决块级作用域,ES6引入了新的关键字
let
,用 let 替代 var 可以声明一个块级元素作用域的变量:

function foo(){
var sum = 0;
for(let i = 0; i< 100 ; i++){
sum +=i;
}
i +=1;  //SyntaxError
}


一个坑

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<!--a[href=###]{我是按钮$}*4-->
<a href="###">我是按钮1</a>
<a href="###">我是按钮2</a>
<a href="###">我是按钮3</a>
<a href="###">我是按钮4</a>
</body>
<script type="text/javascript">
var links = document.getElementsByTagName("a");
for (var i = 0 ; i < links.length ; i++) {
links[i].onclick = function(){
alert(i);   //我们会发现,无论点击哪个,弹出的结果都是4。因为我们的 i 作为局部变量,当运行完成后,i 就变成了4,之后不管你如何操作,它都是4了。
}
}
</script>
</html>


6.4 常量

变量最明显的特征就是里面储存的值是可以随意改变的,常量则与之相反,常量一旦确定,则不能发生改变。

var PI = 3.14;


ES6标准出台之后,我们可以使用新的关键字 const 来定义常量,const 与 let 都具有块级作用域。

const PI = 3.14;
PI = 3; //某些浏览器不会报错,但是无实际效果
PI;         //3.14


7.闭包

7.1 函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为返回结果返回。

我们来实现一个对 Array 的求和,通常,求和的函数是这样定义的:

function sum(arr){
return arr.reduce(function(x,y){
return x + y;
});
}
sum([1,2,3,4,5]);   //15


但是如果不需要立刻求和,而是在后面的代码中,根据需要再计算,那我们可以直接返回求和的函数。

function lazy_sum(arr){
var sum = function(){
return arr.reduce(function (x,y){
return x + y;
});
}
return sum;
}


当我们调用
lazy_sum()
返回的并不是求和的结果,而是求和的函数:

var f = lazy_sum([1,2,3,4,5]); //function sum()


调用函数
f
的时候才是真正计算求和的结果,而是求和函数:

f();   //15


在这个例子中,我们在函数 lazy_sum 中又定义了函数 sum,并且内部函数 sum 可以引用外部函数 lazy_sum 的参数和局部变量,当 lazy_sum 返回函数 sum 时,相关参数和变量都保存在返回的函数中,这种我们称之为“闭包”

需要注意,当我们调用 lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数

var f1 = lazy_sum([1,2,3,4,5]);
var f2 = lazy_sum([1,2,3,4,5]);
f1===f2;    //false


f1()和 f2() 的调用结果互不影响。

注意到返回的函数在其定义内部引用了局部变量 arr,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来容易,做起来可不容易。

另外需要注意,返回的函数并没有立即执行,而是知道调用了 f() 才执行,我们再来看另外一个例子:

function count(){
var arr = [];
for(var i = 1; i<=3;i++){
arr.push(function(){
return i * i;
});
}
return arr;
}

var resullts = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];


在上面的例子中,每次循环都创建了一个新的函数,然后把创建的三个函数都添加到一个 array 循环中并返回了。

你可能认为代用的 f1(),f2(),f3()的结果是 1,4,9.而实际情况是

f1();   //16
f2();   //16
f3();   //16


他们全部都是16,原因在于返回的函数引用了变量 i,但它却不是立即执行,等到三个函数全部执行完毕的时候,i 的值已经变成了 4 ,所以所有的结果都是 16.

所以在返回闭包的时候一定要注意一点: 返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果程序中必须使用的时候呢?

我们需要重新创建一个函数,用该函数的参数绑定循环变量当前的值,之后就不用管循环变量会怎么变,我们已经绑定到函数的参数的值是不会变的。

function count(){
var aarr = [];
for(var i=1;i<=3;i++){
arr.push((function(n){
return function(){
return n*n;
}
})(i));
}
return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1();   //1
f2();   //4
f3();   //9


注意在上面的程序中,我们创建了一个“匿名函数并立即执行”的方法。

(function(x){
return x*x;
})(3)


理论上讲,创建一个匿名函数并立即执行可以这儿写:

function (x){return x * x;} (3);


但是 JS 语法解析,会认为这个函数是错误的,会提示 SyntaxError。因此我们需要用括号吧整个函数定义括起来:

(function(x){return x*x;})(3);


而我们日常工作中,会将这个函数分开来写

(function(x){
return x*x;
})(3);


那我们之前说闭包功能非常强大,他强大在哪里呢?

在面向对象的程序设计语言内,如 Java 和 C++ ,要在对象内部封装一个私有变量,可以用 private 修饰一个成员变量。

在没有 class 机制,只有函数的语言里,借助闭包,同样可以封装一个私有变量。我们用 JavaScript 创建一个计数器:

function create_counter(initial){
var x = initial || 0;
return{
inc : function(){
x += 1;
return x;
}
}
}


使用的时候:

var c1 = create_counter();
c1.inc();   //1
c1.inc();   //2
c1.inc();   //3

var c2 = create_counter(10);
c2.inc();   //11
c2.inc();   //12
c2.inc();   //13


在返回的对象中,实现了一个闭包,该闭包携带了局部变量 x , 并且,从外部代码根本无法访问到变量 x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

闭包还可以把多参数的函数编程但参数的函数。

例如,要计算
x^y
可以使用
Math.pow(x,y)
函数,不过考虑到经常计算
x^2
或者
x^3
, 我们可以利用闭包创建新的函数
pow2
pow3
:

function make_pow(n){
return function (x){
return Math.pow(x,n);
}
}

//创建两个新的函数
var pow2 = make_pow(2);
var pow3 = make_pow(3);

pow2(5);    //25
pow3(7);    //343


8.Match 函数

大家注意到,上面我们使用了一个新的函数,
Match.pow(x,y)
,这个函数的作用是:返回底数(x)的指定次(y)幂,Match 本身就是一个庞大的数学函数库,下面我们再来学习一个新函数 :随机数
Math.random();
,结果为 0 - 1 之间的一个随机数 (包括0,不包括1)

例如我们现在来生产一个 1-10之间的随机数:

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<script type="text/javascript">
for (var i =0;i< 10;i++) {
alert(parseInt(Math.random() * 10));
}
</script>
</head>
<body>

</body>
</html>


9. eval 函数

eval();
是 JS 中的函数,可以将字符转化为可执行的 JS 代码,下面我们来看几个例子:

var a = "5 + 10";
alert(eval(a)); //15

var str = "function(){alert('a')}";

//直接变函数 等价 var str = function(){alert('a')};
str = eval("("+str+")");
str();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息