19. 运算符 加法运算符+: 1. 若运算子是对象,则自动转成原始类型值;此过程中先执行该对象的valueOf方法,若仍然 不是原始数据类型值,则再执行toString方法,若对象是Date实例,则执行toString方法。 2. 两个运算子都是原始类型的数据后,只要有一个为字符串,则运算子会转为字符串后执行字符串 的连接操作。 3. 否则两个运算均转为数值,执行一般的加法运算。 4. 此外还有多个运算子的加法顺序,有括号先计算括号,从左向右,如:'3'+1+5与3+1+'5'得到的结果前者为'315'后者为'45'。 5. 除了加法运算符,其他的减法、乘法、除法等不会同以上1~2点,其规则为所有运算子一律转为数值,再进行相应的数学运算,如'3'-1结果为2。 若出现加法和其他运算的复合运算,则满足加法、其他运算和运算顺序规则即可。如'3'+2-'8'结果为24。 余数运算符%: 若第一个或第二个运算子其中之一为负数,则得到的结果均以第一个的正负号作为运算结果的正负号。 余数运算、自增、自减、、符号运算符(数值前面的+/-)、运算子均不可为字符或其他的数据类型,只能为数值(包括浮点数,或可转为数值的类型如boolean)。 比较运算符: ==、===、!=、!==、<、<=、>、>=; 1. 若两个运算子均为字符串,则按照字典顺序比较(unicode码比较(包括汉字等)); 2. 若有一个字符串且字符串为数字,则将数字字符串转为数值(如同调用Number转换后),再比较; 3. 若有一个字符串且字符串不为数字字符串,则会将该非数字字符串转为NaN再比较,此时结果均始终为false。 4. 一些补充,除了==、===外,在其他的比较运算符在两个运算子均为原始数据类型时均转为数值后再比较; 若运算子之一为对象,则对象调用valueOf后转为原始数据类型值(若转化后还是对象则再调用toString转化字符串)再比较; 而对于Date对象,则相反,是先调用toString方法后,若仍然不是原始数据类型,则再调用valueOf方法再比较。 ==与===之间,前者仅仅比较值是否相等(可能出现转化为同一类型再比较),而后者若类型不一致则直接返回false; 此外对于==,该比较运算符依赖于valueOf或toString方法,此可能导致一些难以预期的结果(依赖的方法被覆写时或者因某些隐式转换); 对于===,因比较时,对于基本的数据类型,则可以直接比较值,而对于对象等复合数据类型,则是比较引用的对象是否是同一个(即同一个地址), 此与大小比较运算符不同,其比较的是数据值; 此时可能有这种情况:[]===[]返回false(此时===两边均为不同的对象);而var a1 = [];var a2=a1;a1===a2;则会返回true; 多数情况下,对象与undefined和null比较,都返回false。只有在对象转为原始值得到undefined时,才会返回true。 null==undefined返回true;null===undefined返回false、0==''返回true而''=='0'返回false; 取反运算符!: 取反运算符会转为布尔运算结果,对于undefined、null、false、+0、-0、NaN、字符串''取反后均为true。其他类型的数据取反后基本上均为false; 且或运算的两个运算子,以及三目运算符的条件、还有条件判断if与while等均会被先自动转为布尔运算结果后再执行运算。 位运算符: 因JavaScript采用64位浮点存储,在对位运算操作时候均转为整型数值(32位有符号)后再执行位操作运算。 a | 0 结果会转为整型数值,故可以借此实现类似于toInt32的函数功能, 此外~~a的两次位否运算也可以实现该功能而且更快,异或运算也可取整如a^0; 左右移位运算也可取整,如12.4>>0或12.4<<0。 对于浮点的小数,则会舍去小数部分(不是四舍五入)。 否运算符~: 否运算对特殊数值的处理是:超出32位的整数将会被截去超出的位数,NaN和Infinity转为0。 对于其他类型的参数,否运算也是先用Number转为数值,然后再进行处理,如~~[]、~~NaN、~~null、~~undefined均为0。 异或运算符^: 异或运算可实现最快的交换两个变量的值。 右移运算符>>与>>>: 右移运算符>>对于负数,则严格按照二进制形式时的移位,此时保留符号位不变-4>>2结果为-1。 而带符号位的>>>,则对于负数,则严格按照二进制形式时的移位,此时符号位填充为0,即结果始终为非负数,如-4>>>2结果为1073741823, 此类似于将一个数转为32位的无符号整数即:a>>>0。 void运算符: 一般用于执行一些表达式,而不需返回值或返回undefined;一般用在包装执行表达式,而屏蔽执行结果的返回值; void(some_expression);此时some_expression可能有其他返回值,但被void包装后,整个语句将返回undefined。 在屏蔽网页跳转、返回值等场合比较有用。 20. 数据类型相互转换 JavaScirpt动态类型语言,变量没有类型声明限制,虽然如此,但数据本身、某些运算符是有类型的,很多操作会出现自动类型转换。 JavaScript中也分为自动和强制类型转换,强制类型转换主要的构造函数有:Number、Boolean、String; Number:对不能转为数值类型的字符串("fef",'23px')、undefined、对象(除了单个内容的数组,即[2]、[]与[2,3]前者可以,后者[2,3]返回NaN)均转为NaN, 其他可转为数值类型的字符串("76",'')、null、boolean类型的均可转换成功。 Number与parseInt内置函数有区别,后者对"23px"可得到23,而前者为NaN,且均自动剔除前后的空格字符( v 等)。 Number对于对象时的转换规则: 1. 调用对象自身的valueOf方法。如果返回原始类型的值,则直接对该值使用Number函数,不再进行后续操作。 2. 如果valueOf方法返回的还是对象,则改为调用对象自身的toString方法; 如果toString方法返回原始类型的值,则对该值使用Number函数,不再进行后续操作。 3. 如果toString方法返回的是对象,此时变回报错。 String的转换规则: 1. 数值转字符串,字符串转本身,布尔值转为'true'或'false',undefined转为'undefined',null转为'null'; 2. 对于对象时,先调用自身的toString方法。如果返回原始类型的值,则对该值使用String函数,不再进行后续操作; 3. 如果toString方法返回的是对象,再调用原对象的valueOf方法。如果valueOf方法返回原始类型的值, 则对该值使用String函数,不再进行后续操作; 4. 如果valueOf方法返回的仍然是对象,就报错。 有个问题:String({a: 1}.toString())得到"[object Object]",而{a: 1}.toString()则报错,({a: 1}).toString()却可以? 原因:表达式语句不能以function(立即声明即执行)和{开头。 Boolean的转换规则: 1. undefined、null、0、+/-0、NaN、''空字符串、false均转为false;其他均为true; 2. 所有对象包括空对象,如{},[],甚至Boolean(new Boolean(false))等均为true(对于对象的处理方式是出于性能考虑); 数据类型的自动转换,因是隐式、不确定的,故而可能出现一些不预期的结果,故对于不确定的地方建议使用强制转换。 自动转Boolean的地方一般有条件语言判断,一些运算符操作; 自动转字符串的地方,如:'45'+33,'ef'+{}等。 自动转为数值的地方,如:'45'-'4',true+1,undefined + 10(将得到NaN), null+10(将得到10),'5'*[](将得到0),'5'*{}(将得到NaN)等。 21. 错误处理 JavaScript提供了一般的Error错误对象,该对象构造函数内容即为错误提示信息,通过Error.message属性获取,部分实现可能有name、stack或其他的属性; JavaScript也提供了try/catch/throw机制来抛出、捕获异常/错误,throw可以抛出基本类型对象、Error对象和自定义对象。 catch捕获异常,只能在内部通过条件判断不同的类型异常,而不是多个catch并列的语句,此与c++、python不同;同样的也支持再次抛出或嵌套try/catch语句。 JavaScript也有finally块,可执行正常或异常的try/catch的收尾工作。 JavaScript在Error基础上还提供了一些内置的错误类型; 如:SyntaxError、ReferenceError、RangeError、TypeError、URIError、EvalError等。 可自定义错误对象,一般是继承Error。 22. 一些风格问题 1. {}风格: block { // some codes; } 以避免被解释器自动添加句末分号导致出现一些难以察觉的问题; 如: return { key: value }; 可能被当做: return; { key: value }; 2. 建议不要省略行尾的分号,部分地方省略分号可能导致错误,除此之外某些像if/while/for等语句块外一般不需要分号; 3. 尽量避免使用全局变量,全局变量可能引入变量冲突混乱以及对代码的模块化、重复使用会有限制影响的。 4. 变量声明会被提升的,故一般建议变量放在函数或代码块的头部,避免不必要的问题。 5. new关键字,若没有new,则创建的对象时是全局变量的,也即该对象内部的this会指向全局对象,导致绑定在this对象的变量变成了全局的变量, 而不是创建对象或调用对象的;故也可使用Object.create()方式来创建。 6. with语句,虽然可减少代码的书写,但可能造成代码块内部变量混淆,不建议使用。 7. ==与===,因==的自动转换或其他的可能导致的意想不到的问题,一般建议用===。 8. switch/if等语句,尽可能用{}包裹,以避免出现错误。