• JavaScript运算符与类型


    1.运算符优先级

    首先讲一下运算符的优先级,它决定了表达式中运算执行的先后顺序,优先级高的运算符最先被执行。

    下面的表将所有运算符按照优先级的不同从高到低排列:

    优先级运算类型关联性运算符
    19 圆括号 n/a ( … )
    18 成员访问 从左到右 … . …
    需计算的成员访问 从左到右 … [ … ]
    new (带参数列表) n/a new … ( … )
    17 函数调用 从左到右 … ( … )
    new (无参数列表) 从右到左 new …
    16 后置递增(运算符在后) n/a … ++
    后置递减(运算符在后) n/a … --
    15 逻辑非 从右到左 ! …
    按位非 从右到左 ~ …
    一元加法 从右到左 + …
    一元减法 从右到左 - …
    前置递增 从右到左 ++ …
    前置递减 从右到左 -- …
    typeof 从右到左 typeof …
    void 从右到左 void …
    delete 从右到左 delete …
    14 乘法 从左到右 … * …
    除法 从左到右 … / …
    取模 从左到右 … % …
    13 加法 从左到右 … + …
    减法 从左到右 … - …
    12 按位左移 从左到右 … << …
    按位右移 从左到右 … >> …
    无符号右移 从左到右 … >>> …
    11 小于 从左到右 … < …
    小于等于 从左到右 … <= …
    大于 从左到右 … > …
    大于等于 从左到右 … >= …
    in 从左到右 … in …
    instanceof 从左到右 … instanceof …
    10 等号 从左到右 … == …
    非等号 从左到右 … != …
    全等号 从左到右 … === …
    非全等号 从左到右 … !== …
    9 按位与 从左到右 … & …
    8 按位异或 从左到右 … ^ …
    7 按位或 从左到右 … | …
    6 逻辑与 从左到右 … && …
    5 逻辑或 从左到右 … || …
    4 条件运算符 从右到左 … ? … : …
    3 赋值 从右到左 … = …
    … += …
    … -= …
    … *= …
    … /= …
    … %= …
    … <<= …
    … >>= …
    … >>>= …
    … &= …
    … ^= …
    … |= …
    2 yield 从右到左 yield …
    yield* 从右到左 yield* …
    1 展开运算符 n/a ... …
    0 逗号 从左到右 … , …

    2.运算符的结合性

    结合性决定了拥有相同优先级的运算符的执行顺序。

    3.类型

    JavaScript是一种无类型语言(更为精确的说,是一种松散类型,动态类型的语言)。这就是说,再声明变量时无需指定数据类型,这使JavaScript具有灵活性和简单性。在代码执行过程中,JavaScript会根据需要自动进行类型转换。例如,如果传递给方法document.write()的是一个数字,JavaScript会自动将其转换成与之等价的字符串表示。

    javascript类型主要包括了primitive和object类型,其中primitive类型包括了:null、undefined、boolean、number、string和symbol(es6)。

    说到类型检测主要包括了:typeof、instanceof和Object.prototype.toString.call(xxx)或{}.prototype.toString.call(xxx)。类型判断

    typeof一般适用于判断primitive类型的数据,在判断object类型的数据时有时会有意想不到的结果,例如:typeof null结果为object。下面的表是typeof元素符的一个结果:

    val 类型结果
    Undefined “undefined”
    Null “object”
    Boolean “boolean”
    Number “number”
    String “string”
    Object(原生,且没有实现 [[Call]]) “object”
    Object(原生或者宿主且实现了 [[Call]]) “function”
    Object(宿主且没实现 [[Call]]) 由实现定义,但不能是 “undefined”、”boolean”、”number” 或 “string”。

    instanceof运算符是用于判断一个实例是否属于某一类型,例如:a instanceof Person,其内部原理实际上是判断Person.prototype是否在a实例的原型链中

    Object.prototype.toString.call(xxx)或{}.prototype.toString.call(xxx),使用Object.prototype.toString会节省创建一个对象。

    4.类型转换

    JavaScript的数据类型有两类:原始类型(数字、字符串、布尔值)和对象类型,还有null、undefined(两个特殊的原始值)。

    类型转换原则

    • 空数组在比较中转换成0;一个元素的数组a转换为a[0],若a[0]为数值或纯数字字符串(不包含布尔值)按数值比较原则,非纯数字字符串与布尔值转化为字符串;多个元素,数组会将方括号内的内容转换为字符串如[1,2] -> '1,2',不能比较,所以返回false。
    • 大小比较:不能参与比较的比较结果都是false,而不是报错;参与比较的纯数字字符串都会转换为数值型;布尔值与数值、布尔值与字符串(非纯数字字符串不能转换,返回false)、字符串与数值(同上)、字符串与字符串(不进行转换,即使是纯数字字符)。
    • 相等性比较:布尔值:true 等于(==)其他形式的 1,这包含‘1’、1、[1]、['1'];false 等于其他形式的 0 ,包含‘0’、0、['0']、[0];数值与对应的字符串是相等的。
    • 四则运算:'+'运算符:布尔+数值、布尔+布尔、数值+数值会转换为数值;有一个字符串,另一个会转换为字符串;其他类型,不能转换为数值的都会转为字符串。其他运算符:参与运算的非数值类型都会试图转换为为数值类型进行运算,但只有布尔类型、纯数字字符串与1个元素(该元素必须是数值型或纯数字字符串,不能是布尔型)或者符合数组转换原则的数组可以实现转换。不能实现转换的参与运算后等到数值型的NaN(用函数isNaN()检测)。
    • 条件判断:if条件语句,while 循环语句中判断的‘假’包含”0、''(空字符串)、null、undefined、false。
    • 逻辑运算:逻辑运算遵循了条件判断的原则,此处应注意取值方法:"||"运算:最基本的当然是两个操作数一真一假,必然返回真的操作数的值。如果判断第一个操作数为真,则不进行第二个操作数的运算直接返回第一操作数的值。如果两个都为假返回的是第二个操作数的值;"&&"运算:最基本的当然是两个操作数一真一假,必然返回假的操作数的值。如果第一个操作数的值为假,则不进行第二个操作数的运算直接返回第一个操作数的值。如果两个都为真则返回第二个操作数的值。"!"运算:返回值必然为布尔类型,true 或者 false。

    (1)隐形转换

    "0" == 0 //true 在比较之前字符串转换成数字 
    0 == false //true 在比较之前布尔值转换成数字  
    "0" == false //true 在比较之前字符串和布尔值都转换成数字

    (2)显性转换

    使用Boolean()、Number()、String()或Object()函数。

    (3)对象转换为原始值

    对象到布尔值:所有的对象(包括数组和函数)都转换为true。对于包装对象亦是如此:new Boolean(false)是一个对象而不是原始值,它将转换为true。

    JavaScript中对象到字符串的转换经过了如下这些步骤:

    • 如果对象具有toString()方法,则调用这个方法。如果它返回一个原始值,JavaScript将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。需要注意的是,原始值到字符串的转换在表3-2中已经有了详尽的说明。 
    • 如果对象没有toString()方法,或者这个方法并不返回一个原始值,那么JavaScript会调用valueOf()方法。如果存在这个方法,则JavaScript调用它。如果返回值是原始值,JavaScript将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。 
    • 否则,JavaScript无法从toString()或valueOf()获得一个原始值,因此这时它将抛出一个类型错误异常。

    在对象到数字的转换过程中,JavaScript做了同样的事情,只是它会首先尝试使用valueOf()方法:

    • 如果对象具有valueOf()方法,后者返回一个原始值,则JavaScript将这个原始值转换为数字(如果需要的话)并返回这个数字。 
    • 否则,如果对象具有toString()方法,后者返回一个原始值,则JavaScript将其转换并返回。 
    • 否则,JavaScript抛出一个类型错误异常。

    (4)加法运算符会触发三种类型转换:将值转换为原始值,转换为数字,转换为字符串,这刚好对应了JavaScript引擎内部的三种抽象操作:ToPrimitive(),ToNumber(),ToString()

    http://es5.github.io/#x9.1

    ToNumber()是如何将原始值转换成数字的:

    参数 结果
    undefined NaN
    null +0
    布尔值 true被转换为1,false转换为+0
    数字 无需转换
    字符串 由字符串解析为数字.例如,"324"被转换为324

    ToString()是如何将原始值转换成字符串的:

    参数 结果
    undefined "undefined"
    null "null"
    布尔值 "true"  或者 "false"
    数字 数字作为字符串,比如. "1.765"
    字符串 无需转换

    5.示例

    运行一下代码:

    (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]

    结果为:"sb"

    这里涉及到运算及的优先级以及类型转换。

    将上述代码拆分,得到:

    运算符用红色标出,其实中括号[]也是一个运算符,用来通过索引访问数组项,另外也可以访问字符串的子字符,而且中括号的优先级还是最高的。

    什么情况下需要进行类型转化。当操作符两边的操作数类型不一致或者不是基本类型(也叫原始类型)时,需要进行类型转化:

    • 减号-,乘号*,肯定是进行数学运算,所以操作数需转化为number类型。
    • 加号+,可能是字符串拼接,也可能是数学运算,所以可能会转化为number或string
    • 一元运算,如+[],只有一个操作数的,转化为number类型

    执行代码:

    子表达式16:

    +[]    //输出0

    只有一个操作数[],肯定是转化为number了。[]是个数组,object类型,即对象。所以得先调用toPrimitive转化为原始类型。首先调用数组的valueOf方法,会返回自身;接下来调用数组的toString()方法,返回一个空字符串"";在调用引擎方法toNumber(),返回0。所以结果就是0。

    子表达式15:

    [~+""]    //结果为[-1]

    空串""前面有两个一元操作符,但是操作数还是只有一个,所以,最终要转化为的类型是number。根据结合性,先计算+"",调用引擎方法toNumber(),结果为0;接下来是~,它是位运算符,作用可以记为把数字取负然后减一,所以~0就是-1 。加上中括号,所以结果为[-1]。

    子表达式13:

    --[~+""][+[]]     //根据表达式15、16,结果为--[-1][0]

    根据优先级,--[-1][0],取数组的第0个元素,然后自减,结果为-2。

    子表达式14:

    [~+[]]    //根据表达式15、16,结果为[-1]

    子表达式9:

    此刻它已变成:-2*[-1]。

    运算符是乘号*,都得转化为number。先将[-1]对象转化为原始类型"-1",再通过引擎方法toNumber()转为Number为-1。

    所以结果为2.

    子表达式10:

    ~~!+[]   //从右往左计算,结果为1

    +[]为0,所以!+[]就为true,一元运算符需要转化为number,所以~true等于-2,~-2等于1,。所以结果为1。

    子表达式4:

    结合子表达式9和10,结果为3。

    表达式7:

    !(~+[])     //有前面表达式得出结果为!-1

    感叹号会把表达式转化为布尔类型,转化规则和js的Truthy和Falsy原则是一样的,后面跟数字的,除0以外都为true,后面跟字符串的,除空串以外都为true。这里的!-1当然就是false了。

    表达式3:

    false+{}。一个布尔加一个对象,那这个{}应该先转化为原始类型。调用引擎方法ToPrimitive(),结果为"[object Object]"。false与"[object Object]"相加,false先转化为字符串"false",相加得结果"false[object Object]"。

    表达式1:

    此时它是这样的:"false[object Object]"[3],因为这个[]可以取字符串的子字符,所以得到了结果"s"。

    表达式11:

    [~!+[]]    //结果为[-2],见表达式10

    表达式12:

    ~+[]    //结果为-1

    表达式6:表达式11和12 相乘,结果为2.

    表达式5:

    ({}+[])   //结果为"[object Object]"

    {}和[]都是对象,调用引擎方法ToPrimitive(),{}转化为原始值为"[object Object]",[]转化为原始值为""。所以结果为"[object Object]"。

    子表达式2:

    得到表达式"[object Object]"[2],所以结果为"b"。

    问题:

    为什么表达式{}+[]和表达式({}+[])结果不一样呢?

    是因为在console中,如果你不用括号包起来的话,不认为你是在进行运算。此时{}会被解析为上一个语句的结束标记。所以{}+[]就相当于是+[]。

    []+{}    //"[object Object]"
    {}+0    //0
    ({}+0)  //"[object Object]0" 
    0+{}    //"0[object Object]"

    示例:

    ++[[]][+[]]+[+[]]
    ++[0][0]+[0]
    1+[0]
    "10"

    示例:

    [] + []   //""
    var arr = [];arr.valueOf() === arr   //true
    String({})   //"[object Object]"
    6 + { valueOf: function () { return 2 } }   //8
    "abc" + { toString: function () { return "def" } }   //abcdef
    {} + {}  //NaN   一元运算符需要转为number
    ({} + {})  //"[object Object][object Object]"

    示例

    [] == ![]     //true

    首先看右边的![],空数组转换为boolean是true的,再进行!,可以知道右边的![]false,当一个对象和boolean进行equal时,[]会进行ToPrimitive,这里就会首先调用Array.prototype.valueOf,调用后返回的是[],不是原始类型,再进行Array.prototype.toString,这里返回了""空字符,空字符和false进行相等比较这里就是true了。

    运算符优先级

    运算符优先级 (JavaScript)

    一行神奇的javascript代码

    [译]JavaScript中,{}+{}等于多少?

    JavaScript 类型转换

    javascript 类型与类型转换

  • 相关阅读:
    2014上半年-学习目录
    c++中智能输出文件
    如何在微博侧栏中加入自己的微博[js]
    oracle数据库性能
    Arcgis for Android 空间数据WKT与JSON描述
    echart 折线图、柱状图、饼图、环形图颜色修改
    Echarts横坐标倾斜,顶部显示数字
    解决svn中“工作副本已经锁定”,或者svn清理失败的解决方法
    Oracle 空间查询, 数据类型为 sdo_geometry
    OSS上无法使用字体文件解决方案
  • 原文地址:https://www.cnblogs.com/Chen-XiaoJun/p/6697385.html
Copyright © 2020-2023  润新知