处理 JS 一些未定义的技巧的 7 个技巧

229 篇文章 14 订阅
31 篇文章 0 订阅

原文:https://dmitripavlutin.com/7-tips-to-handle-undefined-in-javascript/

译者:前端小智

为了保证的可读性,本文采用意译而非直译。

大约8年前,当原作者开始学习JS时,遇到了一个奇怪的情况,既存在 undefined 的值,也存在表示空值的 null。它们之间的明显区别是什么?它们似乎都定义了空值,而且,比较 null==undefined的计算结果为 true

大多数现代语言,如Ruby、Python或Java都有一个空值( nil或 null),这似乎是一种合理的式。

对于JavaScript,解释器在访问尚未初始化的变量或对象属性时返回 undefined。例如:

let company;	
company;    // => undefined	
let person = { name: 'John Smith' };	
person.age; // => undefined

另一方面, null表示缺少的对象引用,JS本身不会将变量或对象属性设置为 null

一些原生方法,比如 String.prototype.match(),可以返回 null来表示丢失的对象。看看下面的示例:

let array = null;  	
array;                // => null	
let movie = { name: 'Starship Troopers',  musicBy: null };	
movie.musicBy;        // => null	
'abc'.match(/[0-9]/); // => null

由于 JS 的宽容特性,开发人员很容易访问未初始化的值,我也犯了这样的错误。

通常,这种危险的操作会生成 undefined 的相关错误,从而快速地结束脚本。相关的常见错误消息有:

  • TypeError:'undefined'isnotafunction

  • TypeError:Cannotreadproperty'<prop-name>'ofundefined

  • type errors

JS 开发人员可以理解这个笑话的讽刺:

function undefined() {	
  // problem solved	
}

为了降低此类错误的风险,必须理解生成 undefined的情况。更重要的是抑制它的出现并阻止在应用程序中传播,从而提高代码的持久性。

让咱们详细讨论 undefined 及其对代码安全性的影响。

undefined 是什么鬼

JS 有6种基本类型

  • Boolean: true 或 false

  • Number: 1,6.7,0xFF

  • String: "Gorilla and banana"

  • Symbol: Symbol("name") (starting ES2015)

  • Null: null

  • Undefined: undefined.

和一个单独的 Object 类型: {name:"Dmitri"},["apple","orange"]

根据ECMAScript规范,从6种原始类型中, undefined是一个特殊的值,它有自己的 Undefined类型。

未为变量赋值时默认值为 undefined

该标准明确定义,当访问未初始化的变量、不存在的对象属性、不存在的数组元素等时,将接收到一个 undefined的值。例如

let number;	
number;     // => undefined	
let movie = { name: 'Interstellar' };	
movie.year; // => undefined	
let movies = ['Interstellar', 'Alexander'];	
movies[3];  // => undefined

上述代码大致流程:

  • 未初始化的变量 number

  • 一个不存在的对象属性 movie.year

  • 或者不存在数组元素movies3

都会被定义为 undefined

ECMAScript规范定义了 undefined 值的类型

Undefined type是其唯一值为 undefined 值的类型。

在这个意义上, typeofundefined返回“undefined”字符串

typeof undefined === 'undefined'; // => true

当然 typeof可以很好地验证变量是否包含 undefined的值

let nothing;	
typeof nothing === 'undefined';   // => true

2. 创建未定义的常见场景

2.1未初始化变量

尚未赋值(未初始化)的声明变量默认为 undefined

let myVariable;	
myVariable; // => undefined

myVariable已声明,但尚未赋值,默认值为 undefined

解决未初始化变量问题的有效方法是尽可能分配初始值。变量在未初始化状态中越少越好。理想情况下,你可以在声明 constmyVariable='Initial value'之后立即指定一个值,但这并不总是可行的。

技巧1:使用 let 和 const 来代替 var

在我看来,ES6 最好的特性之一是使用 const和 let声明变量的新方法。 const和 let具有块作用域(与旧的函数作用域 var相反),在声明行之前都存在于暂时性死区。

当变量一次性且永久地接收到一个值时,建议使用 const声明,它创建一个不可变的绑定。

const的一个很好的特性是必须为变量 constmyVariable='initial'分配一个初始值。变量未暴露给未初始化状态,并且访问 undefined是不可能的。

以下示例检查验证一个单词是否是回文的函数:

