Javascript深入学习 基础语法

Javascript是带我走进程序猿世界的一门语言,接触Javascript之前我还是一个小白,到现在从事开发已经三年。
直到最近看了几个关于js的面试题,才突然发现,原来我对js这门语言还不够了解。所以,打算把《Javascript高级编程设计》,好好研读研读,并做个笔记。

Javascript简介

刚学习Javascript的时候,我一直认为ECMASCript和Javascript是一样的,只不过叫法不同,其实他们并不完全相同,Javascript包含三部分:

  • ECMASCript(Javascript的核心,提供核心语言功能)
  • DOM(文档对象模型,提供访问和操作网页内容的方法和接口)
  • BOM(浏览器对象模型,提供与浏览器交互的方法和接口)

ECMASCript

ECMA-262定义的是这门语言的基础,而在此基础上可以构建更完善的脚本语言。web浏览器,nodejs和Adobe Flash就是ECMASCript实现的宿主环境。
宿主环境不仅提供ECMASCript实现,同时提供语言的扩展。ECMASCript就是实现ECMA-262标准规定的各方面内容的语言的描述,Javascript实现了ECMASCript。

HTML中加载Javascript

script元素使用

<script>元素的属性:

  • async: 可选,表示立即下载脚本,但不妨碍页面中的其他操作,只对外部脚本有效。
  • charset: 可选,通过src属性指定的代码的字符集。
  • defer: 可选,表示脚本延迟到文档被解析显示后执行,只对外部脚本有效。
  • src: 可选,外部的脚本文件。
  • type: 可选,脚本语言的内容类型(MIME类型),text/javascript已经不推荐使用,服务器传送javascript脚本文件使用的MIME类型通常是application/x-javascript
    考虑到浏览器兼容性,目前type还是text/javascript,不过不是必须的,这个属性默认就是text/javascript

使用时有以下几点要注意:

  • <script>元素使用方式有两种,直接在页面中嵌入和引用外部脚本文件,如果是直接嵌入javascript代码,必须指定type属性。
  • 使用<script>嵌入JavaScript代码时,代码中不要出现</script>字符串,因为浏览器解析嵌入的代码时,遇到</script>时,会认为是结束的</script>标签。
    通过转义符/可以解决。

    1
    2
    3
    4
    5
    <script type="text/javascript">
    function test() {
    alert("<\/script>");
    }
    </script>
  • 带有src属性的<script>元素,不应再嵌入代码,否则会被忽略。

  • src属性可以包含域外的javascript文件,在访问自己不能控制的服务器上为js文件时,要多加小心。
  • 一般<script>元素会放在body元素中页面内容的后面,也就是</body>前面,这样就不会因为加载js文件而影响页面的呈现。
  • 设置defer属性时,脚本会延迟到页面解析后再执行,但是延迟脚本不一定按顺序执行,因此最好只包含一个延迟脚本。H5会忽略嵌入脚本的defer属性,因为该属性只支持外部脚本。
  • 设置async属性,异步脚本,并保证加载顺序,因此,两者之间不能互相依赖,async属性的目的是为了不让页面等待脚本的加载,所以,异步脚本最好不要操作DOM。

直接在页面中嵌入和引用外部脚本文件比较

一般认为最好还是使用外部脚本:

  • 易维护:所有的js文件集中管理,相比嵌入在HTML中,维护起来要轻松很多。
  • 可缓存:如果多个页面都引用了同一个js文件,就只需要下载一次,加快页面的加载速度。

noscript元素使用

<noscript>可以指定不支持脚本的浏览器中显示的内容,如果浏览器支持,就不会显示。

基本概念

语法

区分大小写

ECMASCript中的一切变量,函数名都区分大小写。

标识符

标识符就是指变量,函数,属性的名字,函数的参数。不能把关键字,保留字,true,false,null用作标识符

注释

单行注释//,块注释/****/

语句

语句结尾,最好加上;,条件控制语句,使用代码块,即使只有一行代码。

变量

通过var定义变量。

