Javascript笔记(三)之语法基础介绍

运算符

算数运算符

+运算符

加号运算符如下几个功能:

  • 正号:可以提供数学上到正数标示;
  • 加法操作:提供基本的数学加法运算
  • 字符串拼接:进行两个字符串到拼接操作

注意:
1、在进行字符串拼接到时候要注意运算到顺序问题
2、在进行字符串拼接到时候要注意隐式转换问题
3、如果+参与的运算里有对象,则会先调用valueOf()方法,如果不能转换为数值,再调用toString()方法,其他原理与isNaN()一致;

1+{}   // '1[object Object]'
1+{toString:function(){return 1;}}   //2
1+{valueOf:function(){return 1},toString:function(){return 1;}}  //2
1+{valueOf:function(){return 3},toString:function(){return 1;}}  //4
1+{valueOf:function(){return {}},toString:function(){return 1;}} //2

-运算符

减号运算符有如下几个功能:

  • 负号:可以提供数学上的负数标示;
  • 减法操作:提供基本的数学加法运算

注意:
1、在进行减法运算法的时候需要注意隐式转换问题;
2、同样是对象操作的时候,会先调用valueOf(),在调用toString();

1-{}   // 'NaN'
1-{toString:function(){return 1;}}   // 0
1-{valueOf:function(){return 1},toString:function(){return 1;}}  // 0
1-{valueOf:function(){return 3},toString:function(){return 1;}}  // -2
1-{valueOf:function(){return {}},toString:function(){return 1;}} // 0

*运算符

*号运算符有如下几个功能:

  • 乘法操作:提供基本的数学加法运算

注意:
1、在进行乘法运算法的时候需要注意隐式转换问题
2、与上述同理

1*{}   // 'NaN'
1*{toString:function(){return 1;}}   // 1
1*{valueOf:function(){return 1},toString:function(){return 1;}}  // 1
1*{valueOf:function(){return 3},toString:function(){return 1;}}  // 3
1*{valueOf:function(){return {}},toString:function(){return 1;}} // 1

/运算符

/运算符提供除法操作,提供基本的除法运算,返回结果是小数。

注意:
1、在进行除法运算法的时候需要注意隐式转换问题
2、与上述同理

1/{}   // 'NaN'
1/{toString:function(){return 1;}}   // 1
1/{valueOf:function(){return 1},toString:function(){return 1;}}  // 1
1/{valueOf:function(){return 3},toString:function(){return 1;}}  // 0.3333333333333333
1/{valueOf:function(){return {}},toString:function(){return 1;}} // 1

%运算符

%运算符提供数学上取模运算或取余运算符。

注意:
1、在进行取余运算法的时候需要注意隐式转换问题

1%{}   // 'NaN'
1%{toString:function(){return 1;}}   // 0
1%{valueOf:function(){return 1},toString:function(){return 1;}}  // 0
1%{valueOf:function(){return 3},toString:function(){return 1;}}  // 1
1%{valueOf:function(){return {}},toString:function(){return 1;}} // 1

自增自减运算

自增++

对变量进行自增加一操作,其等价于a+=1a=a+1

自减--

对变量进行自增加一操作,其等价于a-=1a=a-1

不论是++还是--,我们都需要考虑到++--参与其他运算到时候,在前还是在后到运算顺序到问题,简而言之,在前的时候,先参与自增或自减运算,然后再参与其他运算,如果在后,先参与其他运算,在参与自增或自减运算。

不论是++还是--,都不能参与常量或字面量的运算,例如1++这是一种错误的表达式,将会抛出如下错误;
ReferenceError: Invalid left-hand side expression in postfix operation

赋值运算符与复合

= 赋值运算符
a+=b 等价于a=a+b
a-=b 等价于a=a-b
a*= b等价于a=a*b
a/=b 等价于a=a/b
a%=c 等价于a=a%b

位运算(Bitwise)

所有按位操作对操作数都会被转换位补码形式都有符号32位整数,补码形式指的是一个数都负对应值位数值都所有都bit位反转再加1;

按位与&

按位与操作是对每个bit位进行与操作,基本规则就是全1位1,否则位0;

按位或|

按位或操作是对每个bit位进行与操作,基本规则就是全0位0,否则位0;

按位异或^

按位或操作是对每个bit位进行或操作,基本规则就是不相同为1,相同0

按位非~

对每个bit位执行非操作,也就是对数求反码的操作

对于按位非操作,我们可以使用如下的操作形式,其性能较佳,但是不一定便于其他人理解

var str = 'rawr';
var searchFor = 'a';

