当前位置: 首页 > 科技观察

小心JavaScript的这三个特性

时间:2023-03-18 02:25:16 科技观察

JavaScript已经存在了相当长的一段时间(大约26年),并且这门语言在这段时间里发展了很多。这种演变大部分是有目的的,尤其是在最近的迭代中,并且开发人员社区已经成功地影响了其中的一些变化,使JavaScript成为一种非常灵活且易于使用的语言。然而,经过多年的发展,可以说仍然有一些过时的功能残余没有被删除,但实际上没有任何作用(或者更确切地说,对于它们的原始目的来说不是很有效)。以下是您应该避免使用的三个JavaScript功能,即使它们在运行时仍然可用。void运算符您可能在某个时候看到过void运算符的存在。过去,每当您单击会触发JavaScript函数的链接时,您都会添加href="javascript:void(0)"以确保不会触发默认行为(即页面跳转)。但这到底是什么意思呢?void运算符是一种在JavaScript中生成未定义值的方法。没错,它接受任何表达式,每次都返回undefined。我知道您在想什么:为什么不直接使用已经存在的undefined关键字呢?好吧,正如您所见,在ECMAScript5之前,undefined关键字不是常量值。是的,你可以定义undefined,你想想,这不就是我们以前想做的吗?当然,这样做是没有意义的,这就是为什么它最终被重新定义为一个常量值,不可改变的原因。但是,由于您可以更早地更改它,void将允许您访问undefined,即使该常量不再有任何作用。事实上,一个好方法是重新定义只属于你的命名空间的常量,避免第3方库的任何问题,方法是创建你自己的IIFE,其中一个参数确实未定义,就像这样:(function(window,undefined){})(window,void(0))当然,void运算符今天仍有用处,但不是必需的。例如,在今天的JavaScript中,最好的用例是帮助避免单行箭头函数的意外返回。您可能知道,单行箭头函数将返回该行的结果,即使您没有显式使用return语句也是如此。constdouble=x=>x*2;constcallAfunction=()=>myFunction();这两个函数都会返回一些东西。显然,对于一个double函数,你期望它返回一个值,而另一个可能没有,你可能只想用它来调用另一个函数(即myFunction()),但你对它的结果值不感兴趣。所以你可以这样做:constcallAfunction=()=>voidmyFunction();这将立即覆盖返回值并确保您的调用仅返回未定义的。对我来说,这种行为提供了最小的好处,使void在当今的JavaScript时代变得无用。我建议您避免使用它并将其保持在弃用状态。With语句这是JavaScript的内置结构之一,但您可能从未听说过它,因为它并未真正普及。事实上,即使官方MDN文档也不鼓励您使用它,因为它会导致代码非常混乱。with语句允许扩展给定语句的作用域链。换句话说,它允许您将表达式注入到给定语句的范围内,理想地简化了该语句。这是一个示例,因此您可以看到我要说的内容:functiongreet(user){with(user){console.log(`Hellothere${name},howareyouandyour${kids.length}kidstoday?`)}}greet({name:"Fernando",kids:["Brian","Andrew"]})请注意greet函数中with语句的魔力。这是一个显示表达式的快乐路径的基本示例。但是,让我们看一下事情变得有点复杂的另一种情况:functiongreet(user,message){with(user){console.log(`Hey${name},hereisamessageforyou:${message}`)}}greet({name:"Fernando"},"Yougot2emails")greet({name:"Fernando",message:"Unrelatedmessage"},"yougotemail")复制代码你认为这个执行方法的输出会是什么?嘿费尔南多,这是给你的一条消息:你有2封电子邮件嘿费尔南多,这是给你的一条消息:无关消息复制代码因为你在传入的对象中添加了一个同名的属性,你无意中覆盖了函数的第二个参数。我想补充一点,这是完全正常的,因为人们永远不会期望两者处于同一范围级别。然而,多亏了with,我们混合了两个范围,结果不太理想。这都是为了避免使用with,虽然它看起来是节省代码大小的好方法,但您的代码很快就会变得非常复杂,其他人(或您在两周内)将很难理解您的代码造成精神负担。标签如果你足够早地学习编程(像我一样),你已经体验过对其他语言如C的go-to语句的仇恨。那太糟糕了,那是一个在当时有意义的特性,但最终随着对于同一问题的更新解决方案,它变得如此过时和糟糕以至于它成为一种反模式。所以JavaScript必须实现它。Go-to语句是一种在代码中的任何位置放置标记并从其他地方跳转到那里的方法。您可以在函数中间或IF语句内部跳转,它就像一个神奇的入口点,可通往您代码中的任何位置。我相信你能看出这可能是个问题,它的功能太多,灵活性太大,当然我们也错过了使用它的机会。然而,JavaScript实现了一个相似但不完全相同的结构:labels标签。JavaScript中的标记语句是您放在语句之前的标记,然后您可以中断或继续。请注意,没有更多的go-tos,这是一个很好的优势。你可以这样写:label1:{console.log(1)letcondition=trueif(condition){breaklabel1}console.log(2)}console.log("end")复制代码,输出将是:1endCopy当然是代码,这个例子看起来很像一个if..else语句。你完全可以争辩说它看起来并没有那么糟糕。但是,您打破了正常的代码流程并跳过了语句。如果那是你想要的,那么使用if..else就可以减少精神负担。当我们将标签与循环和continue语句的交互包括在内时,标签的问题变得更加明显。乐蒂,j;loop1:for(i=0;i<10;i++){loop2:for(j=0;j<10;j++){if(j==3&&i==2){continueloop2;}控制台。log({i,j})if(j%2==0){continueloop1;}}}你能在脑海中运行上面的代码并告诉我确切的输出吗?这并非不可能,但需要一些时间。上面的脚本会打印:{i:0,j:0}{i:1,j:0}{i:2,j:0}{i:3,j:0}{i:4,j:0}{i:5,j:0}{i:6,j:0}{i:7,j:0}{i:8,j:0}{i:9,j:0}从本质中复制代码上面说到,第二个if在0处计算为真,所以continue语句影响外循环,导致它移动到下一个索引值,这反过来又重置内循环,导致它回到0,同样它一直在发生并重复了10次。第一个if,以防你想知道,永远不会评估为真,因为j永远不会达到0以外的任何值。标签可能是一个棘手的小家伙,即使你把它们弄对了并且从解释器的角度来看它们是有意义的,你应该为人类而不是机器编写代码。其他人会来阅读它(即使是三周后的你),并且在他们看到标签的那一刻就会永远恨你。当然,他们会花更多时间了解您的代码的基本流程,但现在这是次要问题。不要误会我的意思,我喜欢JavaScript作为一种语言,自18年前开始从事Web开发以来,我一直以不同的方式与它进行交互。我已经看到这门语言像美酒一样随着时间的推移而变得更好。但是,如果我说该语言没有一些我不喜欢的阴暗角落,那我就是在撒谎。而这三个特征恰恰表明了这一点。好消息是,以我多年的经验,我还没有看到带有标签(Label)的实现和部署到生产中。这并不是说没有这样的案例,只是我从未见过,这让我认为再多的代码审查也不会让它们通过。你见过现代JavaScript中使用的这些特性吗?