JavaScript运算符:递增和递减
JavaScript中的递增和递减运算符都是一元操作符,言外之意就是只能操作一个值的操作符。递增和递减操作符直接借鉴自C语言,各有两种版本:前置型(递增++i
,递减--i
)和后置型(递增i++
,递减i--
)。
在JavaScript中,递增(递减)的前置和后置运算符对于初学者都非常容易混淆。我就属于这一类型,这次下定决心把这两者的使用和不同之处了解清楚。如果你和我一样,不仿一起来了解一二。
前置型递增(递减)
前置型递增也称之为前增量(pre-increment)运算符,它对操作数进行增量计算,并返回其计算后的值。这样或许不太好理解,换过一种方式来理解:前增量指的先计算,后赋值。
如果文字不好理解,直接上示例,因为代码也是一种好的阐述方式。假设有一个变量i
,其值为1
。那么前置递增为++i
。
var i = 1;
console.log(i); // => 1
++i;
console.log(i); // => 2
这个示例,前置递增++i
操作符把i
的值变成了2
(也就是1+1
)。实际上,执行前置递增++i
和执行下面的表达式效果相同:
var i = 1;
i = i + 1;
console.log(i); // => 2
简单点讲,前置型递增就是先自身计算,再赋值给变量:
var i = 1;
var a = ++i; // var i = 1; i = i + 1; a = i;
console.log(a); // => 2
console.log(i); // => 2
其中var a = ++i
,实际上做了下面这几个操作:
i = 1;
a = i + 1;
i = i + 1;
a = i;
上面的可能有点简单,来看一个复杂一点的:
var i = 1;
a = (++i) + (++i) + (++i);
最终a
的值是多少?把上面的简化为:
(function () {
var i = 1;
var foo = function () {
var j;
j = i + 1;
i = i + 1; // =>2
return j; // => 2
};
var bar = function () {
var m;
m = i + 1; // => 3
i = i + 1; // => 3
return m; // => 3
};
var baz = function () {
var n;
n = i + 1; // => 4
i = i + 1; // => 4
return n; // => 4
};
var a = foo() + bar() + baz(); // 2 + 3 + 4 = 9
return a; // 9
})();
最终a
的值是9
。
前置递减--i
和前置递增++i
类似,不同的是先做减法(减1
),再赋值。比如:
var i = 6;
a = --i;
console.log(a); // => 5
console.log(i); // => 5
代码中的--i
相当于:
var i = 6;
i = i - 1;
a = i - 1;
a = i;
执行前置递增和递减时,变量的值都是在语句被求值以前改变的(返回它递增(减)之后的值)。在计算机科学领域中,这种情况通常被称之为副效应。来看个例子:
var age = 29;
var anotherAge = --age + 2;
console.log(age) // => 28 (age = age - 1)
console.log(anotherAge); // 30 (anotherAge = age - 1 + 2)
这个示列中变量anotherAge
的初始值等于变量age
的值前置递减(age = age - 1
)再加上2
。也就是前置--age
先做减法操作,age
的值变成了28
,所以再加上2
,其值就是30
。
另外在JavaScript运算符中,前置递增(++i
)和递减(--i
)与执行语句的优先级相同,因此整个语句会从左至右被求值。再来看一个示例:
var foo = 1;
var baz = 20;
var bar = ++foo + baz;
var baf = foo + baz;
console.log(foo); // => 2
console.log(bar); // => 22
console.log(baf); // => 22
在这里,bar
等于22
是因为foo
选加了1
之后与baz
相加。而baf
也等于22
是因为相应的加法操作使用了foo
加上1
之后的值。
需要注意的是,表达式++i
并不总和i = i + 1
完全一样,++
运算符从不进行字符串连接操作,它总是会将操作数转换为数字并增1
。如果i
是字符串"1"
,++i
的结果是数字2
,而i+1
是字符串"11"
。
var i = "1";
console.log(typeof(i)); // => string
console.log(++i); // => 2
console.log(typeof(++i)); // => number
现来看i+1
;
var i = "1";
console.log(typeof(i)); // => string
console.log(i+1); // => "11"
console.log(typeof(i+1)); // => string
后置型递增(递减)
前面也说过了,递增(递减)分前置型和后置型。前置型是++i
(--i
),后置型不同的是,将++
(或--
)放置在变量的后面,即i++
(或i--
)。后置型递增(递减)又称之为后增量(post-increment)运算符。
后置型递增是和前置型递增不同之处是其先赋值,后递增。简单点说,后置型递增是先将自身的值赋值给变量,然后再自增1
。来看个例子:
当var a = i++
时,其实际上做了下面这样的操作:
i = 1;
j = i;
i = i + 1;
a = j;
对于后置递增运算符,其自身并不会改变语句的结果,因为递增是这条语句的唯一操作。
var i = 1;
console.log(i); // => 1
console.log(i++); // => 1
上面的例子中i++
并没有改变值,仍然是1
。当然,当语句中还包含其他操作符时,上述区别就会非常明显了。如下面的示例:
var num1 = 2;
var num2 = 22;
var num3 = num1++ + num2; // var n; n = num1; num1 = num1 + 1; num3 = n + num2 = 24
var num4 = num1 + num2; // num1 + num2 = 25
console.log(num1); // => 3
console.log(num3); // => 24
console.log(num4); // => 25
来看一个复杂一点的示例,把前面示例中的前置递增,换成后置递增:
var i = 1;
a = (i++) + (i++) + (i++);
此时的a
又是多少呢?按同样的方法,将上面的表达式(i++) + (i++) + (i++)
拆解一下:
(function () {
var i = 1;
var foo = function () {
var j;
j = i; // => 1
i = i + 1; // => 2
return j; // => 1
};
var bar = function () {
var m;
m = i; // => 2
i = i + 1; // => 3
return m; // => 2
};
var baz = function () {
var n;
n = i; // =>3
i = i + 1; // => 4
return n;
};
var a = foo() + bar() + baz(); // 1 + 2 + 3 = 6
return a; // 6
})()
a
最后的值是6
。不过将前置递增和后置递增混合在一起做运算时,事情将会变得更复杂一些,不过按上述的方法去拆解,就不会出错。来看个示例:
var i = 2;
a = (++i) + (i++) + (++i) + (i++);
上面代码中a
的值又是多少呢?
(function () {
var i = 2;
var foo = function () {
var j;
j = i + 1; // => 3
i = i + 1; // => 3
return j; // => 3
};
var baz = function () {
var m;
m = i; // => 3
i = i + 1; // => 4
return m; // => 3
}
var bar = function () {
var n;
n = i + 1; // =>5
i = i + 1; // => 5
return n; // => 5
}
var boo = function () {
var l;
l = i; // => 5
i = i + 1; // => 6
return l; // => 5
}
var a = foo() + baz() + bar() + boo(); // 3 + 3 + 5 + 5
return a; // => 16
})();
后置递减(i--
)和后置递增是类似的,不同的是,其先赋值,后减1
。简单点看个示例:
var i = 2;
var a = i--;
console.log(a); // => 2
console.log(i); // => 1
其中i--
就是:
var i = 1;
var m = i;
i = i - 1;
前置递增(递减) VS. 后置递增(递减)
如果你看完了前面两部分内容,到这里的话你对前置递增(递减)和后置递增(递减)之间的区别已有一定的了解。这部分再啰嗦一下两者之间的区别。
- 前置递增:前置递增运算符
++
在变量的前面,如++i
- 后置递增:后置递增运算符
++
在变量的后面,如i++
- 前置递减:前置递减运算符
--
在变量的前面,如--i
- 后置递减:后置递减运算符
--
在变量的后面,如i--
简单回顾两个示例,就拿前置递增和后置递增为例吧,先来看前置递的示例++i
:
var i = 1;
console.log(i); // => 1
var a = ++i;
console.log(i); // => 2
console.log(a); // => 2
接下来看后置递增的示例:
var i = 1;
console.log(i); // => 1
var a = i++;
console.log(i); // => 2
console.log(a); // => 1
上面两示例可以看出:
- 前置递增
++i
,i
先将身的值自增1
(相当于i=i+1
),再将自增后的值赋值给变量a
- 后置递增
i++
,i
先将自身的值赋值给变量a
,然后i
再自增1
(相当于i = i + 1
) - 前置递减
--i
,i
先将自的值自减1
(相当于i = i - 1
),再将自减后的值赋值给变量a
- 后置递减
i--
,i
先将自身的值赋值给变量a
,然后i
再自减1
(相当于i = i - 1
)
其中原理并不复杂,主要是和JavaScript中运算符优先级有关系:
++
作为前置递增时,优先级为15
,--
作为前置递减时,优先级为15
++
作为后置递增时,优先级为16
,--
作为后置递减时,优先级为16
=
作为赋值运算符时,优先级为3
所以++
(或 --
)会优先于=
而执行。
归纳起来:
前置递增(前置递减)运算符,先计算(自增
1
(或自减1
)),再赋值,返回操作对象递增(递减)之后的值;后置递增(后置递减)运算符,先赋值,再计算(自增1
(或自减1
)),返回操作对象递增之前的值。
可以理解成:
++i
,增加变量,返回一个新的值i++
,增加变量,返回旧的值--i
,减少变量,返回一个新的值i--
,减少变量,返回旧的值
看个示例:
var a = 5;
var b = 5;
c = ++a;
d = b++;
console.log(a); // => 6
console.log(b); // => 6
console.log(c); // => 6
console.log(d); // => 5
最后为了方便查阅,列表表格
JavaScript自增、自减运算符与表达式,假设i
的初始值为6
:
运算符 | ++i |
--i |
i++ |
i-- |
---|---|---|---|---|
名称 | 前置递增(前增量) | 前置递减(前减量) | 后置增量(后增量) | 后置减量(后减量) |
表达式 | ++i |
--i |
i++ |
i-- |
描述 | 先自增1 ,后赋值 |
先自减1 ,后赋值 |
先赋值,后自增1 |
先赋值,后自减1 |
返回 | 返回操作对象递增的值 | 返回操作对象递减的值 | 返回操作对象递增之前的值 | 返回操作对象递减之前的值 |
示例 | ++i; |
--i; |
i++; |
i-- |
i 的结果 |
7 |
5 |
7 |
5 |
逻辑语句中的递增(递减)
在for
循环语句中,常会看到i++
、i--
、++i
和--i
。那么像for
语句中前置递增(递减)和后置递增(递减)有没有区别呢?
先来看看for
循环中的i++
和++i
:
for(var i = 0; i < 10; i++) {
console.log(i);
}
console.log("-------------- 华丽的分割线 --------------");
for(var i = 0; i < 10; ++i) {
console.log(i);
}
运行结果如下图所示:
从运算的结果上来看,它们的结果是一致的。或许你会感觉有点不对,因为前面讲++i
和i++
还是有区别的。那么仔细看看他们的运算顺序。
就上面的示例来说,for
循环的执行顺序是:
- 进入循环
- 首先是初始化,
var i = 0;
- 条件判断
i < 10;
,如果条件满足,进入执行语句console.log(i)
- 循环结束后,执行
i++
(或者++i
) - 执行完
i++
后跳到第三步,依此3-4-5-3-4-5这样的循环,直到条件不满足为止
把for
循环语句块换成:
for(A;B;C) {D}
实际上程序执行的过程是:A-(B-D-C)-(B-D-C)-(B-D-C)...-B
这样的一个过程。或许你感觉他们的结果本不相同的,但输出为什么都是一样呢?我们再来回忆一下i++
和++i
:
先来看:
i++;
其等价于下面的语句:
i = i;
i = i + 1;
结果i
增加了1
。再来看看:
++i;
其等价于下面的语句:
i = i + 1;
i = i;
从上面的代码来看,不管怎么执行,他们最终都自增了1
。那么将其赋值在别的值上呢?如下:
a = i++;
等价于
a = i;
i = i + 1;
而
b = ++i;
其相当于:
i = i + 1;
b = i;
也就是b = i + 1
。
如此一来,将前面的示例改动一下:
for (var j = 0, i = 0; j < 10; j) {
j = i++;
console.log("循环" + j + ":" + "i=" + i + ",j=" +j);
}
console.log("------------------- 华丽的分割线 -------------------------");
for (var j = 0, i = 0; j < 10; j) {
j = ++i;
console.log("循环" + j + ":" + "i=" + i + ",j=" +j);
}
上图可以说明一切了吧。除了for
循环之外,在JavaScript中还有while
和switch
等,感兴趣的同学可以跑个测试,看看其结果是否相同。
前置递增(递减)和后置递增(递减)适用场景
不管是前置递增(递减)和后置递增(递减)这四种操作符对任何值都适用,也就是它们不仅适用于整数,还适用于字符串、布尔值、浮点数值和对象。在应用于不同的值时,递增和递减操作符遵循下列规则:
- 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减
1
操作。字符串变量变成数值变量。 - 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为
NaN
。字符串变量变成数值变量。 - 在应用于布尔值
false
时,先将其转换为0
再执行加减1
的操作。布尔值变量变成数值变量。 - 在应用于布尔值
true
时,先将其转换为1
再执行加减1
的操作。布尔值变量变成数值变量。 - 在应用于浮点数值时,执行加减
1
的操作。 - 在应用于对象时,先调用对象的
valueOf()
方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是NaN
,则调用toString()
方法后再应用前述规则。对象变量变成数值变量。
来看个示例:
var s1 = "2",
s2 = "z",
b1 = false,
b2 = true,
f = 1.1,
o = {
valueOf: function() {
return -1;
}
};
console.log(s1++); // => 3
console.log(s2++); // => NaN
console.log(b1++); // => 0
console.log(b2++); // => 1
console.log(f++); // => 1.1
console.log(o++); // => -1
其他三种结果,可以将上面的代码简单更改一下,然后跑一下测试,就能看到对应的结果。这里就不再做相应的测试了。
总结
这篇文章主要介绍了JavaScript中前置递增(递减)和后置递增(递减)的使用,以及它们之前的不同之处。对于初学者而言,++i
(--i
)和i++
(i--
)经常会混淆,不知道它们之间的具体区别。经过这次,对它们之间的不同之处有了一个基本的了解。最后,希望这篇文章对您有所帮助。