镜头矫正 棋盘矫正_矫正强迫,而不是症状

镜头矫正 棋盘矫正

TL;DR

TL; DR

Your complaints of x == y behaviors being weird, buggy, or downright broken have all blamed == as the culprit. No, it's really not. == is pretty helpful, actually.

您对x == y行为怪异,越野车或彻头彻尾的损坏的抱怨都归咎于==是罪魁祸首。 不,真的不是。 ==实际上很有帮助。

The problems you're having are not with the == operator itself, but with the underlying values and how they coerce to different types, especially in the weird corner cases.

您遇到的问题不是==运算符本身,而是基本值以及它们如何强制转换为不同类型,尤其是在奇怪的极端情况下。

Instead of fixing your problems by avoiding == (and always using ===), we should focus our efforts on either avoiding—or fixing!—the corner case value coercions. Indeed, those are where all the WTFs really come from.

与其避免避免== (始终使用=== )来解决问题,我们不应该将重点放在避免或解决极端情况下的强制值上。 确实,这些是所有WTF真正来自的地方。

Quick Jump:

快速跳转:

  • First Section - Yay Coercion :)

    第一部分-Yay Coercion :)

  • Second Section - Boo Coercion :(

    第二部分-Boo强制:(

  • Third Section - Coercion Fixed!

    第三节-强制修正!

This post announces the release of the latest book in my You Don't Know JS book series, YDKJS: Types & Grammar, which can be read for free online!

这篇文章宣布了我的《 你不知道JS 》丛书《 YDKJS:Types&Grammar》中的最新书籍的发布,可以在线免费阅读!

Types & Grammar includes a Foreword by our own amazing David Walsh, and is also available for purchase through O'Reilly and other sellers, like Amazon. If there's any part of this post that you enjoy, check out Types & Grammar for lots more in-depth coverage of JS types, coercion, and grammar rules.

Types&Grammar包含了我们自己惊人的David Walsh的序言 ,也可以通过O'Reilly和其他卖家(例如Amazon)购买 。 如果您喜欢这篇文章的任何部分,请查看Types&Grammar,以更深入地了解JS类型,强制和语法规则。



Warning: Herein lies a controversial, and really long, discussion that's likely to upset you. This post defends and endorses the oft-hated JavaScript coercion mechanism. Everything you've ever heard or felt about what's wrong with coercion is going to be challenged by what I lay out here. Make sure you set aside plenty of time to chew on this article.

警告:这是一个有争议的, 很长的讨论,很可能会让您不高兴。 这篇文章捍卫并认可了经常被讨厌JavaScript 强制机制。 您所听到或感受到的有关强制性问题的所有内容都会受到我在这里所说的一切的挑战。 确保您留出大量时间阅读本文。

胁迫已经死了吗? (Isn't Coercion Already Dead?)

Why on earth am I talking about—let alone, defending and endorsing!—a mechanism that has been so universally panned as being awful, evil, magical, buggy, and poor language design? Hasn't the boat long since sailed? Haven't we all just moved on and left coercion in the dust? If Crockford says it's bad, then it must be.

为什么我到底在谈论(更不用说捍卫和认可!)这种被普遍批评为可怕,邪恶,神奇,越野车和不良语言设计的机制? 船航行了很久了吗? 难道我们都没有继续前进,在尘埃中留下了胁迫吗? 如果克罗克福德说这很糟糕,那一定是。

Ummm... no. On the contrary, I think coercion hasn't ever been given a fair shot, because it's never been talked about or taught correctly. It's not surprising that you hate coercion when all you've ever seen of it is the completely wrong way to understand it.

嗯...不 相反,我认为从来没有对胁迫进行过公正的评判,因为从来没有对胁迫进行过正确的讨论或教导。 当您所经历的一切都是完全错误的理解方式时,您讨厌胁迫就不足为奇了。

For better or worse, nearly the entire reason for the Types & Grammar book, as well as many of my conference talks, is to make just this case.

不管是好是坏,《 Types&Grammar》一书以及我的许多会议演讲的几乎全部原因都在于此。

But maybe I'm wasting my time trying to convince you. Maybe you'll never change your mind.

但是也许我在浪费时间试图说服您。 也许您永远都不会改变主意。

In fact, Mr. Crockford has something to say directly to that point:

实际上,克罗克福德先生有话要直接说:

演示地址

Douglas Crockford - Are they gone? "The Better Parts", Nordic.js 2014

道格拉斯·克罗克福德(Douglas Crockford)-他们走了吗? “更好的零件”,Nordic.js 2014

And the reason these things take a generation is because ultimately we do not change people's minds. We have to wait for the previous generation to retire or die before we can get critical mass on the next idea. So it's like we look around: "Are they gone?"

这些事情需要一代人的时间是因为我们最终不会改变人们的想法。 我们必须等待上一代人退休或死亡,才能在下一个想法上获得足够的支持。 就像我们环顾四周:“他们走了吗?”

--Douglas Crockford

-道格拉斯·克罗克福德

So, is he right? More pointedly, could coercion be "the next idea" that just hasn't had enough of the old static typing generation die off to be given a fair and objective examination?

所以,对吗? 更尖锐地讲,强制是不是仅仅没有足够的旧静态类型生成器就死掉以进行公平客观检查的“下一个想法”?

I think maybe so.

我想可能是这样。

Essentially, I've been looking around at coercion naysayers for years, asking, "Are they gone, yet?"

从本质上讲,我多年来一直在寻找强迫反对者,他们问:“他们走了吗?”

不便的真相 (An Inconvenient Truth)

"Do As I Say, Not As I Do."

“按我说的做,不如我做。”

Your parents told you that when you were a kid, and it annoyed you back then, didn't it? I bet it would annoy you today if someone in our profession held that stance.

你的父母告诉你,当你还是个孩子的时候,这让你很烦,不是吗? 我敢打赌,如果我们这个行业的某人持这种立场,今天会让您烦恼。

So, when you hear Douglas Crockford speak negatively about coercion, you certainly assume that he similarly avoids using it in his own code. Right? Ummm... how do I put this? How do I break it to you?

因此,当您听到道格拉斯·克罗克福德(Douglas Crockford)对强制施加负面评价时,您当然会认为他同样避免在自己的代码中使用它。 对? 嗯...我怎么放这个? 我该如何将它交给你?

Crockford uses coercions. There, I said it. Don't believe me?

克罗克福德使用胁迫。 在那里,我说了。 不相信我吗

// L292 - 294 of json2.js
for (i = 0; i < length; i += 1) {
    partial[i] = str(i, value) || 'null';
}


json2.js, L293

json2.js,L293

Do you see the coercion? str(..) || 'null'. How does that work?

你看到强迫了吗? str(..) || 'null' str(..) || 'null' 。 这是如何运作的?

For the || operator, the first operand (str(..)) is implicitly coerced to boolean if it's not already one, and that true / false value is then used for the selection of either the first operand (str(..)) or the second ('null'). Read more about how || and && work and the common idiomatic uses of those operators.

对于|| 运算符,如果第一个操作数( str(..) )还不是boolean则将其隐式强制转换为boolean值,然后使用true / false值选择第一个操作数( str(..) )或第二个操作数( 'null' )。 了解有关||更多信息 &&工作以及这些运算符的惯用用法。

In this case, the first operand is definitely not expected to be a boolean, as he earlier documents the str(..) function this way:

在这种情况下,绝对不希望第一个操作数是boolean ,因为他先前以这种方式记录了str(..)函数:

function str(key, holder) {

// Produce a string from holder[key].

..


So, his own code is relying on an implicit coercion here. The very thing he's spent a decade lecturing us is bad. And why? Why does he use it?

因此,他自己的代码在此依赖于隐式强制。 他花了十年的时间教导我们的事情很糟糕。 又为什么呢 他为什么使用它?

More importantly, why do you use such idioms? Because I know you do. Most JS devs use that || operator idiom for setting a default value to a variable. It's super useful.

更重要的是,为什么使用这样的习语? 因为我知道你做。 大多数JS开发人员都使用|| 用于将默认值设置为变量的运算符惯用法。 超级有用。

He could have instead written that code as so:

他本可以这样编写该代码:

tmp = str(i, value);
partial[i] = (tmp !== '') ? tmp : 'null';


This avoids coercion entirely. The !== operator (in fact all the equality operators, including == and !=) always returns a boolean from the equality check. The ? : operator first checks the first operand, then picks either the second (tmp) or the third ('null'). No coercion.

这完全避免了胁迫。 !==运算符(实际上是所有相等运算符,包括==!= )始终从相等性检查中返回boolean? : ? :运算符首先检查第一个操作数,然后选择第二个( tmp )或第三个( 'null' )。 没有胁迫

So why doesn't he do this?

那他为什么不这样做呢?

Because the str(..) || 'null' idiom is common, shorter/simpler to write (no need for a temporary variable tmp), and generally easy'ish to understand, certainly as compared to the non-coercion form.

因为str(..) || 'null' 与非强制形式相比, str(..) || 'null'惯用语是常见的,编写起来更短/更简单(不需要临时变量tmp ),并且通常易于理解。

In other words, coercion, especially implicit coercion, has usages where it actually improves the readability of our code.

换句话说,强制,尤其是隐式强制,在实际使用中可以提高代码的可读性。

OK, so that's just one isolated exception he made, right? Not quite.

好,那只是他提出的一个孤立的例外,对吧? 不完全的。

In just that one "json2.js" file, here's a (not necessarily complete) list of places Crockford uses either explicit or implicit coercions: L234, L275, L293, L301-302, L310, L316-317, L328-329, L340-341, L391, L422, and L442.

在一个“ json2.js”文件中,这是克罗克福德使用显式隐式强制的地方(不一定完整)列表: L234 , L275 , L293 , L301-302 , L310 , L316-317 , L328-329 , L340 -341 , L391 , L422和L442 。

Oh, wait. This is just the old "json2.js" library. That's unfair, right? How about his own JSLint library, which he still maintains (EDIT: he's soon releasing an update for ES6): L671, L675, L713, L724, L782, ... You get the point, right?

等一下。 这只是旧的“ json2.js”库。 那不公平,对吧? 他仍然维护着自己的JSLint库(编辑:他即将发布ES6的更新 ) 怎么 样 : L671 , L675 , L713 , L724 , L782 ……...你明白了吧?

Doug Crockford uses coercion to make his code more readable. I applaud him for it.

Doug Crockford使用强制使他的代码更具可读性。 为此我称赞他。

Ignore what he says about coercion being evil or bad. It's useful, and he proves that with his code no matter what headline-grabbing slides he puts up in his conference talks.

忽略他所说的强迫是邪恶还是邪恶的话。 这很有用,并且他用自己的代码证明了他在会议演讲中张贴的任何标题吸引的幻灯片。

但是... ==是邪恶的 (But... == Is The Evil)

OK, you're right, there's not a single instance of == in his code. And whenever he derides coercion, he's almost certainly talking about == specifically.

好的,您是对的,他的代码中没有==的单个实例。 每当他放弃强制时,他几乎肯定是在专门谈论==

So am I being unfair by highlighting a bunch of non-== coercions? Actually, I'd argue it's he that's being unfair, by constantly equating == with coercion (pun intended, of course!). He's not alone. I'd say almost all JS developers do the same. When they hear "coercion", they inevitably invoke ==.

那么,我强调一堆非==强迫会不公平吗? 实际上,我认为通过不断 == 等同强制 (当然是双关语!),是他不公平。 他并不孤单。 我会说几乎所有的JS开发人员都这样做。 当他们听到“胁迫”时,他们不可避免地调用==

Coercion is a mechanism that is allowed to work when == is used, and prevented from being used when === is used. But that realization should make it clear that == and coercion are orthogonal concerns. In other words, you can have complaints about == that are separate from complaints about coercion itself.

强制转换是一种机制,当使用==时允许工作,而在使用===时禁止使用。 但是,这种认识应该明确表明==和强制是正交的问题。 换句话说,您可能对==投诉与对强制本身的投诉是分开的。

I'm not just trying to nitpick here. This is super important to understand the rest of this post: we have to consider coercion separately from considering ==. Call == "equality coercion" if you like, but don't just conflate it with coercion itself.

我不只是想在这里挑剔。 这对于理解本文的其余部分非常重要:我们必须将强制与考虑==分开考虑。 如果愿意,请致电== “平等强制”,但不要仅将其与强制本身混为一谈。

By and large, I think almost all complaints made against == are actually issues with coercion, and we're going to get to those later. We're also going to come back to ==, and look at it a bit more. Keep reading!

总的来说,我认为几乎所有针对==投诉实际上都是与强制有关的问题,我们将在稍后讨论。 我们还将返回== ,并对其进行更多的研究。 继续阅读!

需要强制吗? (Need Coercion?)

Coercion is what happens in JavaScript when you need to go from one type (like string) to another (like boolean). This is not unique to JS though. Every programming language has values of different types, and most programs require you to convert from one to the other. In statically typed (type enforced) languages, conversion is often called "casting", and it's explicit. But the conversion happens nonetheless.

当您需要从一种类型 (如string )转换为另一种类型 (如boolean )时,JavaScript中会发生强制转换。 但是,这并不是JS独有的。 每种编程语言都有不同类型的值,并且大多数程序都要求您将一种转换为另一种。 在静态类型(强制类型)语言中,转换通常称为“强制转换”,它是显式的。 但是转换仍然发生。

JavaScript coercion can either be intentional and explicit, or it can happen implicitly as a side-effect.

JavaScript强制可以是故意的和显式的 ,也可以作为副作用隐式发生。

But there is just hardly any non-trivial JS programs out there that don't at some point or another rely on coercion of some form. When people hate on coercion, they're usually hating on implicit coercion, but explicit coercion is usually seen as OK.

但是,几乎没有任何不平凡的JS程序在某个时候或另一个不依赖某种形式的强制。 当人们讨厌强制时,他们通常讨厌隐式强制,但是通常将显性强制视为可以。

var x = 42;

var y = x + "";     // implicit coercion!
y;                  // "42"

var z = String(x);  // explicit coercion!
z;                  // "42"


Even for those who are publicly against implicit coercion, for some reason they're usually just fine with the x + "" form here. I don't frankly understand why this implicit coercion is OK and many others aren't.

即使对于那些公开反对隐式强制的人,由于某种原因,通常使用x + ""形式也可以 。 我不坦率地理解为什么这种 隐式强制是可以的,而其他许多方面却不是。

You can focus on deciding whether you prefer explicit or implicit coercion forms, but you cannot reasonably argue that most JS programs can be written without any coercion at all.

您可以集中精力确定是偏爱显式还是隐含的强制形式,但是您不能合理地认为大多数JS程序可以完全不强制使用。

An awful lot of developers say we shouldn't have coercion, but they almost never take the time to think through all the corner cases that would present. You can't just say the mechanism shouldn't exist without having an answer to what you should do instead.

很多开发人员说我们不应该强制执行,但是他们几乎从来没有花时间思考所有可能出现的情况。 您不能只说不应该存在该机制,而不能回答应该做什么。

This article in a sense is an exercise in that pursuit, to examine just how sensible such a position is. Hint: not much.

从某种意义上说,这篇文章是这种追求的一种实践,旨在研究这种立场的合理性。 提示:不多。

为什么要强制? (Why Coercion?)

The case for coercion is much broader than I will fully lay out here. Check out Chapter 4 of Types & Grammar for a lot more detail, but let me try to briefly build on what we saw earlier.

强迫的理由比我在这里要完整阐述的要广泛得多。 请查看《 类型与语法 》的第4章以获取更多详细信息 ,但让我尝试简要介绍一下我们先前所看到的内容。

In addition to the x || y (and x && y) idioms, which can be quite helpful in expressing logic in a simpler way than the x ? x : y form, there are other cases where coercion, even implicit coercion, is useful in improving the readability and understandability of our code.

除了x || y x || y (和x && y )的惯用法,比用x ? x : y更简单的方式来表达逻辑很有帮助x ? x : y x ? x : y形式,在其他情况下,强制甚至隐式强制在提高我们代码的可读性和可理解性方面很有用。

// no coercion
if (x === 3 || x === "3") {
    // do something
}

// explicit coercion
if (Number(x) == 3) {
    // do something
}

// implicit coercion
if (x == 3) {
    // do something
}


The first form of the conditional skirts coercion entirely. But it's also longer and more "complicated", and I would argue introduces extra details here which might very well be unnecessary.

条件裙的第一种形式完全是强制性的。 但是它也更长,更“复杂”,我认为这里引入了额外的细节,这些细节很可能是不必要的。

If the intent of this code is to do something if x is the three value, regardless of if it's in its string form or number form, do we actually need to know that detail and think about it here? Kinda depends.

如果此代码的目的是在x三个值的情况下做某事 ,而不管它是string形式还是number形式,那么我们是否真的需要了解该细节并在这里考虑一下? 有点取决于。

Often, no. Often, that fact will be an implementation detail that's been abstracted away into how x got set (from a web page form element, or a JSON response, or ...). We should leave it abstracted away, and use some coercion to simplify this code by upholding that abstraction.

通常不会。 通常,该事实通常是一个实现细节,已被抽象到如何设置x (从网页表单元素或JSON响应或...)。 我们应该将其抽象化,并采用某种强制性,通过坚持这种抽象来简化此代码。

So, is Number(x) == 3 better or worse than x == 3? In this very limited case, I'd say it's a toss-up. I wouldn't argue with those who prefer the explicit form over the implicit. But I kinda like the implicit form here.

那么, Number(x) == 3x == 3好还是差? 在这种非常有限的情况下,我会说这是一个折腾。 我不会与那些喜欢显式形式而不是 形式的人争论。 但是我有点喜欢这里的隐式形式。

Here's another example I like even more:

这是我更喜欢的另一个示例:

// no coercion
if (x === undefined || x === null) {
    // do something
}

// implicit coercion
if (x == null) {
    // do something
}


The implicit form works here because the specification says that null and undefined are coercively equal to each other, and to no other values in the language. That is, it's perfectly safe to treat undefined and null as indistinguishable, and indeed I would strongly recommend that.

隐式形式之所以在这里起作用,是因为该规范指出nullundefined 强制彼此相等 ,并且在语言中不等于其他值。 也就是说,将undefinednull视为不可区分是绝对安全的,确实,我强烈建议您这样做。

The x == null test is completely safe from any other value that might be in x coercing to null, guaranteed by the spec. So, why not use the shorter form so that we abstract away this weird implementation detail of both undefined and null empty values?

x == null测试对于从x强制转换为null任何其他值(由规范保证)是完全安全的。 因此,为什么不使用较短的形式,以便我们抽象出undefinednull 空值的怪异实现细节?

Using === prevents you from being able to take advantage of all the benefits of coercion. And you've been told that's the answer to all coercion problems, right?

使用===会使您无法利用胁迫的所有好处。 有人告诉您这是所有强迫问题的答案,对吗?

Here's a dirty secret: the <, <=, > and >= comparison operators, as well as the +, -, *, and / math operators, have no way to disable coercion. So, just simply using === doesn't even remotely fix all your problems, but it removes the really useful instances of the coercive equality == tool.

这是一个肮脏的秘密: <<=>>=比较运算符,以及+-*/数学运算符, 都无法禁用强制。 因此,仅使用===甚至无法远程解决所有问题,但是它删除了强制相等==工具的真正有用的实例。

If you hate coercion, you've still got to contend with all the places where === can't help you. Or, you could embrace and learn to use coercion to your advantage, so that == helps you instead of giving you fits.

如果您讨厌强迫,您仍然必须与===不能帮助您的所有地方竞争。 或者,您可以拥抱并学习使用强制手段来发挥自己的优势,因此==可以帮助您,而不是让您适应。

This post has a lot more to get to, so I'm not going to belabor any further the case for coercion and ==. Again, Chapter 4, Types & Grammar covers the topic in a lot more detail if you're interested.

这篇文章有很多去,所以我不会痛打任何进一步胁迫和的情况下== 。 同样,如果您感兴趣, 第4章“ 类型和语法 ”将更详细地介绍该主题。

两种价值观的故事 (A Tale Of Two Values)

I've just extolled why coercion is so great. But we all know coercion has some ugly parts to it—there's no denying it. Let's get to the pain, which really is the whole point of this article.

我刚刚赞扬了为什么强制如此之大。 但是我们都知道,强制措施有一些丑陋的部分,这是无可否认的。 让我们开始痛苦,这实际上是本文的重点。

I'm gonna make a perhaps dubious claim: the root of most evil in coercion is Number("") resulting in 0.

我要提出一个可能令人怀疑的主张: 强制中最邪恶的根源是 Number("")导致0

You may be surprised to see just how many other coercion cases come down to that one. Yeah, yeah, there are others, too. We'll get there.

您可能会惊讶地发现还有多少其他强制性案件归结于该案件。 是的,还有其他人。 我们到达那里。

I said this earlier, but it bears repeating: all languages have to deal with type conversions, and therefore all languages have to deal with corner cases producing weird results. Every single one.

我之前说过这一点,但是需要重复:所有语言都必须处理类型转换,因此所有语言都必须处理产生奇怪结果的极端情况。 每一个。

// C
char s[] = "";
int num = atoi(s);
printf("%d",num);                   // 0

// Java
String s = "";
Integer num = Integer.valueOf(s);
System.out.println(num);            // java.lang.NumberFormatException


C chooses to convert "" to 0. But Java complains and throws an exception. JavaScript is clearly not uniquely plagued by this question.

C选择将""转换为0 。 但是Java抱怨并引发异常。 显然,JavaScript不是唯一受此问题困扰的问题。

For better or worse, JavaScript had to make decisions for all these sorts of corner cases, and frankly, some of those decisions are the real source of our present troubles.

不管是好是坏,JavaScript都必须为所有这些极端情况做出决策,坦率地说,其中一些决策是我们当前麻烦的真正根源。

But in those decisions was an undeniable—and I think admirable—design philosophy. At least in the early days, JS chose to lean away from the "let's just throw an exception every time you do something weird" philosophy, which you get from languages like Java. That's the "garbage in, garbage out" mindset.

但是在这些决定中,是不可否认的(我认为是令人钦佩的)设计哲学。 至少在早期,JS选择摆脱了“让您每次做一些奇怪的事情时都抛出异常”的理念,这种理念来自Java之类的语言。 那就是“垃圾进,垃圾出”的心态。

Put simply, JS tries to make the best guess it can of what you asked it to do. It only throws an error in the extreme cases where it couldn't come up with any reasonable behavior. And many other languages have chosen similar paths. JS is more like "garbage in, some recycled materials out".

简而言之,JS会尽力猜测您要执行的操作。 它只会在无法提出任何合理行为的极端情况下引发错误。 许多其他语言也选择了类似的路径。 JS更像是“垃圾进来,一些可回收的材料出去”。

So when JS was considering what to do with strings like "", " ", and "\n\n" when asked to coerce them to a number, it chose roughly: trim all whitespace; if only "" is left, return 0. JS doesn't throw exceptions all over the place, which is why today most JS code doesn't need try..catch wrapped around nearly every single statement. I think this was a good direction. It may be the main reason I like JS.

因此,当JS考虑要处理诸如""" ""\n\n"类的字符串时,将其强制转换为数字时,它大致选择: 修剪所有空白; 如果只剩下"" ,则返回0 。 JS不会到处抛出异常,这就是为什么今天大多数JS代码不需要try..catch包裹几乎每条语句。 我认为这是一个很好的方向。 这可能是我喜欢JS的主要原因。

So, let's consider: is it reasonable for "" to become 0? Is your answer any different for " " or "\n\n"? If so, why, exactly? Is it weird that both "" and "0" coerce to the same 0 number? Eh. Seems fishy to me.

因此,让我们考虑: ""变为0是否合理? 您对" ""\n\n"回答是否有所不同? 如果是这样,为什么呢? """0"强制为相同的0数字是否很奇怪? 嗯 对我来说似乎很可疑。

Let me ask the inverse question: would it be reasonable for String(0) to produce ""? Of course not, we'd clearly expect "0" there. Hmmm.

让我问一个相反的问题: String(0)产生""是否合理? 当然不是,我们显然希望那里是"0" 。 嗯

But what are the other possible behaviors? Should Number("") throw an exception (like Java)? Ugh, no. That intolerably violates the design philosophy. The only other sensible behavior I can conceive is for it to return NaN.

但是其他可能的行为是什么? Number("")是否应该引发异常(例如Java)? gh,不。 那是无法忍受的违反设计理念的。 我能想到的唯一明智的行为是返回NaN

NaN shouldn't be thought of as "not a number"; most accurately, it's the invalid number state. Typically you get NaN from performing a math operation without the required value(s) being numbers (or number like), such as 42 / "abc". The symmetric reasoning from coercion fits perfectly: anything you try to coerce to a number that's not clearly a valid number representation should result in the invalid number NaN—indeed Number("I like maths") produces NaN.

NaN不应被视为“不是数字”; 最准确地说,这是无效数字状态。 通常,您通过执行数学运算而得到NaN ,而所需的值不是数字(或类似数字),例如42 / "abc" 。 从强制完全符合对称推理:任何你试着将其转换为一个数字,这显然不是一个有效的数字表示应导致无效号码NaN -indeed Number("I like maths")产生NaN

I strongly believe Number("") should have resulted in NaN.

我坚信Number("") 应该会导致NaN

强迫""NaN(Coercing "" to NaN?)

What if we could change just this one thing about JavaScript?

如果我们可以改变JavaScript的这一件事怎么办?

One of the common coercive equalities that creates havoc is the 0 == "" equality. And guess what? It comes directly from the fact that the == algorithm says, in this case, for "" to become a number (0 already is one), so it ends up as 0 == 0, which of course is true.

造成破坏的常见强制性等式之一是0 == ""等式。 你猜怎么着? 这直接来自以下事实: ==算法表示在这种情况下, ""成为数字( 0已经是一个数字),所以最终以0 == 0 ,这当然是true

So, if "" instead coerced to the NaN number value instead of 0, the equality check would be 0 == NaN, which is of course false (because nothing is ever equal to NaN, not even itself!).

因此,如果将""强制转换为NaN数值而不是0 ,则相等检查将是0 == NaN ,这当然是false (因为没有东西等于NaN ,甚至都不等于它!)。

Here, you can see the basis for my overall thesis: the problem with 0 == "" is not the == itself—its behavior at least in this case is fairly sensible. No, the problem is with the Number("") coercion itself. Using === to avoid these cases is like putting a bandaid on your forehead to treat your headache.

在这里,您可以看到我的总体命题的基础: 0 == ""的问题不是==本身-至少在这种情况下,其行为是相当合理的。 不,问题在于Number("")强制本身。 使用===避免这些情况就像在额头上贴创可贴来治疗头痛。

You're just treating the symptom (albeit poorly!), not fixing the problem. Value coercion is the problem. So fix the problem. Leave == alone.

您只是在治疗症状(尽管效果很差!),而不是解决问题。 价值强制是问题所在。 因此,解决问题。 不理会==

Crazy, you say? There's no way to fix Number("") producing 0. You're right, it would appear there's no way to do that, not without breaking millions of JavaScript programs. I have an idea, but we'll come back to that later. We have a lot more to explore to understand my larger point.

疯了,你说? 无法修复Number("")产生0 。 没错, 似乎没有办法,除非没有破坏数百万个JavaScript程序。 我有个主意,但我们稍后再讲。 为了理解我的更大观点,我们还有很多事情要做。

数组到字符串 (Array To String)

What about 0 == []? That one seems strange, right? Those are clearly different values. And even if you were thinking truthy/falsy here, [] should be truthy and 0 should be falsy. So, WTF?

0 == []呢? 那似乎很奇怪,对吧? 这些显然是不同的值。 即使您在这里认为是真/假, []应该是真,而0应该是假。 那么,WTF?

The == algorithm says if both operands are objects (objects, arrays, functions, etc), just do a reference comparison. [] == [] always fails since it's always two different array references. But if either operand is not an object but instead is a primitive, == tries to make both sides a primitive, and indeed primitives of the same type.

==算法表示如果两个操作数都是对象(对象,数组,函数等),则只需进行引用比较即可。 [] == []总是失败,因为它总是两个不同的数组引用。 但是,如果任何一个操作数都不是对象而是一个原语,则==尝试使双方成为一个原语,并且实际上是相同类型的原语。

In other words, == prefers to compare values of the same type. That's quite sensible, I'd argue, because equating values of different types is nonsense. We developers also have that instinct, right? Apples and oranges and all that jazz.

换句话说, ==倾向于比较相同类型的值。 我认为这是非常明智的,因为将不同类型的值等同起来是胡说八道。 我们开发人员也有这种本能,对吗? 苹果和橘子以及所有爵士乐。

So [] needs to become a primitive. [] becomes a string primitive by default, because it has no default to-number coercion. What string does it become? Here's another coercion I'd argue is busted by original design: String([]) is "".

因此[]需要成为原始的。 []默认情况下会成为字符串基元,因为它没有默认的对数字强制。 它变成什么字符串? 我认为这是原始设计破坏的另一种强制: String([])""

For some reason, the default behavior of arrays is that they stringify to the string representation of their contents, only. If they have no contents, that just leaves "". Of course, it's more complicated than that, because null and undefined, if present in an array's values, also represent as "" rather than the much more sensible "null" and "undefined" we would expect.

由于某些原因,数组的默认行为是仅将其字符串化为其内容的字符串表示形式。 如果它们没有内容,则仅留下"" 。 当然,这要复杂得多,因为nullundefined (如果存在于数组的值中)也表示为""而不是我们期望的更明智的"null""undefined"

Suffice it to say, stringification of arrays is pretty weird. What would I prefer? String([]) should be "[]". And btw, String([1,2,3]) should be "[1,2,3]", not just "1,2,3" like current behavior.

可以说,数组的字符串化很奇怪。 我想要什么? String([])应该是"[]" 。 而且顺便说一句, String([1,2,3])应该是"[1,2,3]" ,而不仅仅是像当前行为那样的"1,2,3"

So, back to 0 == []. It becomes 0 == "", which we already addressed as broken and needing a fix job. If either String([]) or Number("") (or both!) were fixed, the craziness that is 0 == [] would go away. As would 0 == [0] and 0 == ["0"] and so on.

因此,回到0 == [] 。 它变为0 == "" ,我们已经将其解决为损坏的并且需要修复作业。 如果固定了String([])Number("") (或两者!),那么0 == []的疯狂就会消失。 0 == [0]0 == ["0"]依此类推。

Again: == is not the problem, stringification of arrays is. Fix the problem, not the symptom. Leave == alone.

再说一次: ==不是问题,数组的字符串化是问题。 解决问题,而不是症状。 不理会==

Note: The stringification of objects is also weird. String({ a: 42 }) produces "[object Object]" strangely, when {a:42} would make a lot more sense. We won't dive into this case any more here, since it's not typically associated with coercion problems. But it's a WTF nonetheless.

注意:对象的字符串化也很奇怪。 当{a:42}更有意义时, String({ a: 42 })奇怪地产生"[object Object]" 。 在这里,我们不再讨论这种情况,因为它通常与强制性问题无关。 但这仍然是WTF。

更多问题(那不是==的错) (More Gotchas (That Aren't =='s Fault))

If you don't understand the == algorithm steps, I think you'd be well served to read them a couple of times for familiarity. I think you'll be surprised at how sensible == is.

如果您不了解==算法步骤 ,我认为您应该多次阅读它们以熟悉它们。 我认为您会对==如此明智感到惊讶。

One important point is that == only does a string comparison if both sides are either already strings, or become strings from an object coercing to a primitive. So 42 == "42" might feel like it's treated as "42" == "42", but in fact it's treated as 42 == 42.

重要的一点是, ==仅在双方都已经是字符串,或者成为从强制到基本体的对象成为字符串的字符串时才进行字符串比较。 因此42 == "42"可能会被当作"42" == "42"对待,但实际上却被视为42 == 42

Just like when your math teacher scolded you for getting the right answer for the wrong reason, you shouldn't be content to accidentally predict == behavior, but instead make sure you understand what it actually does.

就像您的数学老师因错误的原因而责骂您获得正确的答案一样,您不应满足于意外地预测==行为,而应确保您了解它的实际作用。

What about many other comonly cited == gotchas?

那么其他许多被comonly引用的==陷阱呢?

  • false == "": Not as many of you will complain about this one. They're both falsy, so it's at least in the neighborhood of sensible. But actually, their falsiness is irrelevant. Both become numbers, the 0 value. We've already demonstrated what needs to change there.

    false == "" :很少有人会抱怨这一点。 他们俩都是虚假的,所以至少在明智的附近。 但是实际上,他们的虚假是无关紧要的。 两者都变为数字,即0值。 我们已经演示了在那里需要进行哪些更改。

  • false == []: What? [] is truthy, how can it possibly be == false? Here, you're probably tempted to think [] should be coerced to a true / false, but it's not. Instead, false becomes a number (0 naturally), and so then it's 0 == [], and we just saw that case in the previous section.

    false == [] :什么? []是真实的,怎么可能== false ? 在这里,您可能很想考虑[]应该强制为true / false ,但事实并非如此。 取而代之的是, false变成一个数字(自然为0 ),因此它是0 == [] ,我们在上一节中看到了这种情况。

    Should we change Number(false) from 0 to NaN (and, symmetrically, Number(true) to NaN)? Certainly if we're changing Number("") to NaN, I could make that case. Especially since we can observe Number(undefined) is NaN, Number({}) is NaN, and Number(function(){}) is NaN. Consistency might be more important here?

    我们是否应该将Number(false)0更改为NaN (对称地,将Number(true)更改为NaN )? 当然,如果我们将Number("")更改为NaN ,我可能会遇到这种情况。 特别是因为我们可以看到Number(undefined)NaNNumber({})NaN ,而Number(function(){})NaN 。 一致性在这里可能更重要?

    Or not. Strong tradition from the C language is for false to 0, and the reverse Boolean(0) clearly should be false. Guess this one is a toss-up.

    或不。 从C语言强传统为false0 ,而反向Boolean(0)显然应该是false 。 猜猜这是一个折腾。

    But either way, false == [] would be fixed if the other previously stated array stringification or empty string numeric issues were fixed!

    但是,无论哪种方式,如果其他先前声明的数组字符串化或空字符串数字问题得到解决,则false == []将得到解决!

  • [] == ![]: Nuts! How can something be equal to the negation of itself?

    [] == ![] :坚果! 事物如何等于自身的否定?

    Unfortunately, that's the wrong question. The ! happens before the == is even considered. ! forces a boolean coercion (and flips its parity), so ![] becomes false. Thus, this case is just [] == false, which we just addressed.

    不幸的是,这是一个错误的问题。 ! 甚至在==被考虑之前发生。 ! 强制执行boolean强制(并翻转其奇偶校验),所以![]变为false 。 因此,这种情况只是[] == false ,我们刚才已经解决了。

万物之根==邪恶 (The Root Of All == Evils)

OK, wait. Let's review for a moment.

好,等一下。 让我们回顾一下。

We just zipped through a bunch of commonly cited == WTFs. You could keep looking for even more == weirdness, but it's pretty likely that you'd just end up back at one of these cases we just cited, or some variation thereof.

我们只是浏览了一堆常用的== WTF。 您可以继续寻找更多==怪异的事物,但是很可能您最终会回到我们刚才引用的这些情况之一或其中的一些变化。

But the one thing all these cases have in common is that if Number("") was changed to NaN, they'd all magically be fixed. It all comes back to 0 == ""!!

但是所有这些情况的共同点是,如果将Number("")更改为NaN ,那么它们都将被神奇地修复。 一切都回到0 == "" !!

Optionally, we could also fix String([]) to "[]" and Number(false) to NaN, for good measure. Or not. We could just fix 0 == "". Yes, I'm saying that virtually all of the frustrations around == actually stem from that one corner case, and furthermore basically have almost nothing to do with == itself.

(可选)我们也可以将String([]) 固定"[]"并将Number(false)NaN ,以达到良好的效果。 或不。 我们可以修复0 == "" 。 是的, 我是说==周围的所有挫败感实际上源于一个极端的情况 ,此外,与==本身几乎没有任何关系。

Take a deep breath and let that sink in.

深吸一口气,让它沉入。

加入到我们的挫折 (Adding To Our Frustrations)

I really wish I could end the article here. But it's not so simple. Yes, fixing Number("") fixes pretty much all of == woes, but == is only one of the many places people trip over coercion in JS.

我真的希望我可以在这里结束本文。 但这不是那么简单。 是的,修复Number("")几乎可以解决==所有问题,但是==只是人们在JS中因强制而绊倒的众多地方之一。

The next most common source of coercion issues comes when using the + operator. Again, we're going to see that the complaints are usually made against +, but in reality it's the underlying value coercions that are generally to blame.

使用+运算符时,下一个最常见的强制问题来源。 再一次,我们将看到抱怨通常是针对+ ,但实际上,通常应该归咎于潜在的价值强制。

Some people are quite bothered by the overloading of + to be both math addition and string concatenation. To be honest, I neither love nor hate this fact. It's fine to me, but I'd also be quite OK if we had a different operator. Alas, we don't, and probably never will.

有些人对+的重载感到很困扰,因为它们既是数学加法又是字符串连接。 老实说,我既不喜欢也不讨厌这个事实。 对我来说很好,但是如果我们有其他运算符,我也可以。 las,我们没有,也许永远不会。

Simply stated, + does string concatenation if either operand is a string. Otherwise, addition. If + is used with one or both operands not conforming to that rule, they're implicitly coerced to match the expected type (either string or number).

简而言之,如果任一操作数为字符串,则+会进行字符串连接。 否则,加法。 如果将+与一个或两个不符合该规则的操作数一起使用,则将它们隐式强制为匹配期望的类型( stringnumber )。

On the surface, it would seem, if for no other reason than consistency with ==, that + should concatenate only if both were already strings (no coercion). And by extension, you could say that it adds only if both operands were already numbers (no coercion).

从表面上看,如果仅出于与==一致的原因,则+仅在两个都已经是字符串(没有强制)的情况下才可以连接。 通过扩展,您可以说仅当两个操作数都已经是数字(没有强制)时才添加。

But even if we did change + like that, it wouldn't address the corner cases of mixing two different types with +:

但是,即使我们确实进行了 +更改,也无法解决将两种不同类型与+混合的特殊情况:

42 + "";    // "42" or 42?
41 + "1";   // "411" or 42?


What should + do here? Throwing an error is so Java. 1994 just called.

在这里+应该做什么? 抛出错误就是Java。 1994年刚刚致电。

Is addition really more preferable than concatenation here, or vice versa? My guess is, most people prefer concatenation ("42") for the first operation, but addition (42) for the second. However, the inconsistency of that position is silly. The only sensible position is that either these operations must result in "42" and "411" (as currently) or 42 and 42 (as hypothesized).

在这里加法真的比串联更好,反之亦然吗? 我的猜测是,大多数人在第一个操作中更喜欢串联( "42" ),而在第二个操作中更喜欢加法( 42 )。 但是,该位置的不一致很愚蠢。 唯一明智的立场是,这些操作必须导致"42""411" (当前),或者导致4242 (假设)。

Actually, as I argued earlier, if the first + is addition, that operation should result in NaN, not 42, as the "" must become NaN instead of 0. Would you still prefer NaN / 42 to "42" / "411", then? I doubt it.

实际上,正如我之前所说,如果第一个+是加号,则该操作应导致NaN而不是42 ,因为""必须变为NaN而不是0 。 那么,您是否仍然希望NaN / 42 NaN "42" / "411" ? 我对此表示怀疑。

I don't think there's a better behavior we could change + to.

我不认为有一个更好的行为,我们可以改变+到。

So how do we explain + gotchas if it's not the + operator's fault? Just as before: value coercions!

那么,如果不是+运算符的错,我们该如何解释+陷阱呢? 与以前一样:强制执行价值!

For example:

例如:

null + 1;           // 1
undefined + 1;      // NaN


Before I explain, which of those two seems more sensible? I'd say without reservation that the second is vastly more reasonable than the first. Neither null nor undefined are numbers (nor strings), so + can't possibly be seen to be a valid operation with them.

在我解释之前,这两个中哪一个看起来更明智? 我会毫无保留地说第二个比第一个更合理。 数字(也不是字符串)既不是null也不是undefined ,因此+不可能被视为对它们的有效操作。

In the two above + operations, none of the operands are strings, so they are both numeric additions. Furthermore, we see that Number(null) is 0 but Number(undefined) is NaN. We should fix one of these, so they're at least consistent, but which?

在上面的两个+操作中,操作数都不是字符串,因此它们都是数字加法。 此外,我们看到Number(null)0Number(undefined)NaN 。 我们应该修复其中一个,因此它们至少是一致的,但是哪个呢?

I strongly feel we should change Number(null) to be NaN.

我强烈认为我们应该将Number(null)更改为NaN

其他强制WTF (Other Coercion WTFs)

We've already highlighted the majority of cases you'll likely run into in everyday JS coding. We even ventured into some crazy niche corner cases which are popularly cited but which most developers rarely stumble on.

我们已经强调了您在日常JS编码中可能会遇到的大多数情况。 我们甚至冒险进入一些疯狂的利基角落案例,这些案例被普遍引用,但大多数开发人员很少偶然发现。

But in the interest of exhaustive completeness, I have compiled a huge gnarly table of a whole bunch of different corner-case'ish values and all the implicit and explicit coercions you can run them through. Grab a strong bottle of alcohol (or your own favorite coping mechanism) and dive in.

但是出于穷举完整性的考虑,我编译了一个巨大的粗糙表 ,其中列出了许多不同的极端情况下的值以及可以通过它们执行的所有隐式和显式强制。 拿一瓶烈性酒(或您自己喜欢的应对方法)然后潜入。

镜头矫正 棋盘矫正_矫正强迫,而不是症状_第1张图片

If you're looking for a case to criticize coercion, it (or its root) will almost certainly be found on that list. There's a few other surprises hiding in that table, but we've covered the ones you need to be worried about.

如果您正在寻找一个批评胁迫的案例,那么它(或它的根源)几乎肯定会在该列表中找到。 桌子上还藏着其他一些惊喜,但我们已经涵盖了您需要担心的那些惊喜。

我们可以解决吗? (Can We Fix?)

I've rambled at length both about why coercion is awesome and why it has issues. It's important to remember that from my perspective, the operators are not at fault, though they get all the negative attention.

我一直在漫长地徘徊,既关于为什么强迫真棒,又为什么有问题。 重要的是要记住,从我的角度来看,操作员并没有错,尽管他们受到了所有负面的关注。

The real blame lies with some of the value coercion rules. In fact, the root list of problems is rather short. If we fix them, they cascade out to fix a whole bunch of other non-root problems that trip developers up.

真正的责任在于某些价值强制性规则。 实际上,问题的根本清单很短。 如果我们解决了这些问题,它们会级联解决许多其他非根本性的问题,这些问题会使开发人员感到震惊。

Let's recap the root problem value coercions we're concerned about:

让我们回顾一下我们关注的根本问题价值强制:

  • Number("") is 0

    Number("")0

    Should be: NaN (fixes most problems!)

    应该是: NaN (解决了大多数问题!)

  • String([]) is "", String([null]) is "", String([undefined]) is ""

    String([])""String([null])""String([undefined])""

    Should be: "[]", "[null]", "[undefined]"

    应为: "[]""[null]""[undefined]"

  • Number(false) is 0, Number(true) is 1

    Number(false)0Number(true)1

    Should be (optional/debatable): NaN, NaN

    应该是(可选/有争议的): NaNNaN

  • Number(null) is 0

    Number(null)0

    Should be: NaN

    应为: NaN

OK, so what can we do to fix these problems (value coercions) instead of treating the symptoms (operators)?

好的,那么我们该怎么解决这些问题(价值强制)而不是治疗症状(操作员)?

I'll admit that there's no magic bullet that I can pull out. There's no trick (well... we could monkey-patch Array.prototype.toString() to fix those cases). There's no profound insight.

我承认,我没有什么可以拔出的魔法子弹。 没有技巧(嗯……我们可以用猴子补丁Array.prototype.toString()修复这些情况)。 没有深刻的见解。

No, to fix these, we're going to have to brute force it.

不,要解决这些问题,我们将不得不对其进行暴力破解。

Proposing to TC39 a straight-up change to any of these would fail at the first step. There's literally zero chance of that kind of proposal succeeding. But there's another way to introduce these changes, and it might, just might, have a tiny fraction of a % chance. Probably zero, but maybe it's like 1e-9.

向TC39提议对其中任何一个进行直接修改都将在第一步失败。 这种提议成功的可能性几乎为零。 但是,还有另一种方法可以引入这些更改,并且可能(可能)只有很小的几率。 可能为零,但也许像1e-9。

"use proper"; ("use proper";)

Here's my idea. Let's introduce a new mode, switched on by the "use proper"; pragma (symmetrical to "use strict", "use asm", etc), which changes those value coercions to their proper behavior.

这是我的主意。 让我们介绍一种新的模式,该模式通过"use proper";开启"use proper"; 杂注(与"use strict""use asm"等对称),将这些价值强制更改为适当的行为。

For example:

例如:

function foo(x) {
    "use proper";

    return x == 0;
}

foo("");    // false
foo([]);    // false
foo(false); // false

foo("0");   // true


Do you see why this is different—and I'm arguing, better—than ===? Because we can still use == for safe coercions like "0" == 0, which the vast majority of us would say is still a sensible behavior.

您是否明白为什么这比===有所不同(我认为更好)? 因为我们仍然可以将==用于安全强制,例如"0" == 0 ,我们大多数人会说这仍然是明智的行为。

Furthermore, all these corrections would be in effect:

此外,所有这些更正将生效:

"use proper";

Number("");             // NaN
Number("  ");           // NaN
Number("\n\n");         // NaN
Number(true);           // NaN
Number(false);          // NaN
Number(null);           // NaN
Number([]);             // NaN

String([]);             // "[]"
String([null]);         // "[null]"
String([undefined]);    // "[undefined]"

0 == false;             // false
1 == true;              // false
-1 < "";                // false

1 * "";                 // NaN
1 + null;               // NaN


You could still use === to totally disable all coercions, but "use proper" would make sure that all these pesky value coercions that have been plaguing your == and + operations are fixed, so you'd be free to use == without all the worry!

您仍然可以使用===完全禁用所有强制,但是"use proper"将确保困扰您==+操作的所有这些讨厌的值强制都固定,因此您可以自由使用==不用担心!

接下来是什么? (What Next?)

The theoretical proposal I've just made, which likely has near zero chance of ever getting adopted even if I did formally propose it, doesn't seem like it leaves you with much practical take away from all this reading. But if enough of you latch onto the ideas here and help create momentum, it might have a remote chance.

理论建议我刚刚做,它可能具有接近零的机会有朝一日能采取哪怕我没有正式提出的,并没有看起来它给你留下多少实际采取从所有这一切的阅读了。 但是,如果你们中的足够多的人抓住这里的想法并帮助创造动力,那么它可能就很少了。

But let me suggest a couple other possibilities, besides the standards track, to chew on:

但是,让我提出除标准跟踪之外的其他几种可能性:

  1. "use proper" could be become a new transpile-to-JavaScript language ("ProperScript", "CoercionScript", etc), in the same spirit as TypeScript, Dart, SoundScript, etc. It could be a tool that transforms code by wrapping all value operations in runtime checks that enforce the new rules. We could lessen the obvious performance hit quite a bit by specifying annotations (again, TypeScript style) that hint the tool which operations it should wrap.

    按照与TypeScriptDartSoundScript等相同的精神,“ "use proper"可以成为一种新的可转换为JavaScript的语言(“ ProperScript”,“ CoercionScript”等)。它可以是通过包装来转换代码的工具运行时中的所有值操作检查都将强制执行新规则。 我们可以通过指定注释(再次是TypeScript样式)来提示该工具应该包装哪些操作,从而大大降低明显的性能损失。

  2. We could take these sets of desired new value coercion rules and turn them into assertions for a build-process that does simulated run-time checks (with test data) to "lint" your code, in a similar spirit to the RestrictMode project, one of my favorite sleeper projects. This tool would spit out warnings if it detects places in your code that expect coercion results that don't hold.

    我们可以采用这些期望的新值强制规则集,并将它们转变为用于构建过程的断言,该过程进行模拟运行时检查(带有测试数据)以“编码”您的代码,其精神类似于RestrictMode项目,我最喜欢的卧铺项目 如果该工具在代码中检测到预期的强制结果不成立的位置,则会发出警告。

意识 (Awareness)

Finally, let me just say that even if none of this proposal ever comes to pass, I believe there's still value to be gleaned from this article. By learning exactly what things are going wrong in your == and + operations—that is, the value coercion corner cases themselves—you're now empowered to write better, more robust code that robustly handles (or at least avoids) these cases.

最后,我只想说,即使这个提议都没有获得通过,我相信这篇文章仍然有价值。 通过确切地了解==+操作中出了什么问题(也就是价值强制性极端案例本身),您现在可以编写更好,更健壮的代码来稳健地处理(或至少避免)这些情况。

I believe it's far healthier to be aware of the ins and outs of coercion, and use == and === responsibly and intentionally, than it is to just use === because it's easier not to think and not to learn.

我认为了解强制的来龙去脉要健康得多,并且负责任和有意地使用=====比仅使用===更容易,因为不去思考和不去学习比较容易。

If you take writing JS seriously, and I hope you do, isn't it worth your time to internalize this discipline? Won't that do more to improve your code than any blindly-applied linting rule ever will?

如果您认真地编写JS,并且希望您这样做,那么值得您花时间去内部化这门学科吗? 这样做是否会比盲目应用的掉毛规则做得更多呢?

Don't forget to check out my You Don't Know JS book series, and specifically the YDKJS: Types & Grammar title, which can be read for free online or purchased through O'Reilly and other sellers.

不要忘记查看我的《 您不知道JS 》系列丛书,尤其是YDKJS:Types&Grammar标题,可以在线免费阅读或通过O'Reilly和其他卖家购买 。

翻译自: https://davidwalsh.name/fixing-coercion

镜头矫正 棋盘矫正

你可能感兴趣的:(python,java,javascript,编程语言,人工智能)