是什么原因在JavaScript中使用null而不是undefined?


103

我已经写了很长时间的JavaScript,而且从来没有理由使用过null。似乎undefined总是可取的,并且在程序上达到相同的目的。有什么实际的原因null代替使用undefined



好吧,有像document.getElementById()这样的方法可以返回null但不能返回undefined,所以在那种情况下,为什么要测试返回值undefined呢?(当然,如果您使用==而不是,它会起作用===,但仍然,为什么您要故意测试错误的东西?)
nnnnnn

拥有可预测的类型可能会有所帮助,这可以指导思考使用一种或另一种类型。如果设计总是返回,需要或需要对象,则对虚假结果使用null(例如document.getElementById('does-not-exist'))。变量var a;和函数返回值默认为undefined。过去,null在全局范围内,因此使用它会减慢执行速度,并导致我更喜欢其他虚假类型(false,'',0)而不是自由引用。我个人避免使用null,除非有其他令人信服的理由,否则因为我认为它更简单(通常更好)。
吉姆蒙特

Answers:


59

Null和undefined本质上是两个含义相同的值。唯一的区别是在约定的你如何使用它们系统。正如某些人提到的那样,有些人使用null表示“无对象”,有时您可能会得到一个对象,而未定义意味着没有对象(或有错误)。我的问题是它完全武断,完全没有必要。

就是说,有一个主要区别-未初始化的变量(包括未传递任何参数的函数参数等)始终是未定义的。

这就是为什么在我的代码中我从不使用null,除非我无法控制的东西返回null(例如正则表达式匹配)。这样做的好处是使事情简化了很多。我永远不必检查x === undefined || x ===空。如果您习惯使用==或仅使用if(x)...之类的东西。停下来。!x对于空字符串,0,null和NaN,结果将为true-即您可能不需要的东西。如果您要编写不是很糟糕的javascript,请始终使用Triple equals ===,并且永远不要使用null(改为使用undefined)。这将使您的生活更轻松。


137
我不同意这一点。Null用于以编程方式定义为空。未定义是指该引用不存在。空值具有对“ nothing”的定义引用。如果您正在调用对象的不存在的属性,则将无法定义。如果我故意将该属性设为空,则它必须为null,以便您知道它是有目的的。许多javascript库都以这种方式工作。
com2ghz

6
@ com2ghz您所描述的是许多哲学之一。许多js库的确可以这种方式工作。许多人也不是这样。在javascript中,您可以像使用null一样轻松地将显式的未定义值存储在对象或数组中。两者的含义完全取决于上下文。即IE,它们表示您想要的含义。
BT

3
这就是为什么JS是一种可怕的语言。就像您说的那样,许多图书馆都有自己的理念,但是您为一个项目提供了许多图书馆。因此,您遇到了这些库的许多约定。您需要进行几次虚假的检查。这不是最后一次将null对象连接到字符串并获得“ null”作为字符串。或传递一个0作为数字值,并想知道IF语句为什么处理t为假。
com2ghz 2016年

2
@ com2ghz那么JS是一种可怕的语言,因为它同时具有null和undefined?我可以数出所有主要语言中的数十种错误语言决定,而js绝非例外。我实际上不需要或不想在我的代码中使用虚假检查,而总是使用===or !==。如果您知道如何使用JS,它就是一种奇妙的表达语言。
BT

6
对。我刚刚了解到null/ undefined二分法是必要的,因为JS的初始版本没有hasOwnPropertyin运算符。既然如此,我真的不明白为什么其中一个没有在ES6或我见过的任何ES7提议中被废除。
安迪

86

我真的不知道答案,但根据尼古拉斯C. Zakas,他的书第30页专业的JavaScript的Web开发

当定义一个打算稍后用于保存对象的变量时,建议将变量初始化null为与其他变量相反的值。这样,您可以显式检查该值,null以确定该变量以后是否已用对象引用填充


8
+1是,我同意,而且我始终会尝试记住将vars初始化为null。然后,我很确定这undefined意味着灾难发生了。只是恕我直言。
彼得·威尔逊,

9
@Pete-但这对“灾难”一无所知,那有什么意义呢?而且,只有在知道函数遵循约定的情况下,才能做出该假设。
RobG

7
另一方面,您也可以使用初始化变量,var myVar;并显式检查该值,undefined以确定它是否稍后被对象引用填充。我的观点是,这完全是学术性的,您可以选择任何一种方式,而任何提出一种建议而不是另一种建议的人都只是在推动自己的惯例。
thdoan

1
自从15年前开始使用JavaScript以来,我唯一的问题是您经常不得不测试undefinednull。这仍然很烦人。我避免分配变量null只是为了减少额外的null测试。
G Man

4
@GMan-这是唯一可以进行==比较(而不是===)的地方:(v == nullv == undefined)将检查null或未定义。
里克·洛夫

