JS基础回顾-运算符1

在不更,基本也就告别简书了.
提出几个案例,咱们从后往前推,看看一个小玩意到底牵扯了多少知识点

1. a+++a;
2. a++ + ++a;
3. a+b++;
4. +!!a++;
5. a?b:c?d:e?f:g;
6. [ ] == ![ ]
7. a?b:c === a&&b||c

一、优先级 => 关联性(结合性) =>运算顺序

1、优先级

我们在上数学课的时候,经常会听老师叨叨:'先算乘除,再算加减,遇到小括号先算括号里面的'.这个就是典型的运算优先级,小括号>乘除>加减,js中也是一样,

var a = 2*(2+3);
2、关联性(结合性)

当一条表达式的运算符优先级都是同级时,我们就需要考虑此类运算符的关联性,也就是说遇到多个同级操作符相连接的表达式时,我们是从左开始算还是从右开始算由关联性决定

var a = 2*3/8*9; //从左向右运算
var b = !+!!a; //从右向左运算
3、运算顺序

JS运算顺序永远都是从上到下,从左到右


运算符.jpg

总结一个运算口诀就是‘先算谁(优先级)’,‘怎么算(关联性)’,
测验:

var a = 2*(2+3)/+![0][0]++;//能否口算结果?
拆分运算步骤
//圆括号优先级19,最高,得出
a = 2*5/+![0][0]++;
//属性访问优先级18,仅次于圆括号,得出
a = 2*5/+!0++; //注意,此行代码直接运行会报错,原因++操作符只能操作左值,此处只为展示
//++运算符优先级16,得出
a = 2*5/+!0;
//一元操作符优先级15,
//并且+与!都为同一优先级,开始考虑关联性,一元操作符的关联性为从右到左运算,得出
a = 2*5/+true; a = 2*5/1;
//二元操作符*,/优先级14,
//并且*与/都为同一优先级,开始考虑关联性,二元操作符的关联性为从左到右运算,得出
a = 10/1 ; a = 10;
//最后为 = 赋值运算符,它的优先级只比,高,
//所以 10 被赋值给了 a(栈内存保存数据),表达式运算结束

注:由于JS运算顺序永远都是从上到下,从左到右,所以不重点强调。

经过这个貌似变态的例子的解析过程,我们终于可以高呼“so easy!”

由此,我们可以解决开始提出的1 - 5,和7的问题了。我在这里就不占用太多篇幅了。

//第5题可以这样改写,容易理解
a?b:c?d:e?f:g ==> a?b:(c?d:(e?f:g))

第7题: a?b:c 与a&&b||c的运算原理是不是一致得?

答:不是 。很多人以为三元运算符(也称三目运算符)就是 xx&&xx||xx的一个变体,然而并不是这样。以题目例子为例,我们可以这样写(a&&b)||c ,
  1. 如果a和b都为真,那么a&&b就返回 b,在运算b||c,b是真,
    b||c 肯定就返回了 b;
  2. 如果a为假,b为真,那么a&&b就返回 false,在运算false||c,此时无论c为真或假,c的值都会被返回。
此时看着貌似和三目运算符是一样的对吧?再看第三条
  1. 如果a为真,b为假,那么a&&b就返回 false,在运算false||c,此时无论c为真或假,c的值都会被返回。
这就不对了,我们看一段代码:

所以,再有人跟你写这种奇葩格式,你可以狠狠地回绝他。

可能会遇到的困惑:
为什么a+++++a会报错,而a+++a就不会呢?
答:我也不知道,可能规则如此

二、JS中 等值运算符(==) 涉及到的隐式类型转换

1、先来看对象如何转换成基础数据类型
var a = {
    toString:function(){
        console.log('执行了toString方法');
        return this;
    },
    valueOf:function(){
        console.log('执行了valueOf方法');
        return this;
    }
}

我们创建了一个对象a,再对这个a的toString和valueOf方法进行了重写,这样做的目的就是要验证一下,一个对象在转化为基础数据类型的时候JS解释器是如何帮我们转化的.

var b = String(a);
图S

先解释一下为什么报错,在把对象转化为基础数据类型时,js解释器会调用此对象的toString方法或者valueOf方法,总之就是要把对象转化成一个基础数据类型再进行转化,但是由于我们重写了这两个方法,返回的值并不是一个基础数据,js解释器就会报出异常:不能将对象转换为原始值(原始值就是基础数据);

var b =Number(a);

我们可以借此规律去验证一些平时使用的方法的数据转换规律

alert(a);
由此,我们得出结论:
把一个对象转化为字符串时,会先调用对象的toString方法,如果无法得到原始值,再调用valueOf方法,如果还得不到原始值,则报错
把一个对象转化为数字时,会先调用对象的valueOf方法,如果无法得到原始值,再调用toString方法,如果还得不到原始值,则报错
alert()方法是把参数的数据转化成字符串,然后再弹框显示的

