目录express
“语法”指的是一门语言的书写风格,JavaScript 的语法风格很相似于 C 以及 Java。
语法又是一种概念上的统称,例如如何声明变量、定义函数、书写流程控制语句或者是进行条件判断等,这些都是属于语法风格的范畴。编程
所谓的“标识符”就是对变量、标签、函数、方法、属性以及参数名称的统称。
标识符能够分为“用户自定义标识符”与“系统标识符”,其中对与系统标识符,咱们能够根据其使用情况,又划分为“关键字”或者是“保留字”。
用户自定义标识符,其命名应遵循如下规则:数组
经过声明 use strict
可让支持严格模式的 JavaScript引擎切换到另外一种解析与执行模式,在严格模式下 ES3中的一些不肯定行为将会按照ES5(ES3.1)的规定去处理。
use strict
若是声明在代码的第一行,则整个JS程序都会按照严格模式去执行,若是你只想在某个局部区域使用该模式,请将 use strict
单独加在某个局部做用域的开头。安全
用于特定用途的标识符,这些标识符可用于表示控制流程的开始或结束,或者用于执行特定的操做,例如声明变量、定义函数等。ecmascript
保留字是语言层面保留的标识符,它与关键字的主要区别就在于做为保留字的标识符暂时还未指定用途,但它们将来极可能会用做于关键字。编程语言
ECMAScript中的变量是弱类型的,至关于保存值的占位符。
声明但未初始化的变量,其默认值为 undefined
。
ECMAScript支持同时声明并初始化多个变量,每一个变量用逗号隔开函数
var obj = {}, str = 'string', num = 0, nul = null, und = undefined, boolean = true;
在ECMAScript中不经过
var
关键字声明的变量,默认会附加到全局window
对象上。oop
a = 1; window.a // 1
该种方式在 ECMAScript 5 的严格模式(strict)下是不被容许的。性能
下面是ECMAScript5 的数据类型:this
对于数据类型的检测,JavaScript中也提供了一个内置的操做符 -- typeof
,经过 typeof
咱们能够准确的得到"string"、"number"、"boolean"、"undefined"等基本数据类型,可是须要注意的是使用 typeof
对 null
或者其它对象型或者是数组检测的时候,统一都返回 object
类型。
undefined
类型只有一个值,那就是:"undefined"。
通常而言咱们声明未初始化的变量其默认值就是 undefined
,而ECMAScript引入 undefined
的做用就是为了更好的与 null
进行区分。
这里有一个技巧,使用 typeof
操做符来判断未声明的变量,并不会如咱们想的那样会报错,而是返回 undefined
值。
typeof und // undefined
null
与 undefined
相同都是只有一个值的数据类型。
null
表明一个 “空对象指针”,这也是为何使用 typeof null
返回的倒是一个 object
类型的缘由。
null
对于哪些须要保存为对象但还未真正保存具体对象引用的变量很是有用。也就是说若是定义的变量专门是用来保存对象,那么最好在定义变量的同时初始化为 null
而不是其它值,这样带来的好处体现于,只要检测 null
值就能够得知变量是否保存了一个真正对象的引用。
var obj = null; if(typeof obj === "object" && obj != null){ //.... }
因为 nundefined
的值是派生与 null
(其实不少的数据类型都是派生与null,由于对象原型链的源头就是null),所以ECMAScript的语法规定在作相等性检测的时候返回固定值true
。
null == undefined // true
总的来讲,null是专门用来初始化须要保存对象引用的变量,这也是 null
与 undefined
之间的重要区别。当变量用于保存基本数据类型时,无须要初始化为 undefined
,由于默认就是 undefined
,而对于用于保存对象的变量 null
则须要显示的初始化,这既是用于区分一个变量是否引用了一个有效的对象,也有助于进一步区分 undefined
与 null
。
var age; var name; var sex; var othre = null; age = 26; name = 'gtshen'; sex = 'man'; other = { height:1.7 }
boolean
类型的值有两个:true(真)、false(假),虽然只有两个,可是 ECMAScript中全部类型的值与这个两个值都有等价关系,而想将其它类型的值转换为对相应的Boolean值,其实很是简单,只须要调用 Boolean()
方法便可。
Boolean('string'); // true Boolean(0) // false
其转换依据以下:
数据类型 | 转换为 true | 转换为 false |
---|---|---|
String | 不为空 | 为空('') |
Number | 不为0 | 0 或者 NAN |
Object | 任意对象 | null |
Undefined | / | undefined |
掌握 Boolean()
方法的转换依据有助于咱们明白JavaScript数据类型的隐式转换。
var str = 'string'; if(str) alert(true);
简介
与其它语言不一样的是,在ECMAScript中 Number
类型只有一种而且不区分整数与小数。统一采用 IEEE754 标准,使用64位双精度浮点数来表示与存储。只是在运算的时候,会将能够转换为整数的小数以整数的形式进行计算,从而减小小数位数来更好的节省内存空间。
该标准不只规定了浮点数的运算规则与表示格式,还规定了一些特殊值,例如正零(+0)与负零(-0)、无穷数(Infinity)以及非数值(NaN)等。
字面量格式
ECMAScript支持多种字面量格式,除了默承认以直接使用的十进制数,整数还能够经过八进制数以及十六进制的字面量来表示。
0
开头,若是超出数码的范围,则默认转换为十进制数来保存。0x
开头。070 // 八进制 56(D) 080 // 十进制 80(D) 0x1A // 十六进制 26(D)
须要注意的是ECMAScript5中的严格模式并不支持八进制数。
虽然 ECMAScript 能够支持多种进制数,可是在运算的时候还会自动转换为对应的十进制数。
除了支持多种格式的数值字面量,ECMAScript还支持数值的一种表示方法 -> “科学计数法”。
M×R^E
M
是有效数字,R
是基数,默认为10,E
就是指数。
3125000 => 3.125×10^6 => 3.125E6 0.003 => 3×10^-3 => 3E-3
NaN
NaN
即非数值(Not a Number),用于表示返回一个非正确的数值结果。
NaN
特色的就是与任何类型的值进行运算,返回的结果仍然是 NaN
。任何值与NaN
进行等价判断,返回的结果总会是 false
。
ECMAScript 提供了一个 isNaN
的方法,能够用来检测当前的值是不是一个 NaN
。是的话,返回true
,不是则返回 false
。
isNaN(NaN) // true isNaN('123') // false isNaN(true) // false isNaN('blue') // true
isNaN()
方法在具体的检测中也会根据须要进行值的类型转换。
精度与范围
因为内存容量的限制,以及按照IEEE754规定使用64位二进制位来保存浮点数,所以在 ECMAScript中浮点数的表示范围与精度都是有限的,不可能存的下这个世界上全部的值。
先观察下面这个计算:
0.1 + 0.2 // 0.30000000000000004
运算的结果超出咱们的预料,本来觉得结果是 0.3
,实际上的结果倒是 0.30....4
,咱们先抛开结果不谈,就说下经过观察这个计算,而得出的一些结论:
0.300..4
小数点的位数只有17位,这说明了ECMAScript最多只能保持17位小数的精度,超出部分会被舍去。.300.4
结尾是4,结合第一条超出17位会被舍去的结论,这里确定不只发生舍去还发生了进位。0.1
与 0.2
在转换的时候发生了无限不循环的状况呢?通过查证,咱们能够得知在不少编程语言(包括ECMAScript)其底层的运算都是基于二进制补码的形式来计算的,因此咱们将 0.1
与 0.2
转换为对应的补码:
0.1[补] = 0.0001 1001 1001 1001 1001 1001 1001 1001 …(1001无限循环) 0.2[补] = 0.0011 0011 0011 0011 0011 0011 0011 0011 …(0011无限循环)
接着咱们再大体了解下 IEEE754的存储格式,IEEE754会使用双精度(64bit)来存储浮点数,其中符号位占据1位,指数位占据11位,而尾数部分则占据52位,具体以下图所示:
这也就直接说明了 0.1
与 0.2
在底层转换为补码的时候,就会由于无限循环而最多只能保存53位尾数的状况下发生了舍去进位,从而丢失精度。并且 0.1
与 0.2
在进行求和的时候,其结果还会再进一步舍去进位,丢失精度,最终在JavaScript中以最多17位小数的形式输出丢失精度的结果。
另外,须要注意的是,IEEE754还规定了,有效数字第一位默认老是1。所以表示精度的尾数前面,还存在着一个隐藏位,固定为1,它不包含在64位浮点数长度之中,也就是说尾数的格式老是:1.xx...xx
的格式,其中 xx...xxx
保存在64位长度之中,最长为52位,因此 JavaScript提供的有效数字(尾数)实际最长为53个二进制位。
这是为何 Number.MAX_SAFE_INTEGER
的值是 9007199254740991
而 Number.MIN_SAFE_INTEGER
是 -9007199254740991
这是由于受限与尾数的位数,其数值可表示的安全范围则是: [-2^53-1,2^53-1]。
Number.MAX_SAFE_INTEGER === Math.pow(2,53)-1 //true Number.MIN_SAFE_INTEGER === -Math.pow(2,53)+1 //true
可是实际上,因为尾数前面的隐藏位总为1,因此只要其范围在 (2^52)-1 < x <= (2^53)-1
以内 均可能会出现精度不许的状况。也就是说JavaScript中数值最安全的范围是在 [-(2^52)-1,(2^52)-1]
范围内。
接着再说下JavaScript的最大值与最小值,若是说数值的精度是由尾数的数量来决定的话,那么数值的大小或者说范围就是由指数来决定,IEEE754中,指数的长度是11位,所以指数部分能表示的最大指数就是 2^11-1
即 2047,而后根据规定再取中间值进行偏移,用来表示负指数,也就是说指数的范围是 [-1023,1024]
之间,所以,这种存储结构可以表示的数值范围为 2^1024
到 2^-1023
,超出这个范围的数JavaScript便没法表示,只能返回 Infinity
,Infinity 根据值的正负状况具备 +Infinity
与 -Infinity
。
2^1024
转换为科学计数法以下所示:
2^1024 = 1.7976931348623157 × 10308 === Number.MAX_VALUE === 1.7976931348623157e+308
也所以最小值为 Number.MIN_VALUE = 5e-324
既无限接近于 0。
总的来讲,对于JavaScript中Number类型有效值的范围以及最大最小值能够经过Number对象的如下属性得到:
Number.MIN_VALUE //最小值 Number.MAX_VALUE //最大值 Number.GATIVE_INFINITY //负极限 Number.POSITIVE_INFINITY //正极限
也能够经过如下的方法来判断当前的值是否超出ECMAScript的可表示范围:
Number.isFinite(num)
最后,受限于JavaScript对数值的精度与范围的问题,要尽可能避免使用JS来运算和处理大数值数据,若是必需要处理的话,也请使用一些第三方的数学库,来规避这些问题,这里推荐的数学库有:Math.js、decimal.js、big.js。
数值转换
在 ECMAScript中有三个方法能够显示的将其它类型的值转换为对应的数值型,这三个方法分别是:Number()
、parseInt()
、parseFloat()
。
Number()
方法是对 Number
对象的静态调用,它支持将多种数据格式转换为对应的数值类型,其转换的主要规则(凭据)以下:
undefined
类型会转换为 NaN
。null
类型会转换为 0
,空字符串也会转换为 0
。Number()
方法会返回 NaN
。须要注意的是若是字符串的开头与结尾含有空格,那么 Number()
会忽略前导与后导空格,但并不会忽略其它部位的空格。true
转换为1 false
转换为 0。valueOf()
方法返回对象的值,若是返回的值仍然是一个引用类型而不是基本类型的值,那么再次调用对象的 toString()
方法,最后再转换。Number(' 123 ') // 123 Number(0xla); // 26 Number('') // 0 Number('123abc') // NaN
相比较 Number()
方法,parseInt()
以及 parseFloat()
的转换规则要简明的多,只要是符合字符串形式的数值(正负号、小数点,数值0-9)均可以使用parseInt()
或 parseFloat()
转换为对应的十进制数,只是须要注意的是, parseInt()
方法并不识别小数点。
parseInt("123"); //123 parseInt("+123") //123 parseFloat('-123.123'); //-123.123
当 parseInt()
与 parseFloat()
在转换字符串形式的数值时,若是遇到特殊的字符,则会中止转换,并返回以前符合条件的值,而若是转换的值是其它类型的值,则返回NaN。
parseInt('123.123'); //123 parseInt('1abc123'); //1 parseFloat(true) //NaN
相比较parseFloat()
, parseInt()
在转换的时候能够支持将八进制、十六进制转换为对应的十进制。
parseInt(070) //56 parseInt(0x1a) //26
parseInt()
还具备第二个参数,用来指定当前进行转换的数的进制,从而在转换的时候无需为被转换的数添加所需的前缀。
parseInt(70,8) //56 parseInt('1a',16) 26
一样在ES5严格模式下不支持八进制。
简介
String
类型的值就是指由零个或多个使用16位unicode字符保存的字符序列,即字符串。
转义字符
String
类型的值除了很是广泛的 unicode
字符序列,它还有一些具备特殊含义的字面量,好比“转义字符”
转义字符 | 含义 |
---|---|
\n | 换行 |
\t | 制表符 |
\b | 退格 |
\r | 回车 |
\f | 分页 |
\[*] | 反转义 ,即不转义。例如:\ 就是输出斜杠,' 输出单引号 |
\xnn | 使用十六进制编码来表示一个字符。例如\x41 就是表示 "A" |
\unnnn | 使用四位unicode编码来表示一个字符。例如 \u03a3 表示希腊字母Σ |
实际上经过仔细观察能够发现,转义字符都会以斜杠(\) 开头。
字符串转换
将其它数据类型的值显示的转换为 “字符串” 能够直接使用每种数据类型都会自带的 toString()
方法。
var num = 123; var boolean = true var obj = {}; num.toString() // "123"; boolean.toString() // "true"; obj.toString() //"[object Object]";
须要注意的是若是是将数值转换为字符串,toString()
方法还能够接受一个参数,即转换为对应的进制数的字符串。
var num = 10; num.toString(2); //"1010" num.toString(8); //"12" num.toString(16); //"a"
若是不带参数,默认状况下就是直接转换为十进制。
为何每种数据类型都会自带 toString()
方法呢?实际上道理很简单,那就是 ECMAScript中万物皆对象,对象中自带了 toString
方法,而别的数据类型则也是继承至对象,因此便与生俱来自带有 toString
方法。
很明显的是像 null
,undefined
这两个类型明显不是派生于对象,因此它们并不具备 toString
方法。
var nl = null; var un = undefined; nl.toString(); //Cannot read property 'toString' of null un.toString(); //Cannot read property 'toString' of undefined
在此种状况下若是想将 null
与 undefined
再经过对象自带的 toString()
方法转换为字符串明显是行不通的了。
解决的方法也很简单,那就是与 Number
类型同样,使用 ECMAScript 自带的静态 String()
方法。
String(null); // "null" String(undefined); // "undefined"
因此在进行字符串转换的时候,为了稳妥起见仍是尽量的多使用 String()
方法。这是由于String()
方法遵循如下转换规则:
toString()
方法,则调用其 toString()
方法。null
则转换为 "null"。undefined
则转换为 "undefined"。可是使用者须要明白的是,并非全部的引用类型都会继承至object
的toString()
方法,也会具备适合本身的toString()
方法
'123'.toString() // '123' (255).toString(16) // 'ff' Number(255).toString(16) //'ff' [].toString() // "" [1,2,3].toString() // "1,2,3" ({}).toString() //"[object Object]"
固然,你也能够统一的使用某个对象的 toString()
方法来保持统一的转换结果。
Object.prototype.toString.call({}) //"[object Object]" Object.prototype.toString.call([]) //"[object Array]" Object.prototype.toString.call("") //"[object String]" Object.prototype.toString.call(1) //"[object Number]" Object.prototype.toString.call(true) //"[object Boolean]" Object.prototype.toString.call(null) //"[object Null]" Object.prototype.toString.call(undefined)//"[object Object]"
对象类型,也能够称之为“枚举”类型或者是“引用”类型,它是数据与功能的集合,其中数据就是属性,而功能就是方法。
在ECMAScript中 Object
对象是其它全部对象的基础,所以Object
对象自带的一些属性方法也必然也会被它的实例化对象所继承。下面就是 Object
对象自带的一些经常使用属性以及方法:
constructor
保存着建立当前对象实例的构造函数。
new Object().constructor; // "ƒ Object() { [native code] }"
这说明当前对象的构造函数就是 Object()
。
hasOwnProperty
该方法接收一个字符串做为参数,这个字符串就是 key,判断这个key是否在该对象的实例中,而非继承至原型。
var obj = new Object(); obj.custom = 1; obj.hasOwnProperty('custom'); // true obj.hasOwnProperty('custom1'); // false
isPrototypeOf(obj)
用于检查传入的对象是不是当前对象的原型上。
function A(){}; var a = new A(); console.log(A.prototype.isPrototypeOf(a)); //true
propertyIsEnumerable(propertyName)
检查当前对象上指定的属性是否可枚举(for...in循环遍历)。
function A(){ this.custom = 1; }; var a = new A(); a.propertyIsEnumerable('custom'); //true a.propertyIsEnumerable('custom1'); //false
toString()
返回对象的字符串值。
valueOf()
返回对象的值,若是返回的值不是基本数据类型(数值,字符串,布尔值,null)则会继续调用 toString()方法返回对象的字符串值。
运算符就是运算符号,在ECMAScript中最神奇的就在于每一个运算符均可以适用于多种类型的数据,为了不程序运行错误,当一个运算符用做于一个不太适当的数据类型时,在运算以前,运算符会先隐式的进行数据类型转换。
null
或者是undefined
时,null
会被转换为0,undefined
为 NaN
。valueOf()
方法,若是返回的值不是一个基本类型,则继续调用 toString()
方法,最后进行转换,再自增自减操做。var str = 'a'; var str_num = '1.23'; var obj = {}; ++str; --str_num; obj++
一元加减运算符很是符合咱们对数学中的正号(+)与负号(-)的认识,只是在 ECMAScript中一元加减运算符除了能改变数值的正负之外,还会对不符合的数据类型进行数值型转换,其转换规则与调用静态方法 Number()
以及自增/自减运算符一致。
简介
在ECMAScript全部的数值都是以 IEEE754标准采用64位二进制进行存储的,可是“位”运算符并不会对64位二进制值进行操做,而是会将64位转换为32位整数再进行运算,最终再将结果转换回64位保存,因为存储和转换的过程对使用者而言都是透明的,因此使用者只能关注到进行运算的32位整数。
在这个32位整数中,有一位为符号位,剩下的31位才是数值位,符号位决定了这个数在计算机中对应二进制数的存储格式,其中0表示正,1表示负,同一个数,若是符号为正,只须要存储其对应的二进制原码,若是为负,便须要将其转换为补码形式进行存储计算。
例如 +8
原码为:
对于正数因为其原、反、补都是相同的,因此无需关注,而对于负数,则求其反码或者是补码会有些繁琐,主要通过如下步骤:
例如 -8
,求其补码为:
(1)- |8| = -1000 = 1...1000 (2) 取反:1...0001 (3) 加1:1...0010
因为ECMAScript的位运算符只能对32位整数进行运算,因此对于超出 2^31-1
的数值(符号位占1位),在运算的时候都会可能出现失去精度的状况。
~~2147483647 // 2147483647 ~~2147483648 // -2147483648 ~~2147483649 // -2147483647 ~~4294967296 // 0
非、与、或
非:~,二进制位(包括符号位)按位取反:~0...1001 => 1...0110
。eg:~9 => -10
。
与:&,二进制位同1位1,不一样为0。
或:|,二进制位有一个1就为1,只有同为0才会为0。
因为“位”运算符会将64位浮点数转换为32位整数,所以利用
~~
双按位反运算符即可以进行数值的快速取整,其功能相似于parseInt,只是这里进行的是位运算,速度更快。
异或
运算符:^
功能:二进制位不一样为1,相同为0.
示例:
101011100 ^ 010001100 ---------------- 111010000
左移位
运算符:<<
功能:将对应操做数的32位二进制数向左移动指定的位数,移动后,右侧空余的位置用0填充。
示例:
2 << 2 // 8 2 << 1 // 4 -2 << 5 // -64
须要注意的是“左移”并不会影响操做数的符号位,它移动的只是操做数的绝对值,可是结果符号的依然与操做数的符号相同。
-64 => -|-64| => -(|-64|<<2) => -256
若是用一组数学公式去计算左移的值的话,应该是:N*~~((2^s))
。其中 N
是要移动的操做数,而 s
则是要左移的位数。
-64 << 2 == -64 * ~~(Math.pow(2,2)); //true
有符号右移
运算符:>>
功能:将对应操做数的32位二进制数向右移动指定的位数,移动后左侧空余的位置用操做数的符号位填充。
64 >> 2 // 16 -64 >> 2 //-16
无符号右移位
运算符:>>>
功能:将对应操做数的32位二进制数向右移动指定的位数,移动后左侧空余的位置用0填充。
示例:
64 >>> 2 //16
若是操做数是正数,那么无符号右移动,与有符号右移彻底相同,可是若是操做数是负数,那么结果就会不一样了
-64 >>> 5 //67108863
出现这个问题的缘由,就是由于无符号右移运算符会把负数的二进制数再按照指定的位数右移后,却会以正数的二进制数形式转换为对应的十进制。从而致使结果很是大。
例如 -64
的补码是:1111 1111 1111 1111 1111 1111 1100 0000
右移后的结果是 0000 0011 1111 1111 1111 1111 1111 1111
可是在转换为十进制数的时候,并无减一取反,而是以正数的二进制数直接转换为10进制,因此结果为 67108863
非运算符
运算符:!
功能:“逻辑非”运算符会将操做数取反,最终返回一个布尔值。逻辑非在进行求反以前会调用 Boolean()
这个静态方法将非布尔型的值转换为布尔值,而后进行求反操做。
示例:
!NaN //true !undefined //true !null //true
利用逻辑取反运算符会返回布尔值的这一特性,经过使用双反运算来进行数据类型的转换。
!!NaN //false !!undefined //false !!null //false
与运算符
运算符:&&
功能:“逻辑与”是一个双目运算符,它能够连接多个操做数,而且操做数能够是任何的数据类型,并不必定是非要是布尔值,并且逻辑与运算符的运算结果也并不是必定是布尔值,而是知足条件的那个结果。
说明:逻辑与存在短路的状况,即只要第一个操做数不为“真(true)”,那么后面的条件就会被忽略。
false && a // false
这里变量 a
并未定义,可是再进行逻辑与运算的时候,并不会报错,这是由于在逻辑短路的状况下,后面的代码就不会被执行。
对于逻辑与而言,只有当第一个操做数的结果为真的时候,才会向下执行,最终返回符合条件的那个操做数的值,可是一旦遇到值为“假”的操做数,就会中断执行。
true && false && 1 // false NaN && 'true' //NaN true && 2 //2
或运算符
运算符:||
功能:“逻辑或”是一个双目运算符,它能够连接多个操做数,而且操做数能够是任何的数据类型,并不必定非要是布尔值,并且逻辑或运算符的运算结果也并不是必定是布尔值,而是知足条件的那个结果。
说明:逻辑或存在短路的状况,即只要第一个操做数为“真(true)”,那么后面的条件就会被忽略。
true || a //true
这里变量 a
并未定义,可是在进行逻或与运算的时候,并不会报错,这是由于再逻辑短路的状况下,后面的代码就不会被执行。
对于逻辑或而言,只有当第一个操做数的结果为“假”的时候,才会向下执行,最终返回符合条件的那个操做数的值,可是一旦遇到值为“真”的操做数,就会中断执行。
true || false //true false || true //true false || false //false
在算术运算符中,若是操做数的数据类型不一致,也会进行数据类型的隐式转换,只是不一样的运算符转换的规则也有必定的不一样:
乘/除/求余
乘/除/求余
操做,若是所得的结果超过ECMAScript可表示的范围,则返回Infinity
。NaN
,那么运算结果也将是 NaN
Number()
方法将其转换为数值型,而后进行乘/除/求余
操做。NaN
Infinity
。1*NaN //NaN 1/0 //Infinity 0/0 //NaN
加减
加
操做,若是所得的结果超过ECMAScript可表示的范围,则返回Infinity
。null
、undefined
、布尔值等其中任意一个,则调用Number()
静态方法将它们转换为对应的值后进行运算。NaN
进行 加
运算,结果依然为 NaN
。valueOf()
方法获取这个引用类型的基本值,若是值仍然不是一个基本数据类型,则再调用 toString()
方法。1 + 'a' //'1a' 'a' + 'b' //'ab' 1 + null //NaN 1 + true //2 1 + undefined //NaN 1 + NaN //NaN 1 + {} // "1[object Object]"
减法
减
操做,若是所得的结果超过 ECMAScript可表示的范围,则返回Infinity
。NaN
进行 减
运算,结果依然为 NaN
。null
、undefined
中的任意一种,在进行减法运算以前,都会先调用 Number()
方法将它们转换为对应的数值,最后再进行减法运算。valueOf()
方法获取这个引用类型的值,若是值仍然不是一个基本数据类型,则再调用 toString()
方法,接着再使用 Number()
方法将引用对象返回的值再转换为数值,最后再进行减法运算。1 - null //NaN 1 - undefind //NaN 1 - true //0 1 - NaN //NaN 1 - {} //NaN 'A' - 'b' //NaN
比较运算符的返回结果是一个布尔型(boolean)。
比较运算符有:> 、>= 、< 、<= 、!= 、!==、==、===。
比较运算符在计算的时候,也会根据值的不一样而不一样。
在同类型值的状况下:
true
大于 false
若是所比较的值类型并不相同,那么便须要进行必定的隐式转换以后方能进行比较:
NaN
。NaN
时,除了不相等运算符外任何比较运算符的结果都是 NaN
。null
、undefind
时,null
会转换为数值0,而 undefind
转换后为 NaN
false
转换为数值0,若是有 true
转换为数值1。valueOf
方法返回对象的值,若是返回的值依然不是一个基本数据类型,则再进一步调用 toString()
方法,最后再根据值的数据类型是否一致从而分别应用上述规则。若是对象进行相等性检测,则会判断是否具备相同的引用。var obj = { valueOf: function() { return 1; } }; console.log(2 > obj); //true console.log(null > 0) //false console.log(null >= 0) //true console.log(null < 0) //false console.log(null <= 0) //true
在比较运算符中,还有一个很是特殊的地方,那就是相等性与不相等性的比较运算符。
== //相等 != //不相等 === //严格相等(全等) !== //严格不相等(不全等)
“严格相等”、“严格不相等”与普通的“相等”运算符以及“不相等”运算符的最大区别在于前者在进行比较运算的时候,不会发生数据类型的隐式转换。
55 == '55' //true 55 === '55' //false
对于“严格相等”、“严格不相等”运算符实际上并无什么好说的,无非就是数据类型不会发生转换(使得使用时对结果的预测更加的准确),所以这里主要着重说明下普通的相等运算符与不相等运算符:
null == undefined
比较结果为 true
(注意在严格相等比较中,返回 false
由于它们并非相同的数据类型)。NaN
不相等 NaN
时,结果为 true
。NaN != NaN
。null
在进行相等运算时也不会进行类型转换:null == 0 //false
,这个须要注意下。true
或者返回 false
。null == undefined //true null === undefined //false NaN != NaN //true null == 0 //false var a = {}; var b = a; var c = []; a == b //true c == b //false
对于具体的开发,在进行相等性比较,强烈建议使用“严格相等”与“严格不相等”运算符,避免因数据类型的隐式转换而产生错误。
条件运算符是 ECMAScript中惟一的一个三目运算符
conditaion ? exp1 : exp2
须要注意的是一些不常见的复合赋值运算符
<<= 、>>= 、>>>=、%=
逗号运算符主要有两个做用:
var a,b,c;
var num = (1,2,3,4); // 4;
“表达式”一般指的是用运算符号将操做数连接起来可能会产生运算结果的式子。而“语句”则以功能或者行为为基本单位,用来完成某个给定的任务或者是操做,并常以分号为结尾。日常也称“语句”为指令或指令的集合。
知道“语句”的定义与概念,因此表达式加上分号 ;
也能够称之为表达式语句,再详细的划分有:
除了众多的表达式语句,在ECMAScript中使用关键字来定义的语句还有“条件语句”、“循环语句”、“函数语句”等。
在ECMAScript中组成循环语句的关键字是有:while
、for
、in
、do
等。
这些关键字构成的循环语句主要有如下类别:
while(){..} do{}while() for(){..} for..in
其中 for
循环是最经常使用到的,其详细格式以下:
for(init;condition;loop_express) statement
其中的 init
表示初始化一个变量,这个变量也能够在for
循环外定义,可是不管是外仍是内,由于 ECMAScript5以前都不存在块级做用域,因此循环体的上下文均可以访问到这个变量。
condition
是循环的条件,只有为 true
时才会进入循环体,而 loop_express
则是用于控制循环条件状态的变动。
for(var i=0;i<10;i++){ console.log(i++) }
在 for
循环中,init
与 loop_express
能够留空,在别的地方定义
var i = 0; for(;i<10;){ console.log(i++) }
固然,你也能够三个参数都不要,这样实际上就成为了一个死循环
for(;;){}
可是仍是强烈建议在定义 for
循环的时候参数都尽可能都写在循环之中。
在具体的使用上,init
不只能够用于初始化变量,还能够是一些列用逗号 ,
隔开来同时执行的表达式。
for(var i=0,j=10,m=20;i<20;i++){...}
除了 for
循环外,经常使用的就数 for..in
循环了,这二者的区别,for
循环能够更好的对数组进行遍历,而 for..in
则能够方便的枚举对象的成员。
格式:
label:statements
须要注意的是 label
并非关键字,而是代指“标识符”。
“标签语句”经常使用于与 break
或 continue
等关键字结合使用,在代码执行的时候跳转到程序的指定位置。
“标签语句”在具体的使用中,经常使用于多层循环体的内部循环,实现退出整个循环的功能(由于单纯的 break
只能退出当前循环,而没法在多层的循环内部直接中断全部循环)。
wrap: for(var i=0;i<10;i++){ for(var j=0;j<10;j++){ if(j==5 && i==5) break wrap; } }
将指定的对象加入到当前做用域链的源头,从而实现快速的访问对象中的某个成员。
with(location){ console.log(href) }
with
语句虽然能够提供对象成员的便捷访问,但也会致使歧义的产生,如在 with
语句体外定义了一个变量与当前对象的成员重名,便会使自定义的变量无效(当重名时,with语句优先级使用对象的同名方法或属性)。
var href = {}; with(location){ console.log(href); }
另外,大量使用 with
语句还会致使性能的降低,而且ES5标准已经规定严格模式不能使用 with
语句。
switch
语句与 if
语句关系很紧密,通常来讲,当须要判断的条件在三个以上的时候,推荐使用 swtich
语句,不然建议使用“条件语句”,下面是 switch
语句的具体格式:
switch(expression){ case value : statements; break; default: statements }
表达式(expression)与每种状况(case)的值(value)能够是任何数据类型的值,不像其它语言中只能是数值,在 ECMAScript中 表达式与 case
的值不只能够是固定的常量,还容许是一个变量:
var a = 'a'; switch("a"){ case 1: break; case a: break; default: }
case
除了数据类型能够不固定外,还能够是一个表达式,固然匹配的结果就是与表达式的值进行匹配。
var num = 20; switch(true){ case num < 0 : break; case num > 5 && num < 10 : break; case num == 10 :break ; default: }
switch语句内部对
expression
与case
的value
来进行判断与匹配时,都是以“全等”的方式来进行判断的,因此 switch中值的匹配时不会发生数据类型的转换。
switch(10){ case '10':break; defaut: }
最后还有一个很实用的技巧,若是一个 switch
语句定义在某一个函数的内部,那么可使用 return
来代替 break
,由于 return
在结束函数执行的过程当中也就中断了switch
语句的执行。
在ECMAScript中若是函数没有明确指定返回值,那么默认返回 undefined
。
函数参数
与其它语言同样,ECMAScript中函数的参数也有两种,分别是声明函数时用来接收值的“形参”以及调用函数时传输的“实参”。
继承至ECMAScript弱类型的特色,形参与变量的性质其实是相同的,能够接收任何类型的值,而且形参还不会像变量那样有做用域提高的状况。
相比与其它语言,ECMAScript中函数参数的传输,不是以单个单个参数的形式传输,而是整个以数组的形式进行传输,也就是说无需关注每次传输时参数的具体数量(形参与实参是否一致)。
关于这点,咱们能够在函数体中访问 arguments
对象便一目了然
arguments.length // 获取传入参数的数量 arguments[0]; arguments[1]; ...
arguments
虽然相似与数组,但并非真的数组,访问其原型链,能够发现继承至 object
。
arguments.__proto__
对于这些相似与数组,而又非数组的值,咱们能够很形象的称之为 Like Array
。
到了这里,咱们就能够明白,实际上函数的形参只是帮助咱们更好的理解与书写代码,而非必须要有的,在使用 arguments
修改某个参数的值,其对应的形参也会发生改变:
arguments[0] = 'test'; console.log(a)
但这并不能说明形参就是对 arguments
中某个元素对象的引用,实际上它们俩的内存空间彻底是独立的,只是它们的值会具备相互绑定的关系,从而实现相互更新值的特性。我我的认为这是由于 arguments
毕竟是一个对象,在函数体中频繁的访问或修改对象的成员,对于性能的影响也是不可忽略的。
经过 arguments
除了能够访问函数的参数,获取参数的数量,它还有一个很是有用的方法 callee
,它与 this
相似能够对自身进行引用,所以经常使用这个方法实现函数的迭代执行。
function factorial(num){ if (num <= 1) return 1; else return num * arguments.callee(num - 1); };
须要注意的是ECMAScript严格模式下
callee
将没法使用。