JS逻辑运算 xxx = xxx || ''

来源:互联网 发布:python hist alpha 编辑:程序博客网 时间:2024/05/20 08:01

1 常规例子

众所周知,逻辑运算符最常用于条件判断的if语句,下面是一个JavaScript的例子

var person = {    name: '张三',    language: 'JS',}if(person.language == 'JS' || person.language == 'C#'){    console.log('录取了');}else{    console.log('不要');}

输出结果:录取了


2 例外情况

直到有一天我看了前端同事的代码,发现在JavaScript里逻辑运算符竟然还可以这样用:

var person = {    name: '张三',    language: '',}var language = person.language || '什么都不会';console.log(language);

输出结果:什么都不会


3 问题分析

什么情况,”||”运算返回的难道不应该是”true”或者”false”吗,怎么会返回一个字符串?其实我这是受了C#的影响。
C#作为一门强类型语言,逻辑运算只能用于布尔值,非布尔值必须转换为布尔值才能进行逻辑运算。而JS作为一门弱类型语言,任何类型的数据都能参与逻辑运算,在运算时会隐式转化为布尔值进行判断。

3.1 C#逻辑运算规则

&& :同真为真,只要有一个为假就返回假。|| :同假为假,只要一个为真就返回真。

都是布尔值参与运算,结果一定返回布尔值,我下意识把这个规则也带入了JS。然而不同的是,JS非布尔值也可以参与逻辑运算,结果并不总是返回一个布尔值,所以运算规则是不能完全这么记的。

3.2 JS逻辑运算规则

&& :如果左操作数为假值则返回左操作数,如果左操作数为真值则返回右操作数。|| :如果左操作数为真值返回左操作数,如果左操作数为假值则返回右操作数。

为什么可以用这个规则,下面引用由David Flanagan著淘宝前端团队翻译的《JavaScript权威指南》这本书的一段话:

3.2.1 解释

“&&”:
运算符返回一个“真值”或者“假值”,但并没有说明这个“真值”或者“假值”到底是什么值。运算符首先计算左操作数的值,即首先计算“&&”左侧的表达式。如果计算结果是假值,那么整个表达式的结果也一定是假值,因此”&&”这时简单地返回左操作数的值,而并不会对右操作数进行计算。

反过来讲,如果左操作数是真值,那么整个表达式的结果则依赖于右操作数的值。如果右操作数是真值,那么真个表达式的值一定是真值;如果右操作数是假值,那么整个表达式的值一定是假值。因此,当左操作数是真值时,”&&”运算符将计算右操作数的值并将其返回作为整个表达式的结果。

“||”:
它会首先计算第一个操作数的值,也就是说会首先计算左侧的表达式。如果计算结果为真值,那么返回这个真值。否则,再计算第二个操作数的值,即计算右侧的表达式。并返回这个表达式的计算结果。

这里的真值/假值并不是布尔值的意思,再次引用《JavaScript权威指南》的内容进行解释

任意JavaScript值都可以转换成为布尔值。下面这些值会被转换成false:
undefined
null
0
-0
NaN
“” // 双引号表示的空字符串
” // 单引号表示的空字符串
所有其他值,包括所有对象(数组)都会转换成true。false和上面7个可以转换成false的值有时被称作“假值”(falsy value),其他值称作“真值”(truthy value)。JavaScript期望使用一个布尔值的时候,假值会被当成false,真值会被当成true。

3.2.2 验证

简单来说,如果是非布尔值参与逻辑运算,会判断这个值转换布尔值的时候是true还是false,true表示真值,false表示假值。最后返回的并不是布尔值而是这个原始值。
我们来验证一下规则是否正确:

// && :如果左操作数为假值则返回左操作数console.log(null && ({}));  // null 为假值,{}为真值,输出 null console.log(NaN && 0);      // NaN为假值,0为假值,输出 NaN// && :如果左操作数为真值则返回右操作数console.log(1 && undefined);    // 1为真值,undefined为假值,输出 undefinedconsole.log([] && 'hello'); //  []为真值,'hello'为真值,输出 'hello'// || :如果左操作数为真值返回左操作数console.log(1 || undefined);    // 1为真值,undefined为假值,输出 1console.log([] || 'hello');     // []为真值,'hello'为真值,输出 []// || :如果左操作数为假值则返回右操作数console.log(null || ({}));  // null 为假值,{}为真值,输出 {}console.log(NaN || 0);      // NaN为假值,0为假值,输出 0

通过验证,发现这些规则确实适用。


4 结论

4.1 与三目运算的关系

现在再去看最初的那个例子,var language = person.language || '什么都不会';其实是三目运算符的简写形式。

var person = {    name: '张三',    language: '',}var l0 = person.language || '什么都不会';var l1 = person.language ? person.language : '什么都不会';console.log(l0);console.log(l1);

输出结果:

什么都不会
什么都不会


C#里也有类似的”??”写法,只不过只能用于判断字符串为null时赋予默认值。

    class Program    {        static void Main(string[] args)        {            var p = new Person            {                name = "张三",                language = null,            };            var l0 = p.language ?? "什么都不会";            var l1 = p.language != null ? p.language : "什么都不会";            Console.WriteLine(l0);            Console.WriteLine(l1);            Console.ReadKey();        }    }    public class Person    {        public string name { get; set; }        public string language { get; set; }    }

输出结果:

什么都不会
什么都不会

4.2 实际运用

“||”:通常用于给假值指定一个默认值,例如前端通过ajax向后端请求数据,返回的数据有可能是null,也有可能是”(空字符串),前端同一处理成”空字符串。
后端返回的数据为

// 后端返回数据var result = {    "code": 0,    "data": [        {            "name": "张三",            "language": null,            "description": "很懒惰"        },        {            "name": "李四",            "language": "",            "description": null        },        {            "name": "王五",            "language": "JS",            "description": ""        }    ]}// 处理var persons = result.data;console.log(JSON.stringify(persons));for (var i = 0; i < persons.length - 1; i++) {    var person = persons[i];    person.language = person.language || '';    person.description = person.description || '';}console.log(JSON.stringify(persons));

输出结果:

[    {        "name": "张三",        "language": null,        "description": "很懒惰"    },    {        "name": "李四",        "language": "",        "description": null    },    {        "name": "王五",        "language": "JS",        "description": ""    }]
[    {        "name": "张三",        "language": "",        "description": "很懒惰"    },    {        "name": "李四",        "language": "",        "description": ""    },    {        "name": "王五",        "language": "JS",        "description": ""    }]