补充:如果是单纯的把对象转换成原始值,所有对象都会先调用valueOf方法,再调用toString方法。

说到alert,我想起了两个面试题
① alert( 1,2,3,4,5)

② alert((1,2,3,4,5))
第一个答案是1,虽然alert是浏览器实现的ui方法,但是本质还是一个函数.而且这个函数只有一个参数,所以得到1是自然而然的事.
第二个答案是5,这个有点迷惑性,分清两个括号,期中alert()为函数调用表达式,(1,2,3,4,5)为一个单独的表达式,拆分一下,相当于var a = (1,2,3,4,5); alert(a);先计算括号里面的.再说逗号运算符.
它将先计算左边的参数,再计算右边的参数值。然后返回最右边参数的值。
举个例子:

var n1 = 1;
var n2 = 2;
var n3 = (n1++,++n2);
console.log(n1,n2,n3)  //2,3,3

注意:并不是所有数据构造器原型上的的toString方法运行机制都是一样的,几乎每个构造器都对toString方法进行了重写,我们可以验证:

var obj = new Object().toString();
var arr = new Array(1,2).toString();
var date = new Date().toString();

console.log(obj + '\n' + arr + '\n' + date);

array.png

object.png
当然,我们也可以把基础数据类型转换成对象
//string、number、boolean类型都可以通过自身的构造器new出一个新对象,
//这个对象与object构造器new出的效果是一样的
var str = new String('a');
var strO = new Object('a');

var num = new Number(1);
var numO = new Object(1);

var boo = new Boolean(false);
var booO = new Object(false);

//undefined和null在js解释器内没有构造器,所以不能直接new对应类型,只能通过 Object构造器
//undefined和null转成对象都是空对象,老版浏览器也有可能报错
var undO = new Object(undefined);
var nulO = new Object(null);
console.log(str,strO,num,numO,boo,booO,undO,nulO);

打印结果
2、再来看ECMAScript中对于 == 运算符运算规则的定义,(任何看似杂乱无章的转化其实都是有规律可循的,善于查看规范)
1.若Type(x)与Type(y)相同, 则
    a.若Type(x)为Undefined, 返回true。
    b.若Type(x)为Null, 返回true。
    c.若Type(x)为Number, 则
        Ⅰ.若x为NaN, 返回false。
        Ⅱ.若y为NaN, 返回false。
        Ⅲ.若x与y为相等数值, 返回true。
        Ⅳ.若x 为 +0 且 y为−0, 返回true。
        Ⅴ.若x 为 −0 且 y为+0, 返回true。
    d.若Type(x)为String, 则当x和y为完全相同的字符序列(长度相等且相同字符在相同位置)时返回true。 否则, 返回false。
    e.若Type(x)为Boolean, 当x和y为同为true或者同为false时返回true。 否则, 返回false。
    f.当x和y为引用同一对象时返回true。否则,返回false。
2.若x为null且y为undefined, 返回true。
3.若x为undefined且y为null, 返回true。
4.若Type(x) 为 Number 且 Type(y)为String, 返回comparison x == ToNumber(y)的结果。
5.若Type(x) 为 String 且 Type(y)为Number, 返回比较ToNumber(x) == y的结果。
6.若Type(x)为Boolean, 返回比较ToNumber(x) == y的结果。
7.若Type(y)为Boolean, 返回比较x == ToNumber(y)的结果。
8.若Type(x)为String或Number,且Type(y)为Object,返回比较x == ToPrimitive(y)的结果。
9.若Type(x)为Object且Type(y)为String或Number, 返回比较ToPrimitive(x) == y的结果。

之前我发过这个表,但是有同学反应有点乱,那我就翻译一个更乱的:


x == y
1.如果变量x与变量y的数据类型相同,则
    a.如果变量x为Undefined类型,那么变量y也是Undefined类型,Undefined类型只有一个值,那就是undefined,所以可得结论:
undefined == undefined 返回true
    b.如果变量x为Null类型,那么变量y也是Null类型,Null类型只有一个值,那就是null,所以可得结论:null == null 返回true
注:如果想要判断一个数据是不是Null类型,不要使用typeof进行判断,因为typeof null 返回的是'object',原因是js解释器设定,
如果一个数据的二进制编码前三位都为0的话,用typeof判断就会返回'object',而null数据的二进制编码所有位数都是0.所以返回'object',
可能就是因为这个原因,在ES3版中新增了一个数据类型 undefined,以弥补null的不足;
如果我们想判断一个数据是否为null,可以使用 (数据 === null),返回true,则为null。或者使用
Object.prototype.toString.call(数据),返回'[object Null]',则为null;
    c.如果变量x位数字类型,则:
       Ⅰ.和Ⅱ.的意思是,无论x和y谁为NaN,返回的结果都为false,因为NaN不等于任何数值,包括它本身
       Ⅲ.数值相等,返回true,不用解释;
       Ⅳ.和Ⅴ. 在js中,0有两种表示方式,0和-0,但是这两个值是完全相等的,0===-0 也会返回 true
    d.如果 == 两边都是字符串,那么将会依次判断==两边索引相同的字符在Unicode字符集中的编码值是否相等;举个例子:'abc' == 'abd' ; 