function isPalindrome(word) {	
  const length = word.length;	
  const half = Math.floor(length / 2);	
  for (let index = 0; index < half; index++) {	
    if (word[index] !== word[length - index - 1]) {	
      return false;	
    }	
  }	
  return true;	
}	
isPalindrome('madam'); // => true	
isPalindrome('hello'); // => false

length 和 half 变量被赋值一次。将它们声明为 const似乎是合理的,因为这些变量不会改变。

如果需要重新绑定变量(即多次赋值),请应用 let声明。只要可能,立即为它赋一个初值,例如, letindex=0

那么使用 var 声明呢,相对于ES6,建议是完全停止使用它。

640?wx_fmt=png

var 声明的变量提会被提升到整个函数作用域顶部。可以在函数作用域末尾的某个地方声明 var变量,但是仍然可以在声明之前访问它:对应变量的值是 undefined

相反,用 let 或者 const 声明的变量之前不能访问该变量。之所以会发生这种情况,是因为变量在声明之前处于暂时死区。这很好,因为这样就很少有机会访问到 undefined 值。

使用 let(而不是var)更新的上述示例会引发 ReferenceError 错误,因为无法访问暂时死区中的变量。

function bigFunction() {	
  // code...	
  myVariable; // => Throws 'ReferenceError: myVariable is not defined'	
  // code...	
  let myVariable = 'Initial value';	
  // code...	
  myVariable; // => 'Initial value'	
}	
bigFunction();

技巧2:增加内聚性

内聚描述模块的元素(命名空间、类、方法、代码块)内聚在一起的程度。凝聚力的测量通常被称为高凝聚力或低内聚。

高内聚是优选的,因为它建议设计模块的元素以仅关注单个任务,它构成了一个模块。

  •  

    专注且易懂:更容易理解模块的功能

     

  •  

    可维护且更容易重构:模块中的更改会影响更少的模块

     

  •  

    可重用:专注于单个任务,使模块更易于重用

     

  •  

    可测试:可以更轻松地测试专注于单个任务的模块

     

640?wx_fmt=png

高内聚和低耦合是一个设计良好的系统的特征。

代码块本身可能被视为一个小模块,为了尽可能实现高内聚,需要使变量尽可能接近使用它们代码块位置。

例如,如果一个变量仅存在以形成块作用域内,不要将此变量公开给外部块作用域,因为外部块不应该关心此变量。

不必要地延长变量生命周期的一个典型例子是函数中 for循环的使用:

function someFunc(array) {	
  var index, item, length = array.length;	
  // some code...	
  // some code...	
  for (index = 0; index < length; index++) {	
    item = array[index];	
    // some code...	
  }	
  return 'some result';	
}

index, item和 length变量在函数体的开头声明,但是,它们仅在最后使用,那么这种方式有什么问题呢?

从顶部的声明到 for语句中变量 index 和 item 都是未初始化的,值为 undefined。它们在整个函数作用域内具有不合理较长的生命周期。

一种更好的方法是将这些变量尽可能地移动到使用它们的位置:

function someFunc(array) {	
  // some code...	
  // some code...	
  const length = array.length;	
  for (let index = 0; index < length; index++) {	
    const item = array[index];	
    // some	
  }	
  return 'some result';	
}

index和 item变量仅存在于 for语句的作用域内, for 之外没有任何意义。 length变量也被声明为接近其使用它的位置。

为什么修改后的版本优于初始版本?主要有几点:

  •  

    变量未暴露 undefined状态,因此没有访问 undefined的风险

     

  •  

    将变量尽可能地移动到它们的使用位置会增加代码的可读性

     

  •  

    高内聚的代码块在必要时更容易重构并提取到单独的函数中

     

2.2 访问不存在的属性

访问不存在的对象属性时,JS 返回 undefined

咱们用一个例子来说明这一点:

let favoriteMovie = {	
  title: 'Blade Runner'	
};	
favoriteMovie.actors; // => undefined

favoriteMovie是一个具有单个属性 title 的对象。使用属性访问器 favoriteMovie.actors访问不存在的属性 actors将被计算为 undefined

本身访问不存在的属性不会引发错误, 但尝试从不存在的属性值中获取数据时就会出现问题。常见的的错误是 TypeError:Cannotreadproperty<prop>ofundefined

稍微修改前面的代码片段来说明 TypeErrorthrow

let favoriteMovie = {	
  title: 'Blade Runner'	
};	
favoriteMovie.actors[0];	
// TypeError: Cannot read property '0' of undefined