// 这是 if (-1*str.indexOf('a') <= 0) 条件判断的另一种方法
if (~str.indexOf(searchFor)) {
  // searchFor 包含在字符串中
} else {
  // searchFor 不包含在字符串中
}

左移<<

左移n位,相当于乘以2的n次方

无符号右移>>>

无符号右移:向右移出的位被丢弃,左侧用0填充,因为符号位变0了(0填充),所以结果总是非负数。

有符号右移>>

有符号右移:向右移出的位被丢弃,拷贝最左侧以填充左侧,由于新的左侧位总是和以前相同,符号位没有被改变,所以被叫符号传播

可以简单理解:相当于除以2的n次方

位运算的案例:标志位与掩码(有深度哦)

位运算经常用来被创建、处理以及读取标志位序列——一种类似二进制的变量,虽然可以使用变量来代替标志位序列,但是这样可以节省内存(1/32)。

例如,有3个标志位,例如我们在linux系统之中,对文件有三种权限,我们可以采用一个3位对二进制数来表示,例如r位读权限,w位写权限,x位可执行权限。

标志位A  Executable,可执行
标志位B:Writable,可写
标志位C:Readable ,可读
标志位通过位序列CBA来表示,当一个位被置位是,它位1,被清除时,他为0
var flags = 5; //0101; 所以这个值表示为有可读、可执行,不可写

因为位运算是32位对,0101实际上是00000000000000000000000000000101,因为前面多余对0没有意义,所以可以忽略

掩码(bitmask)是一个通过&|来读取标志位对位序列,典型对定义每个标志位对原语掩码如下

var FLAG_EXECUTABLE = 1; // 0001
var FLAG_WRITABLE = 2; // 0010
var FLAG_READABLE = 4; // 0100

新的掩码可以在以上掩码上使用逻辑运算创建

var mask = FLAG_EXECUTABLE | FLAG_WRITABLE | FLAG_READABLE;
// 0001 | 0010 | 0100  => 111 

某个特定对位可以通过与掩码与运算的到,通过与掩码对与运算可以去掉无关对位置

var flags = 5; //0101; 所以这个值表示为可读、可执行、不可写
if( flags  &   FLAG_READABLE){  // 0101 & 0100 => 0100 => true, 判断是否被置为可读
 // do something
}

if ( ( flags  &   FLAG_READABLE) || ( flags & FLAG_WRITABLE))
  // ( 0101 & 0100 ) || (0101 & 0100) => 0100 || 0000 =>  0100  =>true 判断是否可读或可写
 // do something
}

//  一下方法与上一个方法一样
if (  flags  & ( FLAG_READABLE | FLAG_WRITABLE))
  // ( 0101 & 0100 ) || (0101 & 0100) => 0100 || 0000 =>  0100  =>true 判断是否可读或可写
 // do something
}

我们可以通过与掩码做或运算(|),进行设置标志位,将掩码对位设置对应对位,例如位们用掩码110来设置可读、可写;

var mask = FLAG_READABLE | FLAG_WRITABLE; // 0100 | 0010 => 0110
flags |= mask;  // 0101 | 0110 => 0111  // 可读、可写、可执行

同样,我们也可以通过与掩码做清除标志位,掩码中位0对位可以设置对应的位,掩码可以通过原语掩码来做非运算取得(~),例如我们使用001来设置不可写、不可读

var mask = ~( FLAG_READABLE | FLAG_WRITABLE) ; // ~110 =>001
flags &= mask; // 0101 | 0001 = 0001 => 可执行、不可读、不可写

// 根据德摩根定律如下代码与上述等价
var mask = ~FLAG_READABLE & ~FLAG_WRITABLE;
flags &= mask; // 0101 | 0001 = 0001 => 可执行、不可读、不可写

我们也可以使用异或运算(^)来切换标志位,例如使用掩码101来切换标志位可读和可执行

var flags = 4; // 可读
var mask = FLAG_WRITABLE  | FLAG_EXECUTABLE;    // 010 | 001=> 011
flags ^= mask; // 0100 ^ 011 = 011 => 可写、可执行

案例2:使用位运算来完成二进制转换(该代码位于mdn上)

function createBinaryString (nMask) {
  // nMask must be between -2147483648 and 2147483647
  for (var nFlag = 0, nShifted = nMask, sMask = ""; nFlag < 32;
       nFlag++, sMask += String(nShifted >>> 31), nShifted <<= 1);
  return sMask;
}

var string1 = createBinaryString(11);
var string2 = createBinaryString(4);
var string3 = createBinaryString(1);

关系表达式

==(等于)与!=(不等于)

