Javascript基本概念

语法

Javascript语法中的基本规范


区分大小写

ECMAScript中的一切(变量、函数名和操作符)都区分大小写。
所以,test和Test分别表示两个不同的变量;


标识符

标识符:指变量、函数、属性的名字,或者函数的参数;规则如下:

  • 第一个字符必须是一个字母、下划线(_)或一个美元符号($);
  • 其他字符可以是字母、下划线、美元符号和数字;

命名惯例:ECMAScript标识符采用驼峰大小写格式,如:myCar , doSomethingImportant 等;
驼峰格式不是强制要求的,但和官方命名规范保持一致是个好习惯;
不能把关键字、保留字、truefalsenull用作标识符;


注释

单行注释
// 单行注释

块级注释
/*
第一行注释
第二行注释
*/


语句

  • 语句结尾加上分号,有些时候不是必须的,加上是个好习惯;
  • 遇到控制语句时,代码块使用{},哪怕只有一行代码;

关键字和保留字

关键字
break do instanceof typeof 等一堆;
保留字
long int public static 等一堆;


变量

ECMAScript 的变量是松散类型的,就是可以用来保存任何类型的数据。
定义变量时使用var操作符,如下:

var message;

这行代码定义了一个名为 message的变量,该变量可以用来保存任何值,像这样未经过初始化的变量,会保存一个特殊的值 undefined 。也可以直接初始化变量,即设置变量的值,如下:

var message = 'hi'; 
message = 100;    //有效,但不推荐

上面例子,开始保存了一个字符串值,又被一个数字值取代。虽然不建议这样做,但这样做也没错;

如果在函数中使用var定义一个变量,这个变量在函数退出后会被销毁,如下:

function test(){
  var message = "hi";   //局部变量
}
test();
alert(message);  // 错误

上面例子,变量message是在函数中使用var定义的,当函数被调用时,就会创建该变量并为其赋值。之后,这个变量又会立即被销毁,因此代码就会导致错误。

如果省略var操作符,就会创建一个全局变量。

function test(){
  message = "hi";   //局部变量
}
test();
alert(message);  // hi

上面例子,省略了var 操作符,因而message就成了全局变量。这样,只要调用过一次test()函数,这个变量就有了定义,就可以在函数上部的任何地方被访问到。
省略var操作符可以定义全局变量,但不推荐这样做。因为在局部作用域中定义的全局变量很难维护,也可能导致混乱。

可以使用一条语句定义多个变量,用逗号分开即可,如下:

var message = "hi",
    found = false,
    age = 30;

上面例子,定义并初始化了3个变量。


数据类型

ECMAScript中有5种基本数据类型:Undefined Null Boolean Number String
还有一种复杂数据类型: Object。
Object 本质上是由一组无序的名值对组成的。

typeof操作符 负责检测给定变量的数据类型;对一个值使用typeof操作符可能返回下列某个字符串:

  • “undefined” --- 如果这个值未定义;
  • “boolean” ---如果这个值是布尔值;
  • “string” --- 如果这个值是字符串;
  • “number” --- 如果这个值是数值;
  • “boject” --- 如果这个值是对象或null;
  • “function” --- 如果这个值是函数;
var msg = "some string"; 
alert(typeof msg); // "string"
alert(typeof(msg)); // "string"
alert(typeof 95); // "number"

上面例子,typeof 操作符的操作数,可以是变量,也可以是数值字面量。
typeof 是一个操作符而不是函数,因此例子中的圆括号尽管可以使用,但不是必需的;


Undefined 类型

Undefined 类型只有一个值,即特殊的undefined。 在使用var声明变量但未对其加以初始化时,这个变更的值就是 undefined,例如:

var msg;
alert(msg == undefined);  // true

Null 类型

Null 类型是第二个只有一值的数据类型,这个特殊的值就是 null。 从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用typeof操作符检测null值时会返回"object"的原因,如下:

var car = null ; 
alert(typeof(car)); // "object"

上面例子,如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null。这样一来,只要直接检查null值就可以知道相应的变量是否已经保存了一个对象的引用,如下:

if(car != null){
  // 对car对象执行某些操作
}

实际上,undefined值是派生自null值的,因此ECMA-262规定对他们的相等性测试要返回true;