1
2
3
4
5
6
7
8
var name = "xiaoming";
name = "xiaoming"; //不推荐,这是的name是全局变量
//定义多个变量
var name = "xiaoming",
age = 18,
sex = "man";

数据类型

5种简单类型:undefined, null, boolean, number, string。
1种复杂的:Object。

typeof操作符

使用typeof判断变量的数据类型:

  • undefined,如果值未定义。
  • boolean,如果值是布尔值。
  • number,如果值是数值。
  • string,如果值是字符串。
  • object,如果值是对象或null。
  • function,如果值是函数。

注意:

  • null值表示一个空对象的指针,这也是为什么typeof null会返回object
  • undefined派生自null,所以
1
2
console.log(null == undefined) //true
console.log(null === undefined) //false
  • NaN,非数值,NaN与任何值都不相等,包括它自己。判断是否是NaN,使用isNaN()函数,这个函数接收一个参数,会尝试将参数转换成数值,
    任何不能被转换为数值的值都会返回true
1
2
3
4
5
console.log(isNaN(NaN)); //true
console.log(isNaN(10)); //false
console.log(isNaN("10")); //false
console.log(isNaN("xiaoming")); //true
console.log(isNaN(true)); //false ,可以被转换成数值1,false会转换成数值0

数值转换

Number()函数
  • 如果是Boolean值,true和false值将分别被转换为1和0。
  • 如果是数字值,只是简单的传入和返回。
  • 如果是null值,返回0。
  • 如果是undefined,返回NaN。
  • 如果是字符串:
    1. 如果字符串中只包含数字时,将其转换为十进制数值,忽略前导0,例如”011”变成11。
    2. 如果字符串中包含有效浮点格式,如”1.1”,将其转换为对应的浮点数字,忽略前导0。
    3. 如果字符串中包含有效的十六进制格式,如”0xf”,将其转换为相同大小的十进制数值
    4. 如果字符串为空,将其转换为0
    5. 如果字符串中包含除上述格式之外的字符,则将其转换为NaN
  • 如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再依照前面的规则转换返回的字符串值。
parseInt()函数

Number()函数,处理字符串比较复杂而且不够合理,处理整数时更常用的是parseInt()函数。

  • parseInt()函数在转换字符串时,会忽略字符串前面的空格,知道找到第一个非空格字符。
  • 如果第一个字符不是数字或者负号,parseInt() 就会返回NaN,同样的,用parseInt() 转换空字符串也会返回NaN。
  • 如果第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析完所有后续字符串或者遇到了一个非数字字符。

parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。
基是由parseInt()方法的第二个参数指定的,所以要解析十六进制的值,当然,对二进制、八进制,甚至十进制(默认模式),
没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。该值介于 2 ~ 36 之间。都可以这样调用parseInt()方法。

parseFloat()函数

与parseInt() 函数类似,parseFloat() 也是从第一个字符(位置0)开始解析每一个字符。也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。

也就是说,字符串中第一个小数点是有效的,而第二个小数点就是无效的了,它后面的字符串将被忽略

parseFloat() 只解析十进制,因此它没有第二个参数指定基数的用法

如果字符串中包含的是一个可解析为正数的数(没有小数点,或者小数点后都是零),parseFloat() 会返回整数。

转换字符串

把一个值转换成字符串有两种方式。

toString()

数值、布尔值、对象和字符串值(没错,每个字符串也都有一个toString()方法,该方法返回字符串的一个副本)都有toString()方法。
但null和undefined值没有这个方法。多数情况下,调用toString()方法不必传递参数。但是,在调用数值的toString()方法时,
可以传递一个参数:输出数值的基数。默认情况下,toString()方法以十进制格式返回数值的字符串表示。而通过传递基数,toString()可以输出以二进制、八进制、十六进制,
乃至其他任意有效进制格式表示的字符串值。

1
2
3
4
5
6
var num = 10;
alert(num.toString()); // "10"
alert(num.toString(2)); // "1010"
alert(num.toString(8)); // "12"
alert(num.toString(10)); // "10"
alert(num.toString(16)); // "a"
String()

