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

上面两示例可以看出:

  • 前置递增++ii先将身的值自增1(相当于i=i+1),再将自增后的值赋值给变量a
  • 后置递增i++i先将自身的值赋值给变量a,然后i再自增1(相当于i = i + 1
  • 前置递减--ii先将自的值自减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--ii++i--
名称前置递增(前增量)前置递减(前减量)后置增量(后增量)后置减量(后减量)
表达式++i--ii++i--
描述先自增1,后赋值先自减1,后赋值先赋值,后自增1先赋值,后自减1
返回返回操作对象递增的值返回操作对象递减的值返回操作对象递增之前的值返回操作对象递减之前的值
示例++i;--i;i++;i--
i的结果7575

逻辑语句中的递增(递减)

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);
}

运行结果如下图所示:

运算符

从运算的结果上来看,它们的结果是一致的。或许你会感觉有点不对,因为前面讲++ii++还是有区别的。那么仔细看看他们的运算顺序。

就上面的示例来说,for循环的执行顺序是:

  • 进入循环
  • 首先是初始化,var i = 0;
  • 条件判断i < 10;,如果条件满足,进入执行语句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中还有whileswitch等,感兴趣的同学可以跑个测试,看看其结果是否相同。

前置递增(递减)和后置递增(递减)适用场景

不管是前置递增(递减)和后置递增(递减)这四种操作符对任何值都适用,也就是它们不仅适用于整数,还适用于字符串、布尔值、浮点数值和对象。在应用于不同的值时,递增和递减操作符遵循下列规则:

  • 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减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--)经常会混淆,不知道它们之间的具体区别。经过这次,对它们之间的不同之处有了一个基本的了解。最后,希望这篇文章对您有所帮助。

原文链接:https://www.w3cplus.com/javascript/javascript-increment-and-decrement-operatorssass.html

发表评论

登录后才能评论