在JavaScript中,如果变量未定义,则可以设置一个变量。

267

我知道我可以测试 JavaScript 变量,如果它是 undefined,那么定义它,但是是否有一种方法可以这样说:

var setVariable = localStorage.getItem('value') || 0;

看起来这是一种更清晰的方式,我相信我在其他语言中也见过类似的写法。


33
这并不是针对“未定义”的测试,而是针对“假值”的测试。 - Alnitak
5
请注意,如果用户禁用了Cookie(至少在Chrome中),则 localStorage.getItem() 将抛出异常,因此你可能需要将其包装在一个 try...catch 语句中。 - urish
2
可能是What does the construct x = x || y mean?的重复问题。 - Michał Perłakowski
13个回答

380

是的,它可以这样做,但严格来说,如果检索到的值是 falsey,那么它将分配默认值,而不是真正的 undefined。因此,它不仅匹配 undefined,还包括 nullfalse0NaN""(但不包括 "0")。

如果您只想在变量严格为 undefined 时设置默认值,则最安全的方法是编写:

var x = (typeof x === 'undefined') ? your_default_value : x;

在更新的浏览器上,实际上可以安全地编写以下代码:

var x = (x === undefined) ? your_default_value : x;

但请注意,在旧的浏览器中,有可能会通过声明一个名为 undefined 的变量并给它定义一个值来破坏这个功能,导致测试失败。


3
我很惊讶这种模式没有被更多的 JS 库所包含。 - joemaller
使用 var x = (x === undefined ? def_val : x); 与稍长的 var x = (typeof x === 'undefined') ? def_val : x; 相比,是否存在缺点?行为是否有所不同? - user2210287
4
在较旧的浏览器中,可以重新定义undefined,从而导致相等性测试失败。 - Alnitak
@a1an 其他语言中有一个"空值合并运算符",它没有 ||= 的(经常是不需要的)行为,即它匹配任何 "falsey" 值,而不仅仅是严格的 "undefined" 值。遗憾的是,JS 没有这个功能。 - Alnitak
@Alnitak,那些旧浏览器的列表在哪里? - dieresys
显示剩余5条评论

162

逻辑空值赋值,ES2021+解决方案

浏览器目前正在添加新的运算符,??=||=&&=。本文将重点介绍??=

这个运算符用于检查左侧是否为undefinednull,如果已定义则短路。如果未定义,则将右侧的值赋给左侧变量。

比较方法

// Using ??=
name ??= "Dave"

// Previously, ES2020
name = name ?? "Dave"

// or
if (typeof name === "undefined" || name === null) {
    name = true
}

// Before that (not equivalent, but commonly used)
name = name || "Dave" // Now: name ||= "Dave"

基本示例

let a       // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false

对象/数组示例

let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

??= 浏览器支持 2022年10月 - 93%

??= Mozilla文档

||= Mozilla文档

&&= Mozilla文档


3
将此答案复制到另外两个问题(在JavaScript中,如果为null或未定义,请替换值JavaScript中是否有“null coalescing”操作符?),这真的是一个好主意吗?把它标记为重复可能更好,或者在评论中链接到相关问题? - Sebastian Simon
这些问题都略有不同,因此答案也各不相同。示例是一致的,以便一目了然地改善内联上下文。可能可以链接更多详细信息,但这将需要额外的跳转,可能会导致人们跳过解决方案。 - Gibolt
2
请注意,一些 IDE(如 NetBeans)会将 ??= 视为无效的语法。默认的自动源代码格式化将把它转换成 ?? =,中间带有一个空格,而这是无效的 JS 代码。现在需要弄清如何自定义自动格式化程序... - OXiGEN
无法相信这个基本功能还没有得到支持。 - ddruganov
遗憾的是,这似乎无法处理嵌套属性,例如 router["GET"]?.["/admin"] ??= {"/admin": handler};。也许更深层次的抽象会更好。 - Redu
它在多层次中使用?.运算符无法正常工作,因为它不会填充缺失的层次,但是在定义的对象的任何深度都可以正常工作。如果a['b']已定义,则a['b']['c'] ??= d将起作用。 - Gibolt

30

2018年ES6的答案是:

return Object.is(x, undefined) ? y : x;
如果变量x未定义,则返回变量y,否则如果变量x已定义,则返回变量x。