alert(null == undefined);  //true

Boolean类型

Boolean 类型是ECMAScript 中使用最多的一种类型,该类型只有两个字面值:true 和 false。

var found = true; 
var lost = false;

上面例子,Boolean 类型的字面值 true 和 false 是区分大小写的,也就是说,True 和 False 都不是Boolean值,只是标识符;

要将一个值转换为其对应的 Boolean 值,可以调用转型函数 Boolean(),如下:

var msg = "Hello Javascript"; 
var msgAsBoolean = Boolean(msg);
console.log(msgAsBoolean);  // true

可以对任何数据类型的值调用 Boolean() 函数,而且总会返回一个 Boolean 值。至于返回的这个值是 true 还是 false,取决于要转换值的数据类型及其实际值,如下:

转换为true的值

  • 任何非空字符串(String)
  • 任何非零数字值(Number)
  • 任何对象(Object)

转换为false的值

  • “”空字符串(String)
  • 0和NaN(Number)
  • null (Object)
  • undefined

这些转换规则对理解流控制语句(如if语句)自动执行相应的 Boolean 转换非常重要,如下:

var msg = "Hello Javascript";
if (msg){
  alert("value is true");
}

上面例子能弹出框,因为字符串msg被自动转换成对应的 Boolean 值(ture)。由于存在这种自动执行的 Boolean 转换,因此确切地知道在流控制语句中使用的是什么变量至关重要。


Number类型

这种类型使用IEEE754格式来表示整数和浮点数值,最基本的字面量格式是十进制整数,如下:

var iteNum = 55;  // 整数

除了十进制,还可以通过八进制和十六进制的字面值来表示。
其中,八进制字面值的第一位必须是零(0),然后是八进制数字序列(0-7),如果字面值中的数值超出范围,那么前导零将被忽略,后面的数值半被当作十进制数值解析。如下:

var octalNum1 = 070;  // 八进制的56 
var octalNum2 = 079;  // 无效的八进制数值, 被解析为79 
var octalNum3 = 08;    // 无效的八进制数值,被解析为8

十六进制字面值的前两位必须是0x,后跟任何十六进制数字(0-9及A-F)。其中,字母A-F可以大写,也可以小写,如下:

var hexNum1= 0xA;  // 十六进制10
var hexNum2= 0x1f;  // 十六进制31

在进行算术计算时,所有以八进制和十六进制的表示的数值都将被转换成十进制数值。

浮点数值
所谓浮点数值,就是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。

var floatNum1 = 1.1; 
var floatNum2 = 0.1;
var floatNum3 = .1;   //  有效,但不推荐;
var floatNum4 = 1.;   //  小数点后面没有数字,解析为1
var floatNum5 = 10.0  //  整数,解析为10
var floatNum6 = 3.125e7;  //  科学计数法,等于31250000

浮点数值计算会产生舍入误差的问题,例如:0.1加0.2不等于0.3,而是0.30000000000000004。 浮点数的最高精度是17位小数。

NaN
Nan,即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况,这样就不会抛出错误了。
Nan本身有两个非同寻常的特点:

  • 任何涉及NaN的操作(Nan/10)都会返回NaN;
  • NaN与任何值都不相等,包括NaN本身; 即 console.log(NaN == NaN) 会返回 false;

isNaN()函数
这个函数接受一个参数,该参数可以是任何类型,函数会确定这个参数是否“不是数值”。
isNaN() 在接收到一个值之后,会尝试将这个值转换成数值,某些不是数值的值会直接转换为数值,如:字符串‘10’或Boolean值。任何不能被转换为数值的值都会返回 true,如下:

console.log(isNaN(NaN));     // true
console.log(isNaN(10));      // false (10是一个数值)
console.log(isNaN(‘10’));    // false (可以被转换成数值10)
console.log(isNaN(true));    // fasle (可以被转换成数值1)
console.log(isNaN(‘blue’));  // true (不能转换成数值)

isNaN 也适用于对象,在基于对象调用 isNaN()函数时,会首先调用对象的 valueOf() 方法,确定该方法返回的值是否可以转换为数值,如果不能,则基于这个返回值再调用 toString() 方法,再测试返回值。