解释器会先判断'abc'的'a'与'abd'的'a'在Unicode字符集中的编码值是否相等(获取一个字符的在Unicode字符集中的编码值的方法是:charCodeAt(索引)
比如要获取'abc'的a的编码值 =>'abc'.charCodeAt(0) ,得到97),相等就去比较'b'和'b',以此类推,直到找到不相等的项,返回false。
或者全部检索完成相等,返回true。
{注意:javaScript采用Unicode的国际化标准,而Unicode目前普遍采用的是UCS-2,使用2个字节,16进制无疑是最合适的表现手段,
\u + 4位16进制在javaScript中也可表示一个字符,并且和对应字符是完全相等的。比如,'我' === '\u6211'返回true;
我们可以如下手段把普通字符转换成Unicode标准形式,来加深印象:
var str = '我';
var code = str.charCodeAt(0); //获取Unicode对应编码;
var code16 = code.toString(16); //把获取到的编码转换成16进制
var unnicodeStr = eval('"'+'\\u'  + code16+'"'); //在转换的4位16进制前 + '\u';
str === unnicodeStr; //返回true
又或者:
var str = '我';
var code = str.charCodeAt(0); //获取Unicode对应编码;
var unnicodeStr = String.fromCharCode(code);//直接获取Unicode对应字符,但是就和'\u'没什么关系了
str === unnicodeStr; //返回true
如果还有疑惑,可以搜索关键字['ASCII','Unicode','utf-8','utf-16','utf-32','GBK']
}
    e.这个很好理解;
    f.还记得我们说过的基础数据和复杂数据的区别吗,这段规则的潜在台词就是:任何两个独立的对象都是不相等的,比如:
var a = [];var b = []; a==b返回false
但是,如果他们引用了同一个对象的内存地址,就完全相等了:
var a = []; var b = a; a==b返回true,a===b返回true.或者
var a = b = [];a==b返回true,a===b返回true //强烈不推荐这么写,因为此时声明的a变量是局部的,但是b却是全局的,
//而且连等赋值同一对象的内存地址,会给你造成很多意想不到的麻烦
var a = {n:1};
var b = a;
a.x = a = {n:2}; 
console.log(a,b)  //{n:2},{n:1,x:{n:2}}
2.和3.的潜在台词就是,null和undefined在和其他数据进行==比较时,不会进行隐式类型转换,但同时又规定,
null和undefined是相等的(可能因为undefined是null的延伸的历史原因)。
[undefined == undefined,null == null, null == undefined,undefined == null]
除了这四种情况返回true外,任何null和undefined参与的==比较全部返回false。
4.和5.表示 如果==两边为number和string类型,那么会把两边都转换成数字类型,在进行比较。
6.和7.表示 如果boolean类型在参与==比较时,会把自身转换成数字类型,在进行比较。
8.和9.表示 除了对象与对象==比较时比较的是内存地址外,对象与任何数据进行==比较都会先把自身转换成原始值
(也就是我们提到的先调用自身的valueOf(),再调用toString()),再参与比较
说了这么多,终于可以解析提到的6. [ ] == ![ ] 的问题

解析步骤:

[ ] == ![ ]
1.首先会先运算 ![ ],任何对象在转换boolean时,都会返回true,再取反,得到false。得出:
[ ] == false;
2.然后开始==比较,从左向右运算,根据== 运算符运算规则,[ ]会转换成原始值,调用valueOf(),再调用toString()。得到 
空字符串 '',得出:
'' == false;
3.根据== 运算符运算规则7.,要把false转换成数字,得到 0 ,得出:
'' == 0;
4.根据== 运算符运算规则5.,要把''转换成数字,得到 0 ,得出:
0 == 0;
返回true。

收工。

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

推荐阅读更多精彩内容

  • 1.埋点是做什么的 2.如何进行埋点 3.埋点方案的设计 近期常被问到这个问题,我担心我的答案会将一些天真烂漫的孩...
    lxg阅读 2,011评论 0 1
  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,966评论 0 13
  • 第一章: JS简介 从当初简单的语言,变成了现在能够处理复杂计算和交互,拥有闭包、匿名函数, 甚至元编程等...
    LaBaby_阅读 1,630评论 0 6
  • 半个暑假贡献给了电子设计竞赛,半个暑假用来好好学C语言了,刚开学趁着没课,简单回顾下入门不久就告了一段落的H5+C...
    宁缺勿滥不忘初心阅读 2,087评论 0 1
  • 今天去参加我姨家的满月酒,恰逢母亲节。办了将近30桌,场面甚是热闹,我姨神采奕奕的在招待客人,相对于其他事情,...
    沙果妈妈阅读 172评论 0 0