如果一个操作数是非数,一个操作数为数值的情况遵循以下规则

  1. 一个操作数是布尔值,则比较之前将其转换为数值,false转换为0,true转换为1;
  2. 一个操作数是字符串,则比较之前将其转换为数值再比较;
  3. 一个操作数是对象,则先调用valueOf()或toString()方法;
  4. 不需要任何转换的情况,null和undefined是相等的;
  5. 一个操作数是NaN,则==返回false,!=返回trueNaN有自身不等特性;

注意:nullundefined在比较等于的时候不会自动转换;

===!==

1、全等于
全等于要求数据类型相等并且值也相等,如果类型不同,返回false,如果类型相同,则判断值是否相同

2、不全等于
如果类型不同或值不同,返回true,否则返回false

<<=>>=

比较规则:
1、如果关系运算符两边的算子都是number类型,则进行数值比较;
2、如果关系运算符两边的算子都是字符串类型,则进行ASCII码值比较;
3、如果只有一个字符串,一个是数值类型,则会转换为数值,再比较;
4、如果有一个是对象,则先调用valueOf()方法或调用toString(),如果这两个方法没有重写,返回false;
5、返回值为布尔类型,truefalse;
6、null会自动转换为0, undefined则不会,undefined和任何数比较都是false;

0>null  // false
0>=null // true
0>{} //false
0>{toString:function(){return '1'}} // false
2>{valueOf: function(){return 2}}  / false
3>{toString:function(){return '1'}} // true
4>{valueOf: function(){return 3}}  // true

in运算符

in 运算符如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。其语法形式如下

prop in object
// prop    一个字符串类型或者 symbol 类型的属性名或者数组索引(非symbol类型将会强制转为字符串)。
// objectName  检查它(或其原型链)是否包含具有指定名称的属性的对象。

值的注意的一点,in判断的是属性,而不是属性值,如果属性被删除了将返回false,如果你赋值为undefined将返回true(因为该属性存在,只是其值为undefined),同时也能判断继承来的属性;

逻辑运算符

逻辑运算符通常用于布尔值都操作,一般和关系运算符配合使用,有三个逻辑运算符:逻辑与、逻辑或、逻辑非

逻辑与&&

如果运算符两边有一个不是布尔值都情况,与运算符不一定返回布尔值,此时遵循如下规则

  1. 如果第一个操作数是对象,则返回第二个操作数;
  2. 如果第二个操作数是对象,则第一个操作返回true,才返回第二个操作数,否则返回false;
  3. 如果有一个操作数是null,则返回null,针对第一个操作数为null或第一个操作数为true的情况;
  4. 如果有一个操作数是undefined,则返回undefined,针对第一个操作数为undefined或第一个操作数为true的情况;

逻辑与采用短路运算规则,如果第一个为false,则为false,如果全为true才为true;

true && null  // null
true && undefined // undefined
false && null  //false
false && undefined //false

var a= {t:1};
a && false; // false
a && 123; // 123
true && a  // { t: 1 }
false && a  // false

逻辑或||

如果运算符两边有一个不是布尔值都情况,或运算符不一定返回布尔值,此时遵循如下规则

  1. 如果第一个操作数是对象,则返回第一个操作数;
  2. 如果第一个操作数的求值结果为false,返回第二个操作数;
  3. 如果两个操作数都是对象,则返回第一个操作数;
  4. 如果两个操作数都是null,则返回null
  5. 如果两个操作数都是NaN,则返回NaN
  6. 如果两个操作数都是undefined,则返回undefined
  7. 如果有一个操作数是nullundefinedNaN,则返回另一个操作数

逻辑与采用短路运算规则,如果第一个为true,则为true,如果全为false才为false;

我们可以使用第七个特性来避免nullundefined,例如客户端JavaScript之中的事件对象event的浏览器兼容性问题,在IE/Opera中,是window.event,而在Firefox中,是event
而事件的对象,在IE中是window.event.srcElement,在Firefox中是event.target,而在Opera中则两者都支持。

function test(event){
  var event=window.event || event;
  var objEle = event.target || event.srcElement;  // 获取事件的document 对象的引用
}

逻辑非!

逻辑非只有一个操作数,可以用于任何值,无论这个值是什么数据类型。它的流程就是先将这个值转换成布尔值,在取反;前面我们已经知道了下面如下几种情况返回false;

  1. null
  2. undefined
  3. '"''
  4. NaN
  5. 0或-0
  6. false

非真即假,非假即真,所以!true == false

猜你喜欢

转载自blog.csdn.net/fj_Author/article/details/82084695