数值转换
有3个函数可以把非数值转换为数值:Number()parseInt()parseFloat()
Number() 可以用于任何数据类型,另两个函数则专门用于把字符串转换成数值。

Number() 函数的转换规则如下:

  • 如果是 Boolean 值,true 和 false 将分别被转换成 1 和0;
  • 如果是数字值,即为数值;
  • 如果是 null 值,返回 0;
  • 如果是 undefined ,返回 NaN ;
  • 如果是字符串,那么按如下规则:
    ++ 如果字符串中只包含数字,则将其转换为十进制数值,即 “1” 会变成1,“123”会变成123,而“011”会变成11(注意:前导的零被忽略了)
    ++ 如果字符串中包含有效的浮点格式,如“1.1”,则将其转换为对应的浮点值。
    ++ 如果字符串中包含有效的十六进制格式,例如:“0xF”,则将其转换为相同大小的十进制整数值;
    ++ 如果字符串是空的,则将其转换为 0;
    ++ 如果字符串中包含除上述格式之外的字符,则将其转换为NaN;
  • 如果是对象,则调用对象的valueOf()方法,然后按照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再次按照前面的规则转换返回的字符串值。

规则讲完,看实例:

var num1 = Number("hello");   // NaN
var num2 = Number("");        // 0
var num2 = Number("000011");  // 11
var num2 = Number("true");    // 1

hello 因为不包含数字值,所以被转换成NaN,空字符串会被转换成0,字符串“000011”会被转换为11,true 值被转成1。

处理整数时,更常用 parseInt() 函数。parseInt() 函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直到找到第一个非空格字符。如果第一个字符不是数字字符或者负号,parseInt() 就会返回NaN;也就是说,用parseInt() 会转换空字符串返回NaN(Number()对空字符串返回0),如果第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析完所有或者遇到 了一个非数字字符。例如:“1234blue” 会被转换成1234,因为"blue"被忽略。类似地,“22.5”会被转换为22,因为小数点并不是有效的数字字符。也能识别八进制和十六进制,用的不多,不说了。

var num1 = parseInt("1234blue"); // 1234
var num2 = parseInt("123abc456"); // 123
var num3 = parseInt("");  // NaN
var num4 = parseInt("22.5");  // 22

parseInt()类似,parseFloat() 也是从第一个字符开始解析每个字符,直到字符串结尾,或者解析到遇见一个无效的浮点数字字符为止。也就是说,字符串中的第一个小数点是有效的,而第二个小数点就无效了,因此后面的字符串会被忽略。

var num1 = parseFloat("123abc");  // 123
var num2 = parseFloat("22.5");  // 22.5
var num3 = parseFloat("22.34.5");  //22.34
var num4 = parseFloat("098.5"); // 98.5
var num5 = parseFloat("3.125e7");  // 31250000

字符串