favoriteMovie没有属性 actors,所以 favoriteMovie.actors的值 undefined。因此,使用表达式 favoriteMovie.actors[0]访问 undefined值的第一项会引发 TypeError

JS 允许访问不存在的属性,这种允许访问的特性容易引起混淆:可能设置了属性,也可能没有设置属性,绕过这个问题的理想方法是限制对象始终定义它所持有的属性。

不幸的是,咱们常常无法控制对象。在不同的场景中,这些对象可能具有不同的属性集,因此,必须手动处理所有这些场景:

接着我们实现一个函数 append(array,toAppend),它的主要功能在数组的开头和/或末尾添加新的元素。 toAppend参数接受具有属性的对象:

  •  

    first:元素插入数组的开头

     

  •  

    last:元素在数组末尾插入。

     

函数返回一个新的数组实例,而不改变原始数组(即它是一个纯函数)。

append()的第一个版本看起来比较简单,如下所示:

function append(array, toAppend) {	
  const arrayCopy = array.slice();	
  if (toAppend.first) {	
    arrayCopy.unshift(toAppend.first);	
  }	
  if (toAppend.last) {	
    arrayCopy.push(toAppend.last);	
  }	
  return arrayCopy;	
}	
append([2, 3, 4], { first: 1, last: 5 }); // => [1, 2, 3, 4, 5]	
append(['Hello'], { last: 'World' });     // => ['Hello', 'World']	
append([8, 16], { first: 4 });            // => [4, 8, 16]

由于 toAppend对象可以省略 first或 last属性,因此必须验证 toAppend中是否存在这些属性。如果属性不存在,则属性访问器值为 undefined

检查 first或 last属性是否是 undefined,在条件为 if(toappendix.first){}和i f(toappendix.last){}中进行验证:

这种方法有一个缺点, undefined, false, null, 0, NaN和 ''是虚值。

在 append() 的当前实现中,该函数不允许插入虚值元素:

append([10], { first: 0, last: false }); // => [10]

0和 false是虚值的。因为 if(toAppend.first){}和 if(toAppend.last){}实际上与 falsy进行比较,所以这些元素不会插入到数组中,该函数返回初始数组 [10]而不会进行任何修改。

以下技巧解释了如何正确检查属性的存在。

技巧3:检查属性是否存在

JS 提供了许多方法来确定对象是否具有特定属性:

  •  

    obj.prop!==undefined:直接与 undefined进行比较

     

  •  

    typeofobj.prop!=='undefined':验证属性值类型

     

  •  

    obj.hasOwnProperty('prop'):验证对象是否具有自己的属性

     

  •  

    'prop'inobj:验证对象是否具有自己的属性或继承属性

     

我的建议是使用 in 操作符,它的语法短小精悍。 in操作符的存在表明一个明确的意图,即检查对象是否具有特定的属性,而不访问实际的属性值。

640?wx_fmt=png

obj.hasOwnProperty('prop')也是一个很好的解决方案,它比 in 操作符稍长,仅在对象自己的属性中进行验证。

涉及与 undefined进行比较剩下的两种方式可能有效,但在我看来, obj.prop!==undefined和 typeofobj.prop!=='undefined'看起来冗长而怪异,并暴露出直接处理 undefined的可疑路径。。

让咱们使用 in操作符改进 append(array,toAppend) 函数:

function append(array, toAppend) {	
  const arrayCopy = array.slice();	
  if ('first' in toAppend) {	
    arrayCopy.unshift(toAppend.first);	
  }	
  if ('last' in toAppend) {	
    arrayCopy.push(toAppend.last);	
  }	
  return arrayCopy;	
}	
append([2, 3, 4], { first: 1, last: 5 }); // => [1, 2, 3, 4, 5]	
append([10], { first: 0, last: false });  // => [0, 10, false]

'first'intoAppend (和 'last'intoAppend)在对应属性存在时为 true,否则为 false。 in操作符的使用解决了插入虚值元素 0和 false的问题。现在,在 [10]的开头和结尾添加这些元素将产生预期的结果 [0,10,false]

技巧4:解构访问对象属性

在访问对象属性时,如果属性不存在,有时需要指示默认值。可以使用 in和三元运算符来实现这一点。

const object = { };	
const prop = 'prop' in object ? object.prop : 'default';	
prop; // => 'default'

