JavaScript基础知识

JavaScript导入方式

JavaScript存在两种导入方式。
方式一,直接在html代码中使用的方式来嵌入JavaScript的代码
方式二,通过src的方式来进行导入。如的方式来导入所有的demo.js的代码。需要注意的是在标签下的JavaScript的代码是无效的;其次,结束标签不能省略。

数据类型

JavaScript存在5中基本的数据类型:Undefined,Null,Boolean,Number和String。
可以通过typeof操作符来检测变量的数据类型。函数在JavaScript中是对象不是一种数据类型,所以使用typeof可以查看函数时返回的是function。

Boolean类型

Boolea类型有true和false,True和False不是。在JavaScript中所有的类型的都存在一个默认的Boolean类型的值,不是true就是false。

string类型

String类型中包含了大量的可用的内置方法。在很多情况下String类型的方法和接下来要讲到的数组的方法很多都是相同或者类似的。字符串也可以通过下标的方式来获取其中的字符。

  • str.concat(str1,…strn):将str+str1+…+strn拼接起来。
  • str.slice(n,m):返回字符串n到m之间位置的字符串。
  • str.substring(n,m):作用与slice()的用法相同。
  • str.substr(n,m):返回字符串n开始的m个字符串。

运算符

JavaScript定一个5个运算符,加减乘除求模。如果在算术运算的值不是数值,那么后台可能会使用Number()转型函数将其转化为数值。这种情况对于数值类型和字符串类型的运算尤其常见。但是也会存在一定的问题。
例如:var result = 100+‘100’,返回的是100100;var result=100-‘70’,返回的结果是30
所以具体的情况还需要自己进行尝试。

函数

JavaScript的函数声明和调用与其他的语言没有差别。

1
2
3
function sum(num1,num2) {
return num1+num2;
}

以上就是一个最简单的JavaScript的函数声明方式。
JavaScript中的函数可以通过arguments对象来接受所有传递进来的参数。如下:

1
2
3
4
5
6
7
8
fucntion sum(){
var sum = 0;
for(var i=0;i<arguments.length;i++) {
sum += arguements[i];
}
return sum;
}
sum(1,2,3,4,5,6,7,8,9);

通过arguments对象,sum调用是无论传递多少对象,sum函数都是可接受处理的。
要注意的是在JavaScript中没有函数重载的功能。如果存在2个同名函数,则后后者会将前者覆盖。

数组

JavaScript中的数组可以存放任何类型的变量。数组的创建方式也可以有很多种。如下。

1
2
var my_array = new Array(10):
var my_array = [1,1,2,'123',{type:'javascript'},[12,]];

对于第一种创建方式来说,new关键字可以去掉。同时10初始化数组的长度为10。第二种是自变量的创建方式,可以看出数组可以包含任意的数据类型,如对象,数字,字符串,数组等。
数组有一些常用的方法,如用于添加元素push()、pop()、join()、shift()、unshift();用于排序的reverse()和sort()方法;用于分片的concat()、slice()、splice()。

对象

JavaScript中的对象的创建方法也是十分的简单,从表面上就如同其他语言中的键值对形式的集合一样。

1
2
3
4
5
6
7
var person= {
name:'Tom',
age:'18',
run:function() {
return 'run...';
}
}

以上就是创建一个对象的简单的方法。访问时,通过person.name,person.run()就可以访问对象的属性和方法了。

正则表达式

声明方式为:var pattern=new RegExp(‘programming’,’ig’)或var pattern = \programming\ig。
常用方法有以下几个:

  • pattern.test(str):在字符串中测试模式匹配,返回true或false
  • pattern.exec(str):在字符串中执行匹配搜索,返回结果数组。如不存在匹配项,则返回null
  • str.match(pattern):返回pattern中的子串或null
  • str.replace(pattern,replacement):用replace替换pattern
  • str.search(pattern):返回字符串开始位置。若没有找到,则返回-1
  • str.split(pattern):返回字符串制定拆分的数组。

Function类型

函数声明的方式除了之前所讲到的方式之外,还可以使用变量来初始化函数。

1
2
3
4
5
6

var sum = function(num1,num2) {

return num1+num2;

}

由于函数名本身也是指向函数的对象指针,所以不仅可以将函数名作为一个参数传递给另一个函数,也可以将一个函数作为返回值返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

function sum(addtenFunction,num) {

return addtenFunction(num);

}



function addtenFunction(num) {

return num+10;

}

var result = sum(addtenFunction.10);

JavaScript中的函数是对象,所以也存在属性,分别为length,prototype。其中length表示函数希望介绍命名参数的个数,prototype中有apply()和call()2个方法。这2个方法的用途都是在特定的作用域中调用函数。

apply()的用法为:

1
2
3
4
5
6
7
8
9
10
11
12

function sum(num1,num2) {

return num1+num2;

}

function add(num1,num2) {

return sum.apply(this.arguments);

}

call()方法和apply()方法的作用相同。区别在于接受参数的方式不同。具体如下

1
2
3
4
5
6
7
8
9
10
11
12

function sum(num1,num2) {

return num1+num2;

}

function add(num1,num2) {

return sum.call(this,num1,num2);

}

实际上,apply()和call()方法经常是再由扩展函数赖以运行的作用域的。call()的调用方法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

var name = 'windowName'

var person = {

name:'personName',

}

function myName() {

alert(this.name)

}

myName.call(this); //作用域在Window

myName.call(window); //作用域在window

myName.call(person); //作用域在person,对象冒充

当使用myName(person)时,myName()方法的运行环境变成了person。

###匿名函数

正常情况下,没有函数名的函数是无法运行的。但是通过其他的方式,可以创建匿名函数。如下:

正常情况下,函数的声明和调用如下:

1
2
3
4
5
6
7
8