14

在这一天结束,因为两者nullundefined要挟相同的值(Boolean(undefined) === false && Boolean(null) === false),你可以在技术上使用要么把工作做好。但是,IMO有正确的方法。

  1. 将用法的使用留给undefinedJavaScript编译器。

    undefined用于描述不指向参考的变量。JS编译器会照顾您。在编译时,JS引擎会将所有提升变量的值设置为undefined。随着引擎逐步执行代码和提供值,引擎将为各个变量分配相应的值。对于那些没有为其找到值的变量,这些变量将继续保持对原语的引用undefined

  2. 仅当您明确希望将变量的值表示为“无值”时,才使用null。

    如@ com2gz所述:null用于以编程方式定义为空。undefined表示该引用不存在。一个null价值有一个定义的参考,以“无”。如果调用的是对象的不存在属性,则将得到undefined。如果我故意将该属性设为空,则必须为空,null以便您知道它是故意的。

TLDR;不要使用undefined原语。当您声明没有赋值的变量或尝试访问没有引用的对象的属性时,JS编译器会自动为您设置一个值。另一方面,null仅当您有意让变量具有“无值”时,才使用。

我从来没有明确地将任何内容设置为undefined(并且我在与之交互的许多代码库中都没有遇到过)。另外,我很少使用null。我唯一使用的时间null是当我想将函数参数的值表示为没有值时,即:

function printArguments(a,b) {
  console.log(a,b);
}

printArguments(null, " hello") // logs: null hello

这应该是选择的答案
alaboudi

13

undefined是不存在事物概念的地方;它没有类型,并且在该范围内从未被引用过;null是已知事物存在的地方,但没有价值。


4
您如何知道是否尝试分配值,但是失败并分配了未定义的值?
RobG

11

每个人都有自己的编码方式和自己的内部语义,但是多年来,我发现这是我向提出这个问题的人提供的最直观的建议:有疑问时,请执行JavaScript的工作

假设您正在使用对象属性(例如jQuery插件的选项)...问问自己JavaScript赋予了一个尚待定义的属性的值-答案是undefined。因此,在这种情况下,我将使用“未定义”初始化这些类型的事物以使其与JavaScript一致(对于变量,您可以var myVar;代替var myVar = undefined;)。

现在,假设您正在执行DOM操作... JavaScript将哪些值赋给不存在的元素?答案是null。如果要创建一个占位符变量,该值将作为初始化值,该变量以后将保存对元素,文档片段或与DOM相关的类似内容的引用。

如果使用的是JSON,则需要进行特殊处理:对于未定义的属性值,应将其设置为""null因为undefined不认为值是正确的JSON格式。

如此说来,正如先前的海报所表达的那样,如果您发现自己在一个蓝色的月亮中初始化一次nullundefined多次初始化,那么也许您应该重新考虑如何编写应用程序。


虽然这是一个很好的立场,但我想补充一下这个答案:stackoverflow.com/a/37980662/883303
Frederik Krautwald

@FrederikKrautwald感谢您的链接-这是非常清楚的描述。
thdoan's

10

您可以采用此处建议的约定,但确实没有充分的理由。没有足够一致地使用它就没有意义。

为了使约定有用,您首先必须知道被调用的函数遵循约定。然后,您必须显式测试返回的值并决定要做什么。如果未定义,则可以假定发生了被调用函数知道的某种错误。但是,如果发生错误,并且函数知道该错误,并且将其发送到更广泛的环境中很有用,为什么不使用错误对象呢?即抛出错误?

因此,总而言之,在简单环境中,除了很小的程序以外,约定实际上对其他任何事情都没有用。


3

在一个有用的属性未定义不符合资格:

> null + 3
3
> undefined + 3
NaN

null当我要“关闭”数值或初始化某些数值时,我会用到。我的最后一个用途是操纵CSS转换:

const transforms = { perspective : null, rotateX : null };
// if already set, increase, if not, set to x
runTimeFunction((x) => { trasforms.perspective += x; });
// still useful, as setting perspective to 0 is different than turning it off
runTimeFunction2((x) => { transforms.perspective = null; });

// toCss will check for 'null' values and not set then at all
runTimeFunction3(() => { el.style.transform = toCss(transforms); });

不知道我是否应该使用此属性...


2

DOM节点和元素不是未定义的,但可以为null。

  • 元素的最后一个子元素的nextSibling为null。

  • 第一个孩子的previousSibling为null。

  • 如果元素不存在于文档中,则document.getElementById引用为null。

但是在所有这些情况下,值都不是undefined;那里没有节点。


感谢您的解释。对我而言,这再违反直觉了。
tomekwi 2014年

