当前位置: 首页 > Web前端 > JavaScript

null和undefined的区别!

时间:2023-03-26 21:15:41 JavaScript

作者:MichaelThiessen译者:前端小智来源:dev有梦想,有干货,微信搜索【大千世界】关注这位凌晨还在洗碗的洗碗智慧。本文已收录到GitHubhttps://github.com/qq449245884/xiaozhi,里面有完整的测试站点、资料和我的一线厂商访谈系列文章。很多编程语言都有一个非值,叫做null。它表示一个变量当前没有指向一个对象,例如,当它还没有被初始化时。相反,JavaScript有两个这样的非值:undefined和null。在这篇博文中,我们将研究它们的不同之处以及如何最好地使用或避免它们。1.undefinedvs.null这两个值非常相似,经常互换使用。因此,它们的区别是微妙的。1.1ECMAScript语言规范:undefinedvs.nullECMAScript语言规范对它的描述如下。undefined是“当一个变量还没有被赋值时使用“null”来表示故意不存在任何对象值”1.2两个非值——一个不可逆的bugJavaScript中有两个非值是现在被认为是一个设计错误(甚至被JavaScript的创造者BrendanEich认为)。那么为什么不从JavaScript中删除其中一个值呢?JavaScript的核心原则是永不破坏向后兼容性。这个原则有很多好处。它最大的缺点是无法消除设计错误。1.3undefined和null的历史在Java中(它启发了JavaScript的许多方面),初始值取决于变量的静态类型。对象类型的变量被初始化为空。每个基本类型都有自己的初始化值。比如int变量被初始化为0。在JavaScript中,每一个变量都可以保存对象值和原始类型值。所以,如果null表示“不是对象”,那么JavaScript还需要一个初始化值,意思是“既不是对象也不是原始值”。此初始化值未定义。undefined的存在如果一个变量myVar还没有被初始化,它的值就是undefined。让myVar;assert.equal(myVar,undefined);如果属性.unknownProp丢失,访问该属性将产生未定义的值。constobj={};assert.equal(obj.unknownProp,undefined);如果一个函数有一个没有参数的return语句,则该函数隐式返回undefined。functionmyFunc(){return;}assert.equal(myFunc(),undefined);如果省略参数x,则语言会将参数初始化为未定义。函数myFunc(){返回;}assert.equal(myFunc(),undefined);我的功能();可选地通过obj?.someProp链接,如果obj未定义或为空则返回未定义。>undefined?.somePropundefined>null?.somePropundefined3.null出现对象的原型要么是对象要么是原型链末尾的null。Object.prototype没有原型。>Object.getPrototypeOf(Object.prototype)null如果我们将正则表达式(如/a/)与字符串(如'x')匹配,我们要么获得具有匹配数据的对象(如果匹配成功),要么如果匹配失败,则为空。>/a/.exec('x')nullJSON数据格式不支持undefined,只支持null。>JSON.stringify({a:undefined,b:null})'{"b":null}'4.专门处理undefined或null的运算符4.1undefined和参数默认值在以下情况下,参数默认值会用到。缺少一个参数。一个参数的值未定义。例如:functionmyFunc(arg='abc'){returnarg;}assert.equal(myFunc('hello'),'hello');assert.equal(myFunc(),'abc');assert.equal(myFunc(undefined),'abc');undefined还会触发参数的默认值,这意味着它是一个元值。下面的例子说明了这个的用处。functionconcat(str1='',str2=''){returnstr1+str2;}functiontwice(str){//(A)returnconcat(str,str);}在A行,我们没有指定参数默认值。当缺少此参数时,我们将此状态转发给concat()以让它选择一个默认值。4.2未定义和解构的默认值解构中的默认值与参数默认值类似——如果变量在数据中没有匹配项,或者它与未定义的变量匹配,则使用它们。const[a='a']=[];assert.equal(a,'a');const[b='b']=[undefined];assert.equal(b,'b');const{prop:c='c'}={};assert.equal(c,'c');const{prop:d='d'}={prop:undefined};assert.equal(d,'d');4.3undefinedandnull和optionalchain当通过value?.prop进行optionalchaining时,如果value为undefined或null,则返回undefined。也就是说,只要value.prop抛出异常,就会发生这种情况。否则,返回value.prop。functiongetProp(value){//可选的静态属性访问返回值?.prop;}assert.equal(getProp({prop:123}),123);assert.equal(getProp(undefined),undefined);assert.equal(getProp(空),未定义);以下两个操作具有类似的效果。obj?.[?expr?]//可选动态属性accessfunc?.(?arg0?,?arg1?)//可选函数或方法call4.4undefinedandnullandnullishcoalescingoperator??如果值undefined或null允许我们使用默认值:>undefined??'默认值''默认值'>null??'默认值''默认值'>0??'默认值'0>123??'默认值'123>''??'默认值'''>'abc'??'默认值''abc'空合并赋值运算符??=:functionsetName(obj){obj.name??='(Unnamed)';returnobj;}assert.deepEqual(setName({}),{name:'(Unnamed)'});assert.deepEqual(setName({name:undefined}),{name:'(Unnamed)'});assert.deepEqual(setName({name:null}),{name:'(Unnamed)'});assert.deepEqual(setName({name:'Jane'}),{name:'Jane'});5.处理undefined和null让我们分享在我们自己的代码中处理undefined和null的最常用方法。5.1undefined和null都不能用作实际值。例如,我们可能希望一个属性file.title永远存在并且永远是一个字符串。有两种常见的方法可以实现这一点。这里只检查undefined和null,而不检查值是否为字符串。您必须自己决定是否要将其作为附加安全措施来实施。5.1undefined和null都被禁止,如下所示}//···}为什么要这样处理?我们希望将undefined和null视为同一事物,因为JavaScript代码经常这样做-例如。//检测属性是否存在if(!obj.requiredProp){obj.requiredProp=123;}constmyValue=myParameter??'一些默认';如果我们的代码有问题,有undefined或者null,我们希望它尽快失败。5.1.2undefined和null都会触发默认值,如下所示functioncreateFile(title){title??='(Untitled)';//···}我们不能在这里使用参数默认值,因为它只被未定义的触发器使用。相反,我们依赖于无效合并赋值运算符??=。为什么选择这种方法?我们希望将undefined视为与null相同(请参阅上一节)。我们希望我们的代码能够稳健且安静地处理undefined和null。5.2undefined或null是“closed”值例如,我们可能希望一个属性file.title是一个字符串或“closed”(文件没有标题)。有几种方法可以实现这一点。5.2.1null是一个“封闭”值}return{title};}另外,undefined可以触发一个默认值。functioncreateFile(title='(Untitled)'){return{title};}为什么选择这种方法?我们需要一个表示“关闭”的not值。我们不希望我们的非值触发参数默认值和解构默认值。我们想将非值字符串化为JSON(我们不能用undefined做)。5.2.2undefined是一个“off”值。functioncreateFile(title){if(title===null){thrownewError('`title`不能为null');}return{title};}为什么采用这种方法?我们需要一个表示“关闭”的not值。我们确实希望我们的非值触发参数默认值和解构默认值。5.2.3为什么不同时使用undefined和null作为“关闭”值?接收值时,将undefined和null都视为“不是值”是有意义的。但是,当我们创建值时,我们希望是明确的,以便使用这些值保持简单。这指向一种不同的方法。如果我们需要一个“off”值,但又不想对这样的值使用undefined或null怎么办?请阅读下文。5.3其他处理“closed”的方法5.3.1特殊值我们可以创建一个特殊值,每当属性.title关闭时我们都可以使用它。constUNTITLED=Symbol('UNTITLED');const文件={标题:无标题,};5.3.2空对象模式空对象模式来自面向对象编程。公共超类的所有子类都具有相同的接口。每个子类实现实例运行的不同模式。其中一种模式是“空”。在下面的示例中,UntitledFile实现了“空”模式。//abstractsuperclassclassFile{constructor(content){if(new.target===File){thrownewError('无法实例化这个类');}this.content=内容;}}classTitledFileextendsFile{constructor(content,title){super(content);this.title=标题;}getTitle(){返回这个标题;}}classUntitledFileextendsFile{constructor(content){super(content);(){返回'(无标题)';}}constfiles=[newTitledFile('亲爱的日记!','我的日记'),newUntitledFile('提醒:选择一个标题!'),];assert.deepEqual(files.map(f=>f.getTitle()),['我的日记','(无题)',]);我们也可以只对标题使用空对象模式(而不是整个文件对象)。5.3.3Maybe类型Maybe类型是一种函数式编程技术。functiongetTitle(file){switch(file.title.kind){case'just':returnfile.title.value;case'nothing':return'(Untitled)';默认值:抛出新的错误();}}constfiles=[{title:{kind:'just',value:'MyDiary'},content:'亲爱的日记!',},{title:{kind:'nothing'},content:'提醒:选择一个标题!',},];assert.deepEqual(files.map(f=>getTitle(f)),['MyDiary','(Untitled)',]);我们可以将数组传递给“just”和“nothing”进行编码。我们的方法的好处在于它得到了TypeScript的良好支持(通过判别联合)。6.我的方法由于三个原因,我不喜欢使用undefined作为“关闭”值。undefined经常意外出现在JavaScript中。ndefined触发参数和结构化默认值(有些人出于同样的原因喜欢undefined)。所以如果我需要一个特定的值,我使用以下两种方法之一。我使用null作为“关闭”值。(作为旁观者,这种方式在TypeScript中支持得比较好)。)我通过上述技术之一避免了undefined和null。这具有更清洁的优点,涉及更多工作的缺点。~完了,我是小智,我去洗碗了,现在只有洗碗才能体验生活中的一点乐趣,逃避~代码部署后,可能的bug无法实时知道,耗费了很多时间以后有钱解决这些bug日志调试的时间,顺便推荐一个好用的BUG监控工具,Fundebug。原文:https://2ality.com/2021/01/un...交流有梦想,有干货,微信搜索【大动作走向世界】关注这位还在早期洗碗的洗碗智慧早晨。本文GitHubhttps://github.com/qq44924588...已收录,有完整的测试站点、资料和我的一线厂商访谈系列文章。