字符串可以由双引号(")或单引号(‘)表示,两种方式表示的字符串完全相同,如下:

var str1 = "hello javascript";
var str2 = 'hello javascript';

开头和结尾要么都用双引号,要么都用单引号,不能一边是单引号,一边是双引号;

字符字面量
String 数据类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者其他用途的字符。
\n 换行;\t 制表;\b 退格;\r 回车; \f 进纸; \\ 斜杠;\' 单引号;\" 双引号;
任何字符串的长度都可以通过访问其length 属性取得,如下:

console.log("hello javascript");  // 16

字符串拼接
字符串可以通过“+”来拼接,如下:

var lang = "Java"; 
lang = lang + "script"; 
console.log(lang);  //  Javascript

转化为字符串
要把一个值转换为字符串,有两个方法: toString()方法和 String()函数。
数值、布尔值、对象和字符串值都有 toString() 方法,但null 和 undefined 值没有这个方法。

var age = 11; 
var ageAsString = age.toString(); // 字符串 "11"
var found = true; 
var foundAsString = found.toString(); // 字符串“true”

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

  • 如果值有 toString() 方法,则调用该 方法并返回相应的结果;
  • 如果值是 null,则返回“null”;
  • 如果值是 undefined, 则返回"undefined";
var v1 = 10; 
var v2 = true; 
var v3 = null ; 
var v4;
console.log(String(v1)); // "10"
console.log(String(v2)); // "true"
console.log(String(v3)); // "null"
console.log(String(v4)); // "undefined"

要把某个值转换为字符串,可以使用加号操作符把它与一个字符串(“”)加在一起,如下。

var v1 = 10; 
var v2 = v1+"";
console.log(typeof(v2)); // “10” --> string

Object类型

ECMAScript中的对象其实就是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建的对象类型的名称来创建。而创建Object类型的实例并为其添加属性或方法,就可以创建自定义对象,如:

var o = new Object();

仅创建Object的实例并没有什么用处,在EMCAScript中,Object类型是所有它的实例的基础。换句话说,Object 类型所具有的任何属性和方法也同样存在于他的实例中。
Object的每个实例都具有下列属性方法:

  • constructor:保存着用于创建当前对象的函数;
  • hasOwnProperty():用于检查给定的属性在当前对象实例中;
  • isPrototypeof():用于检查传入的对象是否是当前对象的原型;
  • propertyIsEnumerable(): 用于检查给定的属性是否能够使用 for-in 语句来枚举;
  • toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应;
  • toString():返回对象的字符串表示;
  • valueOf():返回对象的字符串、数值或布尔值表示;
    在ECMAScript中Object是所有对象的基础,所有对象都具有这些基本的属性和方法。

操作符

ECMAScript 描述了一组用于操作数据值的操作符,包括算术操作符、位操作符、关系操作符和相等操作符。


一元操作符

只能操作一个值的操作符叫做一元操作符。 使用前置递增操作符给一个数值加1时,把两个加号(++)放在这个数值的前面,如下:

var age = 29;
++age;  // 30

前置递增操作符把age的值变成 了30,实际上,执行这个前置递增操作与执行以下操作的效果相同:

var age =29;
age = age +1;

执行递减操作的方法也类似,变量的值都是在语句被求值以前改变的。这种情况被称作:副效应

var age = 29; 
var anotherAge = --age +2;

console.log(age);  // 输出28;
console.log(anotherAge);  // 输出30

上面例子,变量anotherAge的初始值等于变量age的值前置递减之后加2。由于先执行了减法操作,age的值变成了28,所以再加上2的结果就是30。

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之后的值。

后置型递增和递减操作符的语法不变,只不过要放在变量的后面而不是前面。
与前置递增递减的重要区别是:后置的操作是在包含它们的语句被求值之后才执行的。

var num1 = 2; 
var num2 = 20;
var num3 = num1-- + num2;  // 22  与前置 -- 的重要区别,先求值后递减
var num4 = num1 + num2;   // 21

与前置不同,差别在计算 num3时使用num1 的原始值(2)完成了加法计算,而num4则使用了递减后的值(1)。


布尔操作符

布尔操作符一共有3个: 非(NOT)、与(AND)和或(OR)

逻辑非 : 由一个叹号(!)表示,无论这个值是什么数据类型,都会返回一个布尔值。
逻辑非操作符首先会将它的操作类转换为一个布尔值,然后再对其求反。

  • 如果操作数是一个对象,返回false;
  • 如果操作数是一个空字符串,返回true;
  • 如果操作数是一个非空字符串,返回 false;
  • 如果操作数是数值0,返回true;
  • 如果操作数是任意非0数值(包括:Infinity),返回 false;
  • 如果操作数是null ,返回 true;
  • 如果操作数是NaN,返回 true;
  • 如果操作数是undefined,返回true;
console.log(!flase); // true
console.log(!"blue"); //false
console.log(!0); // true
console.log(!NaN); // true
console.log(!""); // true
console.log(!12345); // false

同时使用两个逻辑非,实际上会模拟 Boolean() 转型函数,最终结果与对这个值使用 Boolean() 函数相同,如:

console.log(!!"blue"); //true
console.log(!!0); // false
console.log(!!NaN); // false
console.log(!!""); // false
console.log(!!12345); // true

逻辑与 由两个和号(&&)表示,有两个操作数,如下:

var res = true && false;

逻辑与的真值表
第一个操作数 第二个操作数 结果
true true --> true
true false --> false
false true --> false
false false --> false

逻辑与 操作可以应用于任何类型的操作数,而不仅仅是布尔值,在有一个操作数不是布尔值的情况下,逻辑与 操作就不一定返回尔值。

  • 如果第一个操作是对象,则返回第二个操作数;
  • 如果第二个操作数是对象,则只有在第一个操作数据求值结果为true的情况下,才会返回该对象;
  • 如果两个操作数都是对象,则返回第二个操作数;
  • 如果有一个操作数是null (NaN或undefined),则返回null (NaN或undefined);

逻辑与 操作属于短路操作,即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。对于 逻辑与 操作而言,如果第一个操作数是 false,则无论第二个操作数是什么值,结果都不再可能是 true 了,如:

var found = true; 
var res = (found && someUndefinedVariable);  // 这里发生错误
alert(res);  // 这一行不会执行

上面例子,当执行 逻辑与 操作时会发生错误,因为变量 someUndefinedVariable 没有声明。
由于变量 found 的值是 true,所以逻辑与操作符会继续对变量 someUndefinedVariable 求值,但他尚未定义,因此报错,这说明不能在逻辑与操作中使用未定义的值。如果将 found 的值设置为 false,就不会报错。

var found = false; 
var res = (found && someUndefinedVariable);  // 不会报错
alert(res);   //  会执行,结果是 false

无论变量someUndefinedVariable 有没有定义,永远不会对它求值,因为第一个操作数的值是 false。 意味着逻辑与的结果必定是false,根本用不着再对 && 右侧的操作数求值了。

在使用逻辑与操作符时要始终铭记,它是一个短路操作符。

逻辑或
逻辑或操作符由两个竖线符号(||)表示,如下:

var res = true || false; 

逻辑或的真值表
第一个操作数 第二个操作数 结果
true true --> true
true false --> true
false true --> true
false false --> false

逻辑与相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值;

  • 如果第一个操作数是对象,则返回这个对象;
  • 如果第一个操作数的求值结果为 false,则返回第二个操作数;
  • 如果两个操作数都是对象,则返回第一个操作数;
  • 如果两个操作数都是null(NaN或undefined),则返回null(NaN或undefined);

逻辑与操作符相似,逻辑或操作符也是短路操作符。也就是说,如果第一个操作数的求值结果为 true, 就不会对第二个操作数求值了,看实例:

var found = true; 
var res = (found || someUndefinedVariable) ;     // 不会发生错误
console.log(res);  // 会执行 true

上面例子,变量someUndefinedVariable 也没有定义,但是,由于变量 found 的值是true,而变量 someUndefinedVariable永远不会被求值,因此结果就会输出 true,

我们可以逻辑或的这一行为来避免为变量赋null或undefined值,如:

var myObject = preferredObject || backupObject;

上面例子,变量myObject将被赋予等号后面两个值中的一个,变量preferredObject中包含优先赋给变量 myObject 的值,变量 backupObject负责在preferredObject中不包含有效值的情况下提供后备值。如果preferredObject的值不是null,那么它的值将被赋给myObject;如果是null,则将backupObject的值赋给 myObject。ECMAScript程序的赋值语句经常会使用这种模式。


乘性操作符

ECMAScript 定义了3个乘性操作符:乘法、除法和求模。在操作数为非数值的情况下会执行自动的类型转换。如果参与乘性计算的某个操作数不是数值,后台先使用Number()转型函数将其转换为数值。也就是说,空字符串将被当作0,布尔值将被当作1。

乘法
乘法操作符由一个星号(*)表示,用于计算两个数值的乘积。

var res = 34*56

在处理特殊值的情况下,乘法操作符遵循下列特殊的规则:

  • 如果操作数都是数值,执行常规的乘法计算;
  • 如果有一个操作数是NaN,则结果是NaN;
  • 如果是Infinity与0相乘,则结果是NaN;
  • 如果是Infinity与非0数相乘,则结果是Infinity 或-Infinity,取决于有符号操作数的符号;
  • 如果是Infinity 与Infinity 相乘,则结果是 Infinity;
  • 如果有一个操作数不是数值,则在后台调用 Number() 将其转换为数值,然后再应用上面的规则。

除法
除法操作符由一个斜线符号(/)表示;

var res = 66/11;

与乘法操作符类似,除法操作符对特殊值也有特殊的处理规则,如下:

  • 如果操作数都是数值,执行常规的除法计算;
  • 如果有一个操作数是NaN,则结果是NaN;
  • 如果是Infinity被Infinity除,则结果是NaN;
  • 如果是零被零除,则结果是NaN;
  • 如果是非零的有限数被零除,则结果是 Infinity 或 -Infinity;
  • 如果是Infinity被任何非零数值除,结果是Infinity或-Infinity;
  • 如果有一个操作数不是数值,则在后台调用 Number() 将其转换为数值,然后再应用上面的规则。

求模
求模(求余)操作符由一个百分号(%)表示;

var res = 26%5;  // 1

特殊规则

  • 如果操作数都是数值,执行常规的除法计算,返回除得的余数;
  • 如果被除数是无穷大值而除数是有限大的数值,则返回NaN;
  • 如果被除数是有限大的数值而除数是零,则结果是NaN;
  • 如果是Infinity被Infinity除,则结果是NaN;
  • 如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数;
  • 如果被除数是零,则结果是零;
  • 如果有一个操作数不是数值,则在后台调用 Number() 将其转换为数值,然后再应用上面的规则。

加性操作符

加法和减法也会在后台转换不同的数据类型,捡点重要的说一说;
加法
加法操作符(+) 的用法:

var res = 1+2;

如果两个操作符都是数值,执行常规的加法计算

  • 如果有一个操作数是NaN,则结果是NaN;
  • 如果有一个操作数是字符串,那么
    ++ 如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
    ++ 如果只有一个是字符串,则将另一个操作数转换成字符串再将两个字符串拼接;
  • 如果有一个操作数是对象、数值或布尔值,则调用他们的toString()方法取得相应的字符串值,然后拼接。对于undefined 和 null ,则分别调用String()函数并取得字符串“undefined”和“null”。
var res1 = 5+5;  // 两数相加
alert(res1);  // 10

var res2 = 5+"5";  // 一个数值和一个字符串相加
alert(res2);  // "55"

第一行代码演示了正常的情况,即5+5等于10(数值)。但是,如果将一个操作数改为字符串“5”,结果就变成了"55"(字符串值),因为第一个操作数也被转成了"5"。
减法
减法操作符(-)的用法:

var res = 2-1;

与加法类似,也有一些特殊用法:

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

关系操作符

小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比较。
这几个操作符,都返回布尔值:

var res1 = 5> 3; // true
var res2 = 5< 3; // false

当关系操作符使用了非数值时,也要进行数据转换或完成某些奇怪的操作:

  • 如果两个操作数都是数值,则执行数值比较;
  • 如果两个操作数都是字符串,则比较两个字符串的字符编码值;
  • 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较;
  • 如果一个操作数是对象,则调用这个对象的valueOf()方法,用得到的结果执行比较,如果对象没有valueOf()方法,则调用toString()方法,并用得到的结果执行比较;
  • 如果一个操作数是布尔值,则先将其转换为数值,再执行比较;
    一些可能让人困惑又合理的比较结果,如下:
var res = "Brick" < "alphabet";  // true 

字符串"Brick"被认为小于字符串"alphabet",因为字母B的字符编码是66,而字母a的字符编码是97。
全部转换成小写,再执行比较,如下:

var res = "Brick".toLowerCase() < "alphabet".toLowerCase();  // false

上面例子,"alphabet"按字母表顺序排在"Brick"之前;
再看一个,两个数字字符串比较,如:

var res = "23" < "3";  // true

两个字符串比较,比较的是字符编码,字符串“23”中“2”的字符编码是50,而“3”的编码是51。
如果把其中一个操作数改为数值,那么:

var res = "23" < 3;  // false

此时,字符串"23"会被转换成数值23,再与3比较,就会得出合理的结果。
如果字符串不能被转换成数值的话,如:

var res = "a" < 3; // false,因为"a"被转换成了NaN

由于字母"a"不能被转换成数值,就转换成了NaN。任何操作数与NaN进行关系比较,都是false,于是:

var res1 = NaN > 3; // false
var res2 = NaN <= 3:  // false

与NaN比较时,结果都返回NaN;


相等操作符

相等和不相等:先转换再比较;
全等和不全等:仅比较而不转换;

相等和不相等

相等操作符由两个等于号(==)表示, 如果两个操作数相等,则返回 true。
不相等操作由(!=)表示,如果两个操作数不相等,则返回 true。
这两个操作符都会先转换操作数(通常称为强制转型),然后再比较相等性。

在转换不同的数据类型时,相等和不相等的基本规则:

  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值:false 转换为0,true转换为1;
  • 如果一个操作数是字符串,另一个是数值,那先将字符串转换成数值;
  • 如果一个操作数是对象,另一个不是,则调用对象valueOf()方法,用得到的类型值进行比较;

这两个操作符在进行比较时:

  • null 和 undefined 是相等的;
  • 要比较相等性之前,不能将null 和undefined转换成其他任何值;
  • 如果有一个操作数是 NaN ,则相等操作符返回 false,不相等操作符返回 true;NaN不等于自身;
  • 如果两个操作数都是对象,则比较他们是不是同一对象,如果都指向同一个对象,则相等返回 true,否则,返回 false
null == undefined; // true
"NaN" == NaN; // false  NaN 跟谁比都不相等,也不等于自身
NaN == NaN;  // false
false == 0; // true
true == 1; // true
null == 0; // false
undefined == 0; // false
"5" == 5; // true

全等和不全等

全等用三个等于号(===)表示,它只在两个操作数未经转换就相等的情况下返回 true;

var res1 = ("55" == 55) ; // true 因为转换成相等
var res2 = ("55" === 55); // false 因为不同的数据类型不相等

上面例子,相等操作符比较时,会转换数据类型,字符串“55”被转换成数值55,所以相等,返回true;
全等操作符比较时,不转换数据类型,字符串不等于数值,所以不相等,返回 false;

不全等操作符用(!==)表示,在两个操作数未经转换就不相等的情况下返回 true

var res1 = ("55" !== 55);  // false,因为转换后是相等的
var res2 = ("55" !=== 55); // true, 因为不同的数据类型不相等

null == undefined 会返回 true,因为他们是类似的值;
null === undefined 会返回 false, 因为他们是不同类型的值;

为了保持代码中数据类型的完整性,推荐使用全等和不全等操作符进行比较。


条件操作符

条件 ? 真时的值 : 假时的值

variable = boolean_expression ? true_value : false_value;

基于对 boolean_expression 求值的结果,决定给变量 variable 赋什么值。
如果求值结果为 true , 赋 true_value值;如果求值结果为 false, 赋 false_value值

var max_num = (num1>num2) ? num1 : num2; 

max_num 将会保存一个最大的值。


赋值操作符

赋值操作符用(=)表示,其作用是把右侧的值赋给左侧的变量,如下:

var num = 10; 

复合赋值操作

var num = 10;
num = num +10; // 20

等同于

var num = 10; 
num += 10; // 20

其他复合赋值操作符:*=/=%=+=-=<<=>>=


逗号操作符

用逗号操作符,可在一条语句中执行多个操作

var num1 = 1, num2 = 2, num3 =3;

逗号操作符多用于声明多个变量。


流控制语句


if语句

if (condition) statments1 else statments2

其中 condition(条件)可以是任意表达式,这个表达式的求值结果不一定是布尔值。ECMAScript会自动调用 Boolean() 转换函数将这个表达式的结果转换为一个布尔值。如果是true,则执行语句1,如果是 false,则执行语句2.

var i;
if (i>25){
  alert("Greater than 25.");
} else if (i<0){
  alert("Less than 0.");
} else{
  alert("Between 0 and 25, inclusive.");
}

while语句

while 语句属于前测试循环语句,就是说,在循环体内的代码被执行之前,不会对出口条件求值。
因此,循环体内的代码可能永远不会被执行
语法:while(expression) statement

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

上面例子,变量i开始时的值是0,每次循环递增2.只要i的值小于10,循环就会继续下去。

for语句

for语句也是一种前测循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码的能力。

var count=10;
for(var i = 0;i<count;i++){
    console.log(i);
}

上面例子,定义了变量i的初始值为0,只有当条件表达式(i<count)返回 true 的情况下才会进入 for 循环,因此也有可能不会执行循环体中的代码。如果执行循环体中的代码,则一定会对循环后的表达式 (i++)求值,即递增 i 的值。这个 for 循环与下面 while 语句的功能相同;

var count =10;
var i;
while(i<count){
    console.log(i);
    i++;
}

for 语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这三个表达式全部省略,就会创建一个无限循环,如下:

for(;;){    // 无限循环
    doSomething();
}

只给出控制表达式实际上就是把 for 循环转换成了 while 循环,如下:

var count=10;
var i = 0; 
for(;i<count;){
    console.log(i);
    i++;
}

for 语句比较灵活,用的比较多;

for-in 语句

for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性。

for (var propName in window){
    console.log(propName);
}

上面例子,使用for-in循环显示了 BOM 中 window 对象的所有属性,每次执行循环时,都会将 window 对象中存在 的一个属性名赋值给变量 propName。这个过程会一直持续到对象中的所有属性被枚举一遍为止。

ECMAScript 对象的属性没有顺序。因此,通过 for-in 循环输出的属性名的顺序是不可预测的,具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。

break 和 continue 语句

break 和 continue 语句用于在循环中精确地控制代码的执行。

break 语句会立即退出循环,强制继续执行循环后面的语句。
continue 语句也是立即退出循环,但退出循环后从循环的顶部继续执行。即退出当次的循环。

var num = 0; 
for (var i=0; i<10; i++){
    if (i%5 == 0){
        break;
    }  
    num ++;
}
console.log(num);  // 4

上面例子,当if 语句检查 i 的值是否可以被5整除。如果是,则执行 break 语句退出循环。在变量i 等于5时,循环总共执行了4次。而break 语句的执行,导致循环在 num 再次递增前就退出了。
如果把 break 换成 continue 的话,会是另一个结果:

var num = 0; 
for (var i=0; i<10; i++){
    if (i%5 == 0){
        continue;
    }  
    num ++;
}
console.log(num);  // 8

上面例子,循环共执行8次,当变量i 等于5时,循环会在num再次递增前退出,但接下来执行的是下一次循环,即的值等于 6的循环。于是,循环又继续执行,直到 i 等于10时自然结束。

switch 语句

switch 语句与 if 语句的关系最为密切,所有switch 语句都可以用 if - else if - else 代替。

var i;
swith(i){
  case 25:
      console.log('25');
      break;
  case 35:
      console.log('35');
      break;
  case 40:
      console.log('40');
      break;
  default:
      console.log('other');
}

switch 语句中的每一种情形(case)的含义是:如果表达式等于这个值(value),则执行后面的语句。而break 关键字会导致代码执行流跳出 switch 语句。 如果省略 break 关键字,就会导致执行完当前case 后,继续执行下一个 case ,所以要特别注意。最后的 default 关键字则用于在表达式不匹配前面所有情形的时候,执行机动代码。

case 也可以是布尔值, 即 case i<25:

switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换,例如 字符串 ‘10’ 不等于数值 10.

函数

函数对任何语言来说都是一个核心的概念,通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用 执行。ECMAScript 中的函数使用 function 关键字来声明,如:

function sayHi(name,mes){
    console.log('Hello '+name+','+mes);
}
sayHi('Jack','nice to meet you!');  // Hello Jack,nice to meet you!

ECMAScript 中的函数在定义时不必指定是否返回值,实际上,任何函数在任何时候都可以通过 return 语句后跟要返回的值来实现返回值,如:

function sum(n1,n2){
  return n1+n2;
}
var rs = sum(2,3);
console.log(rs);  // 5

这个 sum() 函数的作用是把两个值加起来返回一个结果。这个函数会在执行完 return 语句之后停止并立即退出,因此,位于 return 语句之后的任何代码都永远不会执行。

function sum(n1,n2){
  return n1+n2;
  alert('hello');  // 不会执行
}

上面例子,alert() 函数位于retrun 语句后面,因此不会执行。
另外,return 语句也可以不带有任何返回值,在这种情况下,函数在停止执行后将返回 undefiend 值,这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。

function sayHi(name,mes){
    return;
    console.log('Hello '+name+','+mes);   // 此句不会执行
}

本章总结:
ECMAScript是 Javascript 语言的国际标准,Javascript 是ECMAScript 的实现。
Javascript 中的基本数据类型包括: Undefined Null Boolean Number String.
ECMAScript 没有为整数和浮点数分别定不同的数据类型,Number类型可以用于表示所有数值。
ECMAScript 也有一种复杂的数据类型,即Object类型,该类型是这门语言中所有对象的基础类型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342