这实际上取决于您要检查的内容。例如,如果要查看是否存在名为“ myVar”的全局变量,window.myVar则将返回“ undefined(如果不存在)”。在JavaScript中有很多返回“未定义”的东西,只是有很多返回“ null”的东西-都取决于上下文。
thdoan

2

少数人表示可以将对象初始化为null。我只想指出,销毁参数默认值不适用于null。例如:

const test = ({ name } = {}) => {
  console.log(name)
}

test() // logs undefined
test(null) // throws error

这要求调用可能经常发生的函数之前进行null检查。


1

我现在正在解决这个确切的问题,并研究以下哲学:

  1. 任何旨在返回结果的函数,如果未能找到结果,则应返回null
  2. 任何不打算返回结果的函数都会隐式返回undefined。

对我来说,这个问题很重要,因为任何调用返回结果的函数的人都不会质疑是否要测试未定义vs空值。

此答案未尝试解决:

  1. 空值和未定义的属性值
  2. 函数中的变量为空还是未定义

在我看来,变量是您自己的业务,而不是API的一部分,并且已定义了任何OO系统中的属性,因此应使用与未定义时不同的值来定义属性(对于已定义,则为null,即未定义)访问不属于您对象的内容时获得)。


1

原因如下:var undefined = 1 是合法的javascript,但var null = 1语法错误。区别在于它null是语言关键字,而undefined出于某种原因却不是。

如果您的代码依赖于比较,undefined就好像它是一个关键字(这是一个if (foo == undefined)很容易犯的错误),该关键字只会起作用,因为没有人使用该名称定义变量。所有人不小心或恶意地用该名称定义全局变量时,所有这些代码都容易受到攻击。当然,我们都知道在JavaScript中意外定义全局变量是完全不可能的...


1
使用void 0而不是未定义。
Frederik Krautwald '16

1

只需添加某些JavaScript库,null和undefined可能会带来意想不到的后果。

例如,lodash的get函数,该函数接受默认值作为第三个参数:

const user = {
  address: {
    block: null,
    unit: undefined,
  }
}
console.log(_.get(user, 'address.block', 'Default Value')) // prints null
console.log(_.get(user, 'address.unit', 'Default Value')) // prints 'Default Value'
console.log(_.get(user, 'address.postalCode', 'Default Value')) // prints 'Default Value'

另一个示例:如果在React中使用defaultProps,则如果传递了属性null,则不使用默认props,因为null被解释为定义的值。例如

class MyComponent extends React.Component {
   static defaultProps = {
      callback: () => {console.log('COMPONENT MOUNTED')},
   }
   componentDidMount() {
      this.props.callback();
   }
}
//in some other component
<MyComponent />   // Console WILL print "COMPONENT MOUNTED"
<MyComponent callback={null}/>   // Console will NOT print "COMPONENT MOUNTED"
<MyComponent callback={undefined}/>   // Console WILL print "COMPONENT MOUNTED"

0

我完全不同意使用null或undefined是不必要的。undefined是使整个原型链接过程保持生命的东西。因此,仅具有null的编译器无法检查此属性是否等于null或未在端点原型中定义。在其他动态类型的语言(例如Python)中,如果要访问未定义的属性,则会引发异常,但是对于基于原型的语言,编译器还应检查父原型,这是未定义的需求最大的地方。

使用null的全部含义只是将变量或属性与对象绑定在一起,该对象是单例并具有空的含义,并且null的使用具有性能目的。这两个代码的执行时间不同。

var p1 = function(){this.value = 1};
var big_array = new Array(100000000).fill(1).map((x, index)=>{
    p = new p1();
    if(index > 50000000){
       p.x = "some_string";
    }

    return p;
});
big_array.reduce((sum, p)=> sum + p.value, 0)

var p2 = function(){this.value = 1, p.x = null};
var big_array = new Array(100000000).fill(1).map((x, index)=>{
    p = new p2();
    if(index > 50000000){
       p.x = "some_string";
    }

    return p; 
});
big_array.reduce((sum, p)=> sum + p.value, 0)

0

未知变量:undefined

已知变量,但无值:null

  1. 您从服务器收到一个对象,server_object
  2. 您参考server_object.errj。它告诉你它是undefined。那意味着它不知道那是什么。
  3. 现在您参考server_object.err。它告诉你它是null。这意味着您引用的是正确的变量,但是它是空的。因此没有错误。

问题是当您声明不带值(var hello)的变量名时,js声明为undefined:此变量不存在;而程序员通常的意思是:“我还没有给它赋值” null

因此,程序员的默认行为(将不带任何值的变量声明为空)与js不一致(将其声明为不存在)。而且,!undefined而且!null两者都是,true所以大多数程序员都将它们视为同等的。

你当然可以确保你总是这样var hello = null,但大多数不会凋落他们的代码这样确保类型理智的故意松散类型的语言,当他们和!运营商同时治疗undefinednull等同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.