在不知道要转换的值是不是null或undefined的情况下,还可以使用转型函数String(),这个函数能够将任何类型的值转换为字符串。
String()函数遵循下列转换规则:

  • 如果值有toString()方法,则调用该方法(没有参数)并返回相应的结果;
  • 如果值是null,则返回”null”;
  • 如果值是undefined,则返回”undefined”。

下面再看几个例子:

1
2
3
4
5
6
7
8
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;
alert(String(value1)); // "10"
alert(String(value2)); // "true"
alert(String(value3)); // "null"
alert(String(value4)); // "undefined"

Object类型

Object类型所具有的任何属性和方法也同样存在于更具体的对象中。
Object的每个实例都具有下列属性和方法:

  • constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)就是Object()。
  • hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)
    必须以字符串形式指定(例如:o.hasOwnProperty(“name”))。
  • isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型(第5章将讨论原型)。
  • propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语句(本章后面将会讨论)来枚举。与hasOwnProperty()方法一样,
    作为参数的属性名必须以字符串形式指定。
  • toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同。由于在ECMAScript中Object是所有对象的基础,因此所有对象都具有这些基本的属性和方法。

操作符

ECMA-262描述了一组用于操作数据值的操作符,包括算术操作符(如加号和减号)、位操作符、关系操作符和相等操作符。ECMAScript操作符的与众不同之处在于,它们能够适用于很多值,
例如字符串、数字值、布尔值,甚至对象。不过,在应用于对象时,相应的操作符通常都会调用对象的valueOf()和(或)toString()方法,以便取得可以操作的值。

一元操作符

递增和递减操作符

执行前置递增和递减操作时,变量的值都是在语句被求值以前改变的

1
2
3
4
var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2; // 等于21
var num4 = num1 + num2; // 等于21

num3之所以等于21是因为num1先减去了1才与num2相加。而变量num4也等于21是因为相应的加法操作使用了num1减去1之后的值。
后置型递增和递减操作符的语法不变(仍然分别是++和–),只不过要放在变量的后面而不是前面。后置递增和递减与前置递增和递减有一个非常重要的区别,
即递增和递减操作是在包含它们的语句被求值之后才执行的

1
2
3
4
var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2; // 等于22
var num4 = num1 + num2; // 等于21

递增和递减操作符遵循下列规则:

  • 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减1的操作。字符串变量变成数值变量。
  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为NaN。字符串变量变成数值变量。
  • 在应用于布尔值false时,先将其转换为0再执行加减1的操作。布尔值变量变成数值变量。
  • 在应用于布尔值true时,先将其转换为1再执行加减1的操作。布尔值变量变成数值变量。
  • 在应用于浮点数值时,执行加减1的操作。
  • 在应用于对象时,先调用对象的valueOf()方法,以取得一个可供操作的值。然后对该值应用前述规则。如果结果是NaN,则在调用toString()方法后再应用前述规则。对象变量变成数值变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var s1 = "2";
var s2 = "z";
var b = false;
var f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1++; // 值变成数值3
s2++; // 值变成NaN
b++; // 值变成数值1
f--; // 值变成0.10000000000000009
o--; // 值变成数值-2
一元加和减操作符

在对非数值应用一元加操作符时,该操作符会像Number()转型函数一样对这个值执行转换。换句话说,布尔值false和true将被转换为0和1,
字符串值会被按照一组特殊的规则进行解析,而对象是先调用它们的valueOf()和(或)toString()方法,再转换得到的值。

位操作符

按位非

按位非操作符~

1
2
3
var num1 = 25; // 二进制00000000000000000000000000011001
var num2 = ~num1; // 二进制11111111111111111111111111100110
alert(num2); // -26

按位与

按位与操作符&,按位与操作只在两个数值的对应位都是1时才返回1,任何一位是0,结果都是0

1
2
3
4
5
6
7
8
var result = 25 & 3;
alert(result); //1
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001

按位或

按位非操作符|,按位或操作在只要有一个位是1的情况下就返回1,而只有在两个位都是0的情况下才返回0

