来感受一下可能你未曾在意过的操作符优先级

在前端的日常开发工作中,除了 cv 大法外,最常用的应该就是对于后端数据根据业务需要进行处理了,处理中充斥着各种操作符,比如三元(?...:...)(4),逻辑与或(&& ||)(7/6),计算(+-*/)(14/15)...

当一个处理过于复杂,需要许多运算组合起来时,常规开发者会基于易读性而选择分段写;非要写在一行的时候,也会尽量选择用括号(21)来区分每一个部分。这除了增加易读性以外,最大的作用就是防止js编译时操作符的处理顺序与我们想要的有冲突,也就是运算符优先级,一个你可能未曾在意过的小知识(比如上面括号中的数字就是运算符对应的优先级)。

下面让我们来感受下操作符优先级的有趣之处

在 js 规范中,包括当前最新的可选链(?.),目前的运算符优先级已有22级(0-21),以下我将重点聊聊常用的运算符以及关于他们的有意思的问题。

1 运算语句是怎么执行的

对于每个 statement ,可以将其中的元素分为两类,操作对象与操作符。当扫描执行每个 statement 时,会从左至右依次将操作对象(o)以及操作符(c)分别放入两个栈中,简称为对象栈(s1)和操作栈(s2),并且遵循以下规则:

  • 1 如果遇到 o ,则将 o 放入 s1 ;
  • 2 如果遇到 c ,则需要和 s2 栈顶的 c2 进行优先级比较:
    • 2.1 如果 c > c2 ,则将 c 放入 s2 ;
    • 2.2 如果 c <= c2 ,则将 c2 与 s1 栈顶的两个元素一同退栈,计算结束后,将结果放入 s1 ,c 放入 s2(此动作相当于再次执行步骤2);

例1: a = 1 + 2 * (3 - 4) / 5;

执行顺序
  • 有很多操作符有着自己的特性,比如上面的 () ,虽然 - 的优先级小于 () ,但是当 - 入栈时并没有直接运算优先级更高的 ()
    这是 ( 的特性:在他之后的操作符会直接入栈,不做判断,用以实现 () 本来想要实现的功能。
    像这样有自己特性的操作符还有很多,并不是所有操作符都会完全遵循以上规范。

2 最高的优先级与最低的优先级是谁

优先级最高的操作符是谁,显而易见,是不清楚优先级时就会用到的括号 () ,优先级高达21

例2.1:(1 + 2) * 3 ==> 3 * 3 ==> 9

高达21的优先级,显然不止是为了在四则运算这种简单的场景下来使用,而是为了对于高优先级的操作符也能起到作用。

例2.2:new (ClassA || ClassB).toString() ==> new ClassA.toString()

  • 在超高优先级的 new 与 . 操作符下,也能使低优先级的 || 先执行

而对应的,低达0优先级的操作符,则是最不起眼的逗号 , ,作为用来间隔语句的操作符,你不会希望他还会误入你的处理中去的。

3 一人之下万人之上的操作符以及关联性

除去括号外,还有一批优先级为20的操作符,其中最重要的当属成员访问a.b , a['b'], a?.b)以及函数调用func(a, b))。

他们都是与两个或多个操作对象参与计算的,又该怎么决定多个操作对象是如何围绕操作符执行的呢?
这就要引申出操作符与操作对象怎样执行的概念——关联性了。

  • 无关联('()', '++', '--', '...'):此类操作符不会对左右内容进行关联,只会执行完自身后返回结果。

  • 从左至右(成员访问,函数调用等):此类操作符会从自身的左侧开始计算。

  • 从右至左(各类赋值等):与从左至右相反。

4 一符多用的操作符

并不是每个操作符的优先级是唯一的,有些操作符会因使用的场景而导致优先级发生变化。

4.1 new

关键字 new 是大家很熟悉的一个了,不仅是面向对象的原因,还有那道最常见的面试题(关键字 new 分别做了几件事),然而 new 的优先级却有两个(20/19),并且其原因与某个重要的操作符相关。

回到上面的例2.2: new ClassA.toString() ,他的下一步将先执行哪个呢('new' or '.' or '()')?
最后的顺序当然是先指向 ClassA.toString ,然后创建这个类函数的实例。

虽然上述的顺序和我们预期的一样,但三个操作符的优先级都是20,按照关联性应该是新建 ClassA 的实例,然后调用实例上的 toString 方法才对,为什么会先执行成员访问呢?

因为 js 规范分别为 new 设计了两种优先级:

  • new(带参数列表):new ... (...),优先级为20

  • new(无参数列表): new ...,优先级为19

以此方法来规避 new 和成员访问造成的各种冲突

4.2 递增&递减

递增递减操作大家很熟悉,并且其分为前置后置两种,但是这两种的优先级又是多少,谁高谁低呢?
可能根据以下例子观察:

var a = 1;
console.log(1 + ++a); // 输出3

var b = 1;
console.log(1 + b++); // 输出2

前置的优先级貌似更高,但是实际上却是后置优先级18,前置与逻辑非('!'), typeof 等操作符一样优先级17,如何证明呢?
由于递增递减只能作用在变量上,所以可以利用 ++a++ 的报错来判断他们的优先级,由下图可见是后置优先级更高。

++a++报前置操作符错误

可是为什么在运算中,明明后置优先级那么高,但是运算时却没有体现出来呢?
原来是因为后置时,操作符将 "a++" 替换成 a 原来的值,而不再是 a 的引用,然后对 a 进行递增。

5 逻辑运算

在数据处理中躲不掉的一定是逻辑运算符,当你厌倦了整篇的 if-elsewhileswitch 时,他就是条件语句的替代,但是逻辑运算的优先级也有不同。

17: 逻辑非(!

12: 小于(<),小于等于(<=),大于(>),大于等于(>=

11: 等于(==),不等于(!=),全等于(===),不全等于(!==

7: 逻辑与(&&

6: 逻辑或(||

4: 条件语句(... ? ... : ...

知道这些操作符的优先级后,你应该能轻松的答出以下语句的输出结果了(数字计算的操作符优先级在14-16之间)。

// 例5
alert(1 + 2 === 3 ? 4 && 0 || 5 : 0 === 6); // 输出?

6 关于优先级的有趣题目

6.1 请写出下列输出结果

    var a = {n: 1};
    var b = a;
    a.x = a = {n: 2};
    console.log(a); // ?
    console.log(b); // ?
    console.log(a.x);  // ?
  • 如果第三行换成 a = a.x = {n: 2}; 答案又是什么呢?

6.2 请写出下列输出结果

    function Foo() {
        getName = function () {
            console.log(1);
        };
        return this;
    }
    Foo.getName = function () {
        console.log(2);
    };
    Foo.prototype.getName = function () {
        console.log(3);
    };
    var getName = function () {
        console.log(4);
    };
    function getName() {
        console.log(5);
    }

    Foo.getName(); // ?
    getName(); // ?
    Foo().getName(); // ?
    getName(); // ?
    new Foo.getName(); // ?
    new Foo().getName(); // ?
    new new Foo().getName(); // ?

小结

操作符是编程语言极重要组成部分,了解操作符不意味着一定要运用其中的一些规则去完成自我认同的奇技淫巧。
了解它,你也许会在复杂的逻辑判断时更好地组织语句,也许会在遇到奇怪的语法错误时准确地抓住重点,无论如何,感谢你能读到这里。

备注

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容