JS操作符拾遗

Clloz · · 134次浏览 ·

前言

对于 js 操作符的一些特性和有趣题目的整理。

逗号操作符

逗号操作符有两个作用,一个是用于当你想要在期望一个表达式的位置包含多个表达式时,可以使用逗号操作符。这个操作符最常用的一种情况是:for 循环中提供多个参数。另一个使用逗号操作符的例子是在返回值前处理一些操作。如同下面的代码,只有最后一个表达式被返回,其他的都只是被求值。

function myFunc () {
  var x = 0;

  return (x += 1, x); // the same of return ++x;
}

赋值多个变量用 var a = 3, b = 4, c = 5;var a = 3; var b = 4; var c = 5; 本质并没有什么不同,只是一种编码习惯,用知乎上一位朋友的话说,前者是 高耦合,低冗余,后者是 低耦合,高冗余,后者更严谨一点,在维护的时候也不容易出错。

关于逗号的第二个用法,有时会引起一些现象,比如下面的代码就改变了函数中的this指向,因为表达式 (0, obj.fun) 返回了 obj.fun 的引用,相当于 window 调用这个函数,所以 this 发生了变化:

var obj = {
    fun: function () {
        console.log(this);
    }
}

obj.fun(); //输出obj
(0, obj.fun)() //输出window对象

链式赋值

我的赋值是可以用链式的方法把一个值赋值给多个变量,当所有变量都被声明,这样做并没有什么问题,但是如果是函数作用域内这样做有时候会引起一些奇怪的现象:

function test() {
    var a;
    a = b = 3;
}
test();
console.log(b); //3
console.log(a); //报错

从结果我们可以看出 b 成为了一个全局变量,要明白原因我们先要知道 js引擎是如何解析 a = b =3; 这条表达式的,我们知道赋值是基于右值的值给左值赋值。根据这个规则我们可以知道,引擎的第一步是把 a 当作左值,b = 3 当作右值,因为 b = 3 是一个表达式,算出值以后才能赋值,所以计算 b = 3 的值,此时引擎发现变量 b 并没有声明,也就是在当前执行环境的变量对象找不到这个变量,于是 b 被处理成了 var b = 3,成了一个全局变量。

变量声明提前不能跨 <script> 标签,更不能跨文件,也就是说变量提升仅仅是把当前js文件或标签中的 var 声明变量或 function 的声明提升到当前文件或标签的开头。并且当函数和变量重名的时候,被提升的声明会是函数。

上面的代码赋值表达式的右值都是 js 的基本类型,如果右值是对象的时候,情况会更复杂一些。因为对象被保存在堆中,我们对对象的赋值只是把对象的引用赋值给变量,而引用的变化不会引起对象的变化,堆中的对象不会因为变量之间引用的传递而发生改变,记住这一点就不太容易被迷惑。不过有时候情况比我们想象的还要复杂一下,比如下面这道经典的题目:

var a  = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x);//undefined
console.log(b.x);//{n: 2};

如果用上面那一套方式来想这道题,第一步左值为 a.x,右值为 a = {n: 1},第二步计算 a = {n: 2},左值为a,右值为 {n: 2},把 {n: 2} 的引用给 a 就可以了,然后赋值给第一步的左值,得到结果 a.x = {n: 2}。流程其实没什么问题,但是在第一步的时候忘记一件事,就是 a对象并没有x属性,而且 .运算符的优先级是仅次于括号的,在还没开始进行赋值分析的时候,已经对 a.x 进行了处理,就是在 a 对象中加入 x 属性,虽然此时并没有对该属性初始化,a.x 应该为 undefined。有了这个思路我们再按刚才的流程走一遍,当完成第二步 a = {n: 2} 的时候 a 的引用已经改变,此时 a.x 代表的是 {n:1, x: undefined} 中的 x(可能有人疑惑 a 已经改变引用,a.x 中的 a 应该也变了,但因为 a.xa 在同一个表达式内,a.x 已经在前面被引擎解析,a.x此时应被理解成堆中对象的属性),最后把这个 {n: 2} 的引用赋给 x 属性。

我们可以发现赋值运算的解析是从左向右,但是计算是从右向左的。链式赋值有时候会遇到这样的副作用,所以还是尽量避免使用。


Clloz

人生をやり直す

发表评论

电子邮件地址不会被公开。 必填项已用*标注

我不是机器人*

EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00