征服javascript学习笔记(三)——基本概念(下)

操作符

操作符有多种,包括算术操作符(加、减号)、位操作符、关系操作符和相等操作符,能够适用于很多值,例如:字符串、数字值、布尔值,甚至对象,在应用于对象时,相应的操作符通常会调用对象的ValueOf()或toString()方法,以便取得可以操作的值。

一元操作符

ES中最简单的操作符,只能操作一个值。

1、递增和递减

借鉴自C,各有两个版本:前置和后置。

顾名思义,使用的时候,就是放到前面或者后面,能起到什么作用

var age=29;
++age;

得到的结果为30.“–”符号亦然,会得到28

至于后置,那就是先输出结果后进行计算了,不再赘述。

2、一元加、减

一元加减应用到数值上面,和我们平时的认知没有不同,代表着正负数。

在对非数值应用时,就像Number()转型函数一样对值进行转换,布尔值true和false会被转换为1和0,字符串值会按照一定的规则进行解析,对象是先调用它们的valueOf()和toString()方法,再转换得到的值。

一元减操作符可以用于负数,一元加、减均可用于基本运算。

位操作符

位操作符用于最基本的层次上,按内存中表示数值的位来操作数值,ES中的所有数值都是以64位格式存储,但位操作符不直接操作64位的数,是将64位的值转换成32位的整数,然后执行操作,最后将结果转换回64位,对于开发人员来说,64位存储格式是透明的,因此整个过程就像只有32位的整数一样。

对于有符号的整数,32位中的前31位用于表示整数的值,第32位用于表示数值的符号:0表示正数,1表示负数。叫做符号位。

符号位的值决定了其他位数值的格式,其中,正数以纯二进制格式存储,31位中的每一位都表示2的幂,负数同样以二进制码存储,但使用的格式是二进制补码,计算一个数值的二进制补码,需要进行3个步骤:
(1)求这个数值绝对值的二进制码
(2)求二进制反码
(3)得到的二进制反码加1

1、按位非(NOT)

由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码,是ES操作符中少数几个与二进制计算相关的操作符之一。

2、按位与(AND)

由一个和号字符(&)表示,有两个操作数,从本质上讲,就是将两个数值的每一位对齐,然后根据有0为0,两个都是1为1的规则,对相同位置上的两个数进行计算

3、按位或(OR)

由一个竖线符号(|)表示,也有两个操作数,遵循有1为1,同为0则为0的规则对相同位置的数进行计算。

4、按位异或(XOR)

由一个插入符号(^)表示,两个操作数,遵循:两位不同为1,相同为0的规则。

5、左移

由(<<)表示,会将数值的所有位向左移动指定的位数。

6、有符号的右移

由(>>)表示,会将数值向右移动,但保留符号位。有符号的右移操作和左移操作恰好相反。

7、无符号右移

由(>>>)表示,会将所有32位都向右移动。对正数来说,无符号右移的结果和符号右移相同,但对负数来说,情况就不同了,首先,无符号右移是以0来填充空位,而不是像有符号右移那样以符号位的值来填充空位,所以,对正数的无符号右移与有符号右移结果相同,但负数的结果就不一样了,其次,无符号右移操作符会把负数的二进制码当成正数的二进制码,而且,由于负数以其绝对值的二进制补码形式表示,因此会导致无符号右移后的结果非常大。

布尔操作符

3个:非(NOT)、与(AND)、或(OR)

逻辑非,一个!号表示,无论操作的值是什么类型,它都会返回一个布尔值,首先把操作对象转换成布尔值,再对其求反。当然,它也可以用作把一个值转换成其对应的布尔值,同时使用两个逻辑非操作符,实际上会模拟Boolean()转型函数的行为,就得到了这个值真正对应的布尔值。

逻辑与,由两个和号(&&)表示,两个操作数。遵循“同为真则真,有一个假则为假”的规则,可以应用于任何类型的操作数,在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值。

逻辑或,由(||)表示,两个操作数,遵循“同假为假,其他为真”的规则。

乘性操作符

乘法 *
除法 /
求模 %

加性操作符

加法 +
减法 -

关系操作符

小于(<)、大于(>)、小于等于(<=)或大于等于(>=)

相等操作符

相等和不相等 “==”和“!=”
全等和不全等 “===”和“!==”

条件操作符

a>b?1:2;

若a大于b则为1,不大于则为2

赋值操作符

用“=”表示,如:a=3;将右侧的值赋给左侧

每个主要的算术操作符都有对应的复合赋值操作符,如:*=、/=、%=、+=、-=、<<=、>>=.

逗号操作符

使用逗号操作符可以在一条语句中执行多个操作,如下例:
var num1=1,num2=2,num3=3;
逗号操作符多用于声明多个变量,除此之外,逗号操作符还可以用于赋值,在用于赋值时,逗号操作符会返回表达式的最后一项,如下例:
var num=(1,2,3,4,5);
最终num的值为5.

语句

首先,我们要知道语句是用来做什么,比如,条件判断,我们常常需要做比较,某个条件成立的情况下执行某个动作,又或者循环,网页中每种类型的信息都可能是多个,那么对多个进行判断或者操作,就可能会用到循环,当然,也跟数组有关,后面会有,这里不探讨。

1、if

if(a){
    do this
    } else {
    do this
    }

虽然在比较简单的情况下,比如一个简单的判断之后执行一个动作,那么它们可以被写在一行上,但是,业界普遍推崇的最佳实践是始终使用代码块,不管是简单还是复杂

2、do-while