function box() {

return 'Lee';

}

alert(box());

但是通过表达式的自我执行,不需要先声明在调用。

1
2
3
4
5
6

(function box() {

alert('Lee');

})();

函数里的匿名函数:

1
2
3
4
5
6
7
8
9
10
11
12

function box() {

return function() {

return 'Lee';

}

}

alert(box()()); //调用匿名函数

闭包

闭包使用

闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。闭包的特点在于,可以将局部变量驻留在内存中,可以避免使用全局变量(全局变量的使用可能不利于程序之后的开发和维护)。

如果要实现一个变量的累加,一种方式是将该变量设置为全局变量。如下:

1
2
3
4
5
6
7
8
9
10

var num = 100;

function count() {

num++;

}

count(); //实现了变量的累加,变为101

使用闭包的做法,通过局部变量也可以实现变量的累加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

function count() {

var num=100;

return function() {

num++;

return num;

}

}

var mynum = count()

mynum(); //变为101

mynum(); //变为102

由于闭包里作用域返回的局部变量资源不会立刻销毁回收,所以可能会占用更多的内存,国度使用闭包会导致性能下降。一般情况下只有在非常有必要的时候才使用闭包。

闭包中的陷阱

由于作用链的机制,在循环中里的匿名函数取得的任何变量都是最后一个值。具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

function box() {

var arr=[];

for(var i=0;i<5;i++) {

arr[i] = function() {

return i;

};

}

return arr;

}

var b = box(); //得到函数数组

for(var i=0;i<b.length;i++) {

alert(b[i]()); //得到所有的结果均为5

}

造成所有的结果都是5的原因是在于b[i]调用的返回结果都是匿名函数,而此时匿名函数还没有执行。等到匿名函数调用的时候,box()已经执行完毕了,i已经变成了5,所有最终的结果都是5。

既然知道了原因,那么也就知道了应对的方法。

将box()中的匿名函数改为自我执行。变为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

function box() {

var arr=[];

for(var i=0;i<5;i++) {

arr[i] = (function(num) {

return num;

})(i);

}

return arr;

}

var b = box(); //得到函数数组

for(var i=0;i<b.length;i++) {

alert(b[i]); //得到所有的结果为1,2,3,4,5

}

虽然通过这种方式解决了问题,但是最终返回给b[i]的数组而不是函数了。那么就存在如下的这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

function box() {

var arr=[];

for(var i=0;i<5;i++) {

arr[i] = (function(num) {

return function() {

return num;

}

})(i);

}

return arr;

}

var b = box(); //得到函数数组

for(var i=0;i<b.length;i++) {

alert(b[i]); //得到所有的结果为1,2,3,4,5

}

除了上面说到的在循环中使用匿名函数的问题,在闭包中使用this对象也会存在一些问题。this对象是在运行是基于函数的执行环境绑定的,如果this在全局对象范围就是window,如果在对象内部就指向这个对象。而闭包却在运行时指向window的,因为闭包并不属于这个对象的属性或方法。最典型的就是下面这个例子了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

var name='The window';

var obj = {

name:'The Object';

getNameFunction:function() {

return function() { //闭包不属于obj,里面的this指向window

return this.name;

};

}

}

var result = obj.getNameFunction()(); //最终结果为The window

为了使得最终的结果返回的是obj对象中的值,也存在多种方式。

一是可以强制指向某个对象,如:obj.getNameFunciotn().call(obj);

二是从上一个作用域中得到对象。所以将getNameFunction改写为:

1
2
3
4
5
6
7
8
9
10
11
12

getNameFunction:function() {

var that = this;

return function() {

return that.name;

}

}

块级作用域

概念

在JavaScript中没有块级作用域的概念。if(){}、for(){}等都没有作用域的概念。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

for(var i=0;i<4;i++){} alert(i);i为4,即使i是在for循环中声明的。



function box() {

for(var i=0;i<10;i++) {}

var i;

alert(i); //结果为11

}

box();

```

所以就算是重新声明,也会覆盖掉前面变量。JavaScript不会说明一个变量是否被重复声明,遇到了这种情况,JavaScript的解释器会忽略掉后面的声明。但是使用模仿块级作用域就可以避免这个问题。

#### 模仿块级作用域

```JavaScript

(function() {

//这里就是块级作用域

})();

所以上面例子就可以改写为:

function box() {

(function() {

for(var i=0;i<10;i++){}

})();

alert(i); //报错,无法访问

}

box();

使用了块级作用域后,匿名函数中定义的任何变量,都会在执行函数结束时被销毁。这种技术经常在全局作用域中被用在函数外部,从而限制想全局作用域中添加过多的变量和函数。一般来说,在开始时候应该尽量避免想全局作用域中添加变量和函数。在大型项目中,尤其是多人开发的时候,过多的全局变量和函数容易导致命名冲突,引起灾难性的后果。如果采用块级作用域,每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域。

BOM

概念

BOM叫做浏览器对象模型,它提供了很多对象,用于访问浏览器的功能。BOM缺少规范,每个浏览器提供商均按照自己的标准去扩展它。那么浏览器共有对象就成文了事实的标准。

BOM的核心对象是window,他表示浏览器的一个实例。window对象处于JavaScript结构的最顶层,对于每个打开的窗口,系统都会自动为其定义window对象。下图显示的就是BOM对象中的结构。

BOM

JavaScript_BOM

在window对象中又有很多的属性,方法以及对象,其中比较常用的有document对象,location对象。在document对象下又有许多的对象。

在window下有2个比较常用的方法,分别为setTimeout()方法。此方法表示在指定的时间过后执行代码。setInterval()表示每个指定的时间重复执行代码。但是在大多数情况下,setTimeout()同样可以实现setTnterval()的方法,所以setTimeout()使用得更多。