当要检查的属性数量增加时,三元运算符语法的使用变得令人生畏。对于每个属性,都必须创建新的代码行来处理默认值,这就增加了一堵难看的墙,里面都是外观相似的三元运算符。

为了使用更优雅的方法,可以使用 ES6 对象的解构。

对象解构允许将对象属性值直接提取到变量中,并在属性不存在时设置默认值,避免直接处理 undefined的方便语法。

实际上,属性提取现在看起来简短而有意义:

const object = {  };	
const { prop = 'default' } = object;	
prop; // => 'default'

要查看实际操作中的内容,让我们定义一个将字符串包装在引号中的有用函数。 quote(subject,config)接受第一个参数作为要包装的字符串。第二个参数 config是一个具有以下属性的对象:

  •  

    char:包装的字符,例如 '(单引号)或 (双引号),默认为 

     

  •  

    skipIfQuoted:如果字符串已被引用则跳过引用的布尔值,默认为 true

     

使用对象析构的优点,让咱们实现 quote()

function quote(str, config) {	
  const { char = '"', skipIfQuoted = true } = config;	
  const length = str.length;	
  if (skipIfQuoted	
      && str[0] === char	
      && str[length - 1] === char) {	
    return str;	
  }	
  return char + str + char;	
}	
quote('Hello World', { char: '*' });        // => '*Hello World*'	
quote('"Welcome"', { skipIfQuoted: true }); // => '"Welcome"'