是一种后测试循环语句,只有在循环体中的代码执行之后,才会测试出口条件,即循环体内的代码至少会被执行一次:

var i=1;

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

    alert(i);

只要变量i的值小于10,语句就会一直执行下去。

3、while

跟do-while相对,它是前测试语句,先判断,后执行。

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

4、for

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

包括初始化变量定义,条件表达式,和循环的方式三个参数设置,例如:

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

其实你可以发现,它跟下面的语句功能相同

var count=10;
    var i=0;
    while(i<count){
    alert(i);
    }

for循环只是把循环有关的代码集中到了一个位置,故而,while做不到的,for也做不到。需要指出的是,在for循环的变量初始化表达式中,可以不用var,也可以在外部进行初始化。

for语句有极大灵活性,是ES中最常用的一个语句。

5、for-in

是一种精准的迭代语句,可以用来枚举对象的属性,譬如示例:

for(var propName in window){
    document.write(propName);
}

上述例子,使用for-in循环来显示了BOM中windows对象的所有属性,注:ES对象的属性没有顺序,因此,通过for-in循环输出的属性名顺序是不可预测的。

6、label

使用label语句可以在代码中添加标签,以便将来使用。

label:statement

示例:

start:for(var i=0;i<count;i++){
    alert(i);
}

此例中定义的start标签可以在将来由break或continue语句引用,加标签的语句一般都要与for语句等循环语句配合使用。

7、break 和 continue

此二者用于在循环中精确的控制代码的执行,其中,break会立即退出循环,强制执行循环后的语句,而continue语句虽然也会立即跳出循环,但退出循环后会从循环的顶部继续执行。

8、with

with语句是将代码的作用域设置到一个特定的对象中,定义with语句的目的主要是为了简化多次编写同一个对象的工作,如下

var qs=location.search.substring(1);
    var hostName=location,hostname;
    var url=location.href;

上面几行代码都包含location对象,如果使用with语句,可以写成如下:

with(location){
    var qs=location.search.substring(1);
    var hostName=location,hostname;
    var url=location.href;
}

但是,大量使用with会导致性能下降,同时也给调试代码带来困难,大型应用程序中不建议使用with语句。

9、switch

switch语句和if语句关系密切,同样可用于多种条件的判断,代码示例如下:

switch(i){
    case:25:
    alert("25");
    break;
    case:35:
    alert("35");
    break;
    case:45:
    alert("45");
    break;
    default;
    alert("Other");
}

通过为每个case后面添加一个break语句,可以避免同时执行多个case代码的情况,若确实需要合并几种情况,要在代码中写清楚注释。

可以在switch中使用任何数据类型,而且每个case的值不一定是常量,可以是变量甚至表达式。

switch语句在比较值时使用的是全等操作符,因此不会发生类型转换。

函数

提到函数,不得不说的一个词是封装,它可以封装任意多条语句,可以在任何地方,任何时候调用执行,使用function关键字来声明,后跟一组参数及函数体。譬如:

function sayHi(name,message){
    alert("hello"+name+","+message);
}

可以通过其函数名进行调用,后面加上一对圆括号和参数,多个参数逗号分开。

sayHi("idea",“you are good”);

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

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

这个sum()函数的作用是两个值的和,除了return语句,没有任何声明表示该函数会返回一个值,调用示例如下:

var result=sum(5,10);

函数会在return语句之后立即退出,若return后面还有代码,将不会被执行。

当然,一个函数可以在函数体的不同代码块中包含多个return语句。另外,return语句可以不带任何返回值,这时,函数在停止执行后将返回undefined值,用于需要函数提前终止又不需要返回值的情况。

推荐做法是,要么让函数始终返回一个值,要么永远不要返回值,否则会给代码调试带来不便。

理解参数

ES函数的参数和其他语言的函数参数不同,它不在乎传进来多少参数,也不在乎参数的数据类型,定义的函数参数个数和调用是传递的参数之间没有必然联系,之所以会这样,是因为ES中的参数内部是用一个数组来表示的,函数接收到的始终都是这个数组,而不关心数组中包含哪些参数,如果这个数组中不包含任何元素,也无所谓,包含多个元素,也没问题,实际上,在函数体内,通过arguments对象来访问这个参数组,从而获取传递给函数的每一个参数。

其实argument对象只是与数组类似,前面例子可以这样写

function sayHi(){
    alert("hello"+arguments[0]+","+arguments[1]);        
}

它不包含命名参数,但函数功能依旧,这个事实说明了ES函数的一个重要特点,命名的函数只是提供了便利,但不是必需的。当然,另外一点是,arguments对象可以和参数一起使用,它的值永远和对应命名参数的值保持同步。

需要记住的最后一点:没有传递值的命名参数将自动被赋予undefined值,这就跟定义了变量却并未初始化一样。

没有重载

ES函数不能像传统意义那样实现重载,而在其他语言中,可以为一个函数编写两个定义,只要两个定义接受的参数的类型和数量不同即可。如前面所述,ES中的函数没有这些,其参数是由包含零活多个值的数组来表示的,所以,重载是不可能做到的。

如:

function addSomeNumber(num){
    return num+100;
}

function addSomeNumber(num){
    return num+200;
}

var result=addSomeNumber(100);  //300

此处,函数被定义了两次,但后定义的函数覆盖了先定义的函数,所以最后的值为300.

好了,关于基本概念,到此就告一段落了,还是有不少内容的,接下来依然路途遥远,但请相信只要我们坚持下去,它就不再那么遥远,定会越来越精彩,一起继续加油吧!~