JS 真的这么做吗?

logo

原文地址:codeburst.io/does-js-really-do-that-43f17bc01e9b

在通读了《You-Dont-Know-JS》后,我很困惑 JavaScript 的奇怪。
渐渐地,我开始寻找方法去验证他的疯狂。
在这里,我简要描述了一些我遇到过的“陷阱”以及我在使用 JavaScript 时遇到的一些问题。


我应该怎么强迫你(How should I coerce you? )

我们经常提及的强迫转换难题是 [] + {} VS. {} + [],因为这两种计算方式出现了不同结果:

[] + {} // "[object Object]" 
{} + [] // 0

那么,发生了什么?

  1. 当执行 + 操作的操作数为对象时,该对象将会被强转为一个原始类型数值。为了实现这个强转,首先 valueOf() 会作用于该对象。如果它无法生成一个简单原始类型值,它将会执行 toString() 来获取结果。

  2. 如果任何 + 操作的操作数是字符串时,结果都将会是字符串。

对于 [] + {}valueOf() 操作符作用于 [] 时将无法产生原始数值,所以 toString() 方法将会被调用,而 [] 它将会被强转为 ""(空字符串)。当一个操作数为字符串时,另一个也会被强转为字符串,因此 {} 将会被强转为 "[object object]"

对于 {} + []{} 被解释为一个独立的空块(不需要分号来终止),而 + [] 作为一个表达式,导致结果显式的将[] 强转为数字 :0。同样可以被写成:{}; +[]


当布尔值与其他类型比较时(When Booleans were compared to others)

当布尔值与其他原始类型进行比较时,将会出现不可预测的结果。
已知非空字符串与非零数字都是 真值。因此我们会预测判断 真值 == true 返回 true。而实际呢?

var x = true, y = "42";
x == y; // 假

y = "true";
x == y; // 假

因为,根据 ES5 规范:

如果 Type(x) 是 Boolean,返回 ToNumber(x) == y 的比较结果;
如果 Type(y) 是 Boolean,返回 x == ToNumber(y) 的比较结果;
如果 Type(x) 是数字,而 Type(y) 是字符串,返回 x == ToNumber(y) 的比较结果;
如果 Type(x) 是字符串,而 Type(y) 是数字,返回 ToNumber(x) == y 的比较结果;

所以,第一个实例中,x 的类型为布尔值,所以它将会转为 ToNumber(x)true 会强转为 1。现在,转为 1 == "42" 的计算,类型仍然不同,需要重新执行算法,根据 ES5 规范,字符串进行强转,转为 1 == 42 的计算,而 1 == 42 明显返回为 false 。

同样的,第二个示例中,x 被转为1,而 y 通过 ES5 规范,进行 ToNumber(y) 计算,"true" 强转为 NaN;而 1 == NaN 明显也返回 false。


对象比较的荒谬实例(The absurd case of object comparison)

我们经常需要检查两个对象引用是否指向同一个对象,并使用 == 操作符来实现该效果。但你是否想过 JavaScript 对两个对象间的其他逻辑比较如何处理?

var a = {b : 42};
var b = {b : 42};
a == b; // 假,检测a,b是否指向同一个对象。
a < b;  // 假
a > b;  // 假

a < b 强制两个对象转为他们各自的原始类型值。因为,这两者都没有定义 valueOf() 方法,他们的值会被 toString() 强制转换,而a 和 b 都会转为 [object object],因此,结果返回 false

如果尝试:

a <= b;  // 真
a >= b;  // 真

哈?!发生了什么?

a == ba < ba > b 都为假时,a <= ba >= b 怎么可能会是真?

因为 ES5 规范规定,计算 a <= b,实际是先计算 b < a,然后取反。而 b <a 肯定是 false,那么 取反,a <= b 就是了。

Js 解析 <=不大于!(a > b)),会视为 !(b < a) 进行处理。而 a >= b,Js 会根据以上方式,作为 b <= a 进行处理。

以上可能有点绕,弄清楚就会很清晰了~


使用后增量预增(Pre-increment with post-incrment)

var a = 42;
++a++ = ?

我们沉迷于偶尔使用的自增运算符,那 ++a++ 会是怎样的怪物呢?它是否合法?

如果你尝试过,将会返回一个 ReferenceError 的报错,那为什么呢?因为这类操作需要一个 变量引用 去定位他们的附加操作。对于 ++a++a++ 将会先被计算(因为操作符优先级),再次增加前会返回变量 a 的值。而当它尝试计算 ++42 时,将会返回 ReferenceError 的报错。

这是因为 ++ 无法直接作用于数值,例如42。


谨慎的使用"name"命名变量(“name” must be named with caution)

让我阐述一下原因。在控制台运行一下代码:

//确保你在全局环境下
name = function(me) {
    console.log("I'm ${me}! :)");
}

确认你没有因为为提前声明 “name” 变量而出现 ReferenceError 的报错。
运行:

name("Parama");
//TypeError : name is not a function

为什么会报错?

因为 namewindow.name 是保留关键字。当变量被命名时,它会自动被字符串化,即 toString() 方法可被调用。

window.name 会获取/设置 window 的变量名。只有在 var 声明时会发生,let 声明不会,因为 let 声明的变量不能影响 window 对象。


undefined 和 null, 相同也不同(undefined and null, same but different)

当进行比较时,undefinednull 都会被强转并返回

undefined == null; // 真
undefined +1 == null +1; //假, 为什么?

当进行相加操作时,他们会生成不同的结果:

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

产生这种结果的原因是:当进行相加等运算操作时,undefinedToNumber 抽象化强转为 NaN,而 null 则强转为 0。


解构的一个警告(A caveat to deconstructuring)

var { a : X, a : Y, a : [Z] } = { a : [1] };

如果你对解构熟悉,你会知道 X 和 Y 的结果是数组 [1],而 Z 的结果是 1。让我们把 X 和 Y 拧到一起:

X.push( 2 );
Y[0] = 10;

X;  // [10, 2]   怎么会?!
Y;  // [10, 2]   发生了什么?!
Z;  // 1

在以上片段中,X 和 Y 以及对象属性(该实例中的数组)被解构并指向同一块内存空间。所以~奇怪的行为。


避免 for 循环(Avoid for-loops)

我有充分的理由发出这个警告,假设以下输出:

for( let i=(setTimeout( ()=>console.log(i)), 0); i<2; i++) {
    i++;
}

如果你想法与我一样,你会假设结果输出为 1 ,是吗?

但是,正确答案却是 0。

让我们一步步的来解开谜题:

  1. 在给循环添加了 setTimeout() 事件后,i 被初始化为 0;

  2. for 循环中的初始声明部分创建一个独立的词法空间。这块词法空间与 for 循环的主体不同,互相独立。因此,i 的值在闭环中会被保存,即 setTimeout() 中的 i 与外部 for 循环中不同

  3. 在第一次迭代中,当条件被满足时 for 循环主体内容被执行,i 变成 1。setTimeout() 已被关闭,因此 for 循环中 i 的修改不会影响到 setTimeout() 中的 i 值。

  4. for 循环终止后,0 将会被打印。

注意:只有在 let 声明的变量中才能观察到这个怪事。

后续将会持续更新…

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
应用背景为变电站电力巡检,基于YOLO v4算法模型对常见电力巡检目标进行检测,并充分利用Ascend310提供的DVPP等硬件支持能力来完成流媒体的传输、处理等任务,并对系统性能出一定的优化。.zip深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值