按位异或

按位异或操作符^,按位异或与按位或的不同之处在于,这个操作在两个数值对应位上只有一个1时才返回1,如果对应的两位都是1或都是0,则返回0

左移

左移操作符<<,这个操作符会将数值的所有位向左移动指定的位数。左移不会影响操作数的符号位

1
2
var oldValue = 2; // 等于二进制的10
var newValue = oldValue << 5; // 等于二进制的1000000,十进制的64

有符号右移

有符号右移操作符>>,这个操作符会将数值向右移动,但保留符号位。

1
2
var oldValue = 64; // 等于二进制的1000000
var newValue = oldValue >> 5; // 等于二进制的10 ,即十进制的2

无符号右移

无符号右移操作符>>>,这个操作符会将数值的所有32位都向右移动。对正数来说,无符号右移的结果与有符号右移相同。

1
2
var oldValue = 64; // 等于二进制的1000000
var newValue = oldValue >> 5; // 等于二进制的10 ,即十进制的2

加性操作符

加法

如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:

  • 如果有一个操作数是NaN,则结果是NaN;
  • 如果是Infinity加Infinity,则结果是Infinity;
  • 如果是-Infinity加-Infinity,则结果是-Infinity;
  • 如果是Infinity加-Infinity,则结果是NaN;
  • 如果是+0加+0,则结果是+0;
  • 如果是-0加-0,则结果是-0;
  • 如果是+0加-0,则结果是+0。

不过,如果有一个操作数是字符串,那么就要应用如下规则:

  • 如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来。

如果有一个操作数是对象、数值或布尔值,则调用它们的toString()方法取得相应的字符串值,然后再应用前面关于字符串的规则。对于undefined和null,则分别调用String()函数并取得字符串”undefined”和”null”。

减法

与加法操作符类似,ECMAScript中的减法操作符在处理各种数据类型转换时,同样需要遵循一些特殊规则,如下所示:

  • 如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
  • 如果有一个操作数是NaN,则结果是NaN;
  • 如果是Infinity减Infinity,则结果是NaN;
  • 如果是-Infinity减-Infinity,则结果是NaN;
  • 如果是Infinity减-Infinity,则结果是Infinity;
  • 如果是-Infinity减Infinity,则结果是-Infinity;
  • 如果是+0减+0,则结果是+0;
  • 如果是+0减-0,则结果是-0;
  • 如果是-0减-0,则结果是+0;
  • 如果有一个操作数是字符串、布尔值、null或undefined,则先在后台调用Number()函数将其转换为数值,然后再根据前面的规则执行减法计算。
    如果转换的结果是NaN,则减法的结果就是NaN;
  • 如果有一个操作数是对象,则调用对象的valueOf()方法以取得表示该对象的数值。如果得到的值是NaN,则减法的结果就是NaN。如果对象没有valueOf()方法,
    则调用其toString()方法并将得到的字符串转换为数值。
1
2
3
4
5
6
var result1 = 5 - true; // 4,因为true被转换成了1
var result2 = NaN - 1; // NaN
var result3 = 5 - 3; // 2
var result4 = 5 - ""; // 5,因为"" 被转换成了0
var result5 = 5 - "2"; // 3,因为"2"被转换成了2
var result6 = 5 - null; // 5,因为null被转换成了0

相等操作符

ECMAScript的提供两组操作符:相等和不相等(先转换再比较),全等和不全等(仅比较而不转换)。

相等和不相等
  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1;
  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较;这两个操作符在进行比较时则要遵循下列规则。
  • null和undefined是相等的。
  • 要比较相等性之前,不能将null和undefined转换成其他任何值。
  • 如果有一个操作数是NaN,则相等操作符返回false,而不相等操作符返回true。重要提示:即使两个操作数都是NaN,相等操作符也返回false;因为按照规则,NaN不等于NaN。
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true;否则,返回false。

    语句

break和continue语句

break语句会立即退出循环。而continue语句是立即退出本次循环,进行下次循环。