4
据我所知,在这种情况下,Object.is并不比===更好。" === 运算符(以及 == 运算符)将数字值-0和+0视为相等,并将 NaN 视为不等于 NaN。"(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)在这里无关紧要。 - Trevor Dixon
即便如此,Trevor,你现在应该养成使用Object.is()的习惯,而不是使用===。 - Sterling Bourne
6
我不同意。了解两者并使用适当的一个。特别是,如果两个操作数可能为NaN,则使用Object.is - Trevor Dixon
3
如果Object.is()能够在所有情况下都起作用,而===只适用于98%的情况,为什么还要冒着风险使用===呢?例如:console.log(+0 === -0); // 输出 true,而 console.log(Object.is(+0, -0)); // 输出 false -- 哪个更好呢?负零和正零是相同的吗?这就是你需要问自己的问题,但如果你使用Object.is(),你总是知道答案。它们不相同,所以输出false是正确且预期的结果。 - Sterling Bourne
2
这里的特定上下文总是与 undefined 进行比较。因此,=== 每次都能正常工作,而不仅仅是 98% 的情况。=== 的优点在于它更易于阅读,尤其是一眼就能看出来,更短并且避免了不必要的方法调用。个人认为,只有在情况需要时才使用 Object.is(),在所有其他情况下都更喜欢使用 === - blubberdiblub
1
当然可以,如果你是一位经验丰富的程序员。除非你不小心忘记了一个等号,在这种情况下,调试为什么“==”在某个地方不起作用是我绝不希望任何开发人员遇到的问题。安全第一,始终使用Object.is(),它被包含在规范中的原因是专门为了帮助开发人员编写更少错误的代码。 - Sterling Bourne

28

ES2020答案

使用空值合并运算符,你可以在value为null或undefined时设置默认值。

const setVariable = localStorage.getItem('value') ?? 0;

然而,请注意nullish coalescing运算符不会返回其他类型的假值,例如0''这样的值时,不会返回默认值。

但是,请注意浏览器支持情况。您可能需要使用像Babel这样的JavaScript编译器将其转换为更具向后兼容性的内容。如果您正在使用Node.js,则自版本14以来已经支持该运算符。since version 14


12

我需要在多个地方“设置未定义的变量”。我使用@Alnitak的答案创建了一个函数。希望它能帮助别人。

function setDefaultVal(value, defaultValue){
   return (value === undefined) ? defaultValue : value;
}  

用法:

hasPoints = setDefaultVal(this.hasPoints, true);

6

检查typeof似乎比检查undefined更合理?我假设您期望一个数字,因为当未定义时将变量设置为0

var getVariable = localStorage.getItem('value');
var setVariable = (typeof getVariable == 'number') ? getVariable : 0;

在这种情况下,如果 getVariable 不是一个数字(字符串、对象或其他类型),则将 setVariable 设置为 0

6
在当今时代,实际上您可以使用JS进行方法调用:
// Your variable is null
// or '', 0, false, undefined
let x = null;

// Set default value
x = x || 'default value';

console.log(x); // default value

所以你的例子是可以正常工作的:

const setVariable = localStorage.getItem('value') || 0;

1
小心布尔值。在紧急情况下很容易忽略,但如果选项x是一个布尔设置,并且用户将其设置为false,则由于OR运算符,它将移动到下一个操作数并始终为true。 - Kerry Johnson

3

您可以使用以下任一方式。

let x;
let y = 4;
x || (x = y)

在ES12或更高版本中

let x;
let y = 4;
x ||= y;

这已经被建议过了。||= 只有在变量为假值时才会设置它,而不是仅仅未定义。 - Sebastian Simon

2
如果你是一个函数式编程(functional programming)的粉丝,Ramda 有一个很棒的辅助函数叫做 defaultTo
使用方法: const result = defaultTo(30)(value) 当处理 undefined 布尔值时,它更加有用: const result2 = defaultTo(false)(dashboard.someValue)

1
这是完全相同的 const result = value || 30;,只不过使用了中间函数和 +1000 字节的库。 - Adelin
1
@Adelin 不是这样的,它也处理布尔类型的假值。如果你已经在使用这个库(就像我一样),那么它非常有用且简洁。 - Damian Green

1
今天我遇到了一个情况,我不想让一些值被零覆盖。我们有一个带有一些常用实用程序方法的文件来处理这种情况。以下是我添加的代码以处理此情况并保持灵活性。
function getIfNotSet(value, newValue, overwriteNull, overwriteZero) {
    if (typeof (value) === 'undefined') {
        return newValue;
    } else if (value === null && overwriteNull === true) {
        return newValue;
    } else if (value === 0 && overwriteZero === true) {
        return newValue;
    } else {
        return value;
    }
}

如果我只想为未定义的值设置或覆盖null或0值,则可以选择最后两个参数可选地调用它。以下是对其的调用示例,如果ID未定义或为空,则将ID设置为-1,但不会覆盖0值。
data.ID = Util.getIfNotSet(data.ID, -1, true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接