const{char='", skipifquote = true} = config解构赋值在一行中从 config对象中提取 char和 skipifquote属性。如果 config对象中有一些属性不可用,那么解构赋值将设置默认值: char为 '"', skipifquote为 false

该功能仍有改进的空间。让我们将解构赋值直接移动到参数部分。并为 config参数设置一个默认值(空对象 {}),以便在默认设置足够时跳过第二个参数。

function quote(str, { char = '"', skipIfQuoted = true } = {}) {	
  const length = str.length;	
  if (skipIfQuoted	
      && str[0] === char	
      && str[length - 1] === char) {	
    return str;	
  }	
  return char + str + char;	
}	
quote('Hello World', { char: '*' }); // => '*Hello World*'	
quote('Sunny day');                  // => '"Sunny day"'

注意,解构赋值替换了函数 config 参数。我喜欢这样: quote()缩短了一行。 ={}在解构赋值的右侧,确保在完全没有指定第二个参数的情况下使用空对象。

对象解构是一个强大的功能,可以有效地处理从对象中提取属性。我喜欢在被访问属性不存在时指定要返回的默认值的可能性。因为这样可以避免 undefined以及与处理它相关的问题。

技巧5:用默认属性填充对象

如果不需要像解构赋值那样为每个属性创建变量,那么丢失某些属性的对象可以用默认值填充。

ES6 Object.assign(target,source1,source2,...)将所有可枚举的自有属性的值从一个或多个源对象复制到目标对象中,该函数返回目标对象。

例如,需要访问 unsafeOptions对象的属性,该对象并不总是包含其完整的属性集。

为了避免从 unsafeOptions访问不存在的属性,让我们做一些调整:

  •  

    定义包含默认属性值的 defaults对象

     

  •  

    调用 Object.assign({},defaults,unsafeOptions)来构建新的对象 options。新对象从 unsafeOptions接收所有属性,但缺少的属性从 defaults对象获取。

     

    const unsafeOptions = {	
      fontSize: 18	
    };	
    const defaults = {	
      fontSize: 16,	
      color: 'black'	
    };	
    const options = Object.assign({}, defaults, unsafeOptions);	
    options.fontSize; // => 18	
    options.color;    // => 'black'

unsafeOptions仅包含 fontSize属性。 defaults对象定义属性 fontSize和 color的默认值。

Object.assign() 将第一个参数作为目标对象 {}。目标对象从 unsafeOptions源对象接收 fontSize属性的值。并且人 defaults对象的获取 color属性值,因为 unsafeOptions不包含 color属性。

枚举源对象的顺序很重要:后面的源对象属性会覆盖前面的源对象属性。

现在可以安全地访问 options对象的任何属性,包括 options.color在最初的 unsafeOptions中是不可用的。

还有一种简单的方法就是使用ES6中展开运算符:

const unsafeOptions = {	
  fontSize: 18	
};	
const defaults = {	
  fontSize: 16,	
  color: 'black'	
};	
const options = {	
  ...defaults,	
  ...unsafeOptions	
};	
options.fontSize; // => 18	
options.color;    // => 'black'

对象初始值设定项从 defaults和 unsafeOptions源对象扩展属性。指定源对象的顺序很重要,后面的源对象属性会覆盖前面的源对象。

使用默认属性值填充不完整的对象是使代码安全且持久的有效策略。无论哪种情况,对象总是包含完整的属性集:并且无法生成 undefined的属性。

2.3函数参数

函数参数隐式默认为 undefined

通常,用特定数量的参数定义的函数应该用相同数量的参数调用。在这种情况下,参数得到期望的值

function multiply(a, b) {	
  a; // => 5	
  b; // => 3	
  return a * b;	
}	
multiply(5, 3); // => 15

调用 multiply(5,3)使参数 a和 b接收相应的 5和 3值,返回结果: 5*3=15

在调用时省略参数会发生什么?

function multiply(a, b) {	
  a; // => 5	
  b; // => undefined	
  return a * b;	
}	
multiply(5); // => NaN

函数 multiply(a,b){}由两个参数 a和 b定义。调用 multiply(5)用一个参数执行:结果一个参数是 5,但是 b参数是 undefined

技巧6:使用默认参数值

有时函数不需要调用的完整参数集,可以简单地为没有值的参数设置默认值。

回顾前面的例子,让我们做一个改进,如果 b参数未定义,则为其分配默认值 2

function multiply(a, b) {	
  if (b === undefined) {	
    b = 2;	
  }	
  a; // => 5	
  b; // => 2	
  return a * b;	
}	
multiply(5); // => 10

虽然所提供的分配默认值的方法有效,但不建议直接与 undefined值进行比较。它很冗长,看起来像一个hack .

这里可以使用 ES6 的默认值:

function multiply(a, b = 2) {	
  a; // => 5	
  b; // => 2	
  return a * b;	
}	
multiply(5);            // => 10	
multiply(5, undefined); // => 10

2.4 函数返回值

隐式地,没有 return语句,JS 函数返回 undefined

在JS中,没有任何 return语句的函数隐式返回 undefined

function square(x) {	
  const res = x * x;	
}	
square(2); // => undefined

square() 函数没有返回计算结果,函数调用时的结果 undefined

当 return语句后面没有表达式时,默认返回 undefined

function square(x) {	
  const res = x * x;	
  return;	
}	
square(2); // => undefined

return; 语句被执行,但它不返回任何表达式,调用结果也是 undefined

function square(x) {	
  const res = x * x;	
  return res;	
}	
square(2); // => 4

技巧7:不要相信自动插入分号

JS 中的以下语句列表必须以分号 (;)结尾:

  •  

    空语句

     

  •  

    let,const,var,import,export声明

     

  •  

    表达语句

     

  •  

    debugger 语句

     

  •  

    continue 语句, break 语句

     

  •  

    throw 语句

     

  •  

    return 语句

     

如果使用上述声明之一,请尽量务必在结尾处指明分号

function getNum() {	
  let num = 1; 	
  return num;	
}	
getNum(); // => 1

let 声明和 return 语句结束时,强制性写分号

当你不想写这些分号时会发生什么?例如,咱们想要减小源文件的大小。

在这种情况下,ECMAScript 提供自动分号插入(ASI)机制,为你插入缺少的分号

ASI 的帮助下,可以从上一个示例中删除分号
function getNum() { // Notice that semicolons are missing let num = 1 return num } getNum() // => 1

上面的代码是有效的JS代码,缺少的分号ASI会自动为我们插入。

乍一看,它看起来很 nice。ASI 机制允许你少写不必要的分号,可以使JS代码更小,更易于阅读。

ASI 创建了一个小而烦人的陷阱。当换行符位于 return和 return\n expression之间时,ASI 会在换行符之前自动插入分号( return;\n expression)。

函数内部 return; ?即该函数返回 undefined。如果你不详细了解ASI的机制,则意外返回的 undefined会产生意想不到的问题。

来 getPrimeNumbers()调用返回的值:

function getPrimeNumbers() {	
  return 	
    [ 2, 3, 5, 7, 11, 13, 17 ]	
}	
getPrimeNumbers() // => undefined

在 return语句和数组之间存在一个换行,JS 在 return后自动插入分号,解释代码如下:

function getPrimeNumbers() {	
  return; 	
  [ 2, 3, 5, 7, 11, 13, 17 ];	
}	
getPrimeNumbers(); // => undefined

return; 使函数 getPrimeNumbers() 返回 undefined而不是期望的数组。

这个问题通过删除 return和数组文字之间的换行来解决:

function getPrimeNumbers() {	
  return [ 	
    2, 3, 5, 7, 11, 13, 17 	
  ];	
}	
getPrimeNumbers(); // => [2, 3, 5, 7, 11, 13, 17]

我的建议是研究自动分号插入的确切方式,以避免这种情况。

当然,永远不要在 return和返回的表达式之间放置换行符。

2.5 void 操作符

void<expression>计算表达式无论计算结果如何都返回 undefined 。

void 1;                    // => undefined	
void (false);              // => undefined	
void {name: 'John Smith'}; // => undefined	
void Math.min(1, 3);       // => undefined

void操作符的一个用例是将表达式求值限制为 undefined,这依赖于求值的一些副作用。

3.未定义的数组

访问越界索引的数组元素时,会得到 undefined 。

const colors = ['blue', 'white', 'red'];	
colors[5];  // => undefined	
colors[-1]; // => undefined

colors数组有3个元素,因此有效索引为 0,1和 2

因为索引 5和 -1没有数组元素,所以访问 colors[5]和 colors[-1]值为 undefined

JS 中,可能会遇到所谓的稀疏数组。这些数组是有间隙的数组,也就是说,在某些索引中,没有定义元素。

当在稀疏数组中访问间隙(也称为空槽)时,也会得到一个 undefined

下面的示例生成稀疏数组并尝试访问它们的空槽

const sparse1 = new Array(3);	
sparse1;       // => [<empty slot>, <empty slot>, <empty slot>]	
sparse1[0];    // => undefined	
sparse1[1];    // => undefined	
const sparse2 = ['white',  ,'blue']	
sparse2;       // => ['white', <empty slot>, 'blue']	
sparse2[1];    // => undefined

使用数组时,为了避免获取 undefined,请确保使用有效的数组索引并避免创建稀疏数组。

4. undefined和null之间的区别

一个合理的问题出现了: undefined和 null之间的主要区别是什么?这两个特殊值都表示为空状态。

主要区别在于 undefined表示尚未初始化的变量的值, null表示故意不存在对象。

让咱们通过一些例子来探讨它们之间的区别。

number 定义了但没有赋值。

let number;	
number; // => undefined

number 变量未定义,这清楚地表明未初始化的变量。

当访问不存在的对象属性时,也会发生相同的未初始化概念

const obj = { firstName: 'Dmitri' };	
obj.lastName; // => undefined

因为 obj中不存在 lastName属性,所以JS正确地将 obj.lastName计算为 undefined

在其他情况下,你知道变量期望保存一个对象或一个函数来返回一个对象。但是由于某些原因,你不能实例化该对象。在这种情况下, null是丢失对象的有意义的指示器。

例如, clone()是一个克隆普通JS对象的函数,函数将返回一个对象

function clone(obj) {	
  if (typeof obj === 'object' && obj !== null) {	
    return Object.assign({}, obj);	
  }	
  return null;	
}	
clone({name: 'John'}); // => {name: 'John'}	
clone(15);             // => null	
clone(null);           // => null

但是,可以使用非对象参数调用 clone()15或 null(或者通常是一个原始值, null或 undefined)。在这种情况下,函数不能创建克隆,因此返回 null—— 一个缺失对象的指示符。

typeof操作符区分了这两个值

typeof undefined; // => 'undefined'	
typeof null;      // => 'object'

严格相等运算符 ===可以正确区分 undefined和 null

let nothing = undefined;	
let missingObject = null;	
nothing === missingObject; // => false

总结

undefined的存在是JS的允许性质的结果,它允许使用:

  • 未初始化的变量

  • 不存在的对象属性或方法

  • 访问越界索引的数组元素

  • 不返回任何结果的函数的调用结果

大多数情况下直接与 undefined进行比较是一种不好的做法。一个有效的策略是减少代码中 undefined关键字的出现:

  •  

    减少未初始化变量的使用

     

  •  

    使变量生命周期变短并接近其使用的位置

     

  •  

    尽可能为变量分配初始值

     

  •  

    多敷衍 const 和 let

     

  •  

    使用默认值来表示无关紧要的函数参数

     

  •  

    验证属性是否存在或使用默认属性填充不安全对象

     

  •  

    避免使用稀疏数组

     

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@大迁世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值