友情提示:视频教程观看时请手动设置清晰度。
01
源码佐证:
_setCostume (target, requestedCostume, optZeroIndex) {
if (typeof requestedCostume === 'number') {
// Numbers should be treated as costume indices, always
target.setCostume(optZeroIndex ? requestedCostume : requestedCostume - 1);
} else {
// Strings should be treated as costume names, where possible
const costumeIndex = target.getCostumeIndexByName(requestedCostume.toString());
数字类型的参数被当做造型编号来执行,字符类型的参数被当做造型名称来执行(前提是有对应的造型名称时)
if (costumeIndex !== -1) {
target.setCostume(costumeIndex);
} else if (requestedCostume === 'next costume') {
target.setCostume(target.currentCostume + 1);
} else if (requestedCostume === 'previous costume') {
target.setCostume(target.currentCostume - 1);
// Try to cast the string to a number (and treat it as a costume index)
// Pure whitespace should not be treated as a number
// Note: isNaN will cast the string to a number before checking if it's NaN
} else if (!(isNaN(requestedCostume) || Cast.isWhiteSpace(requestedCostume))) {
target.setCostume(optZeroIndex ? Number(requestedCostume) : Number(requestedCostume) - 1);
}
字符类型的参数会被优先“尝试”转换为数字类型,并作为造型编号执行。
只是诸多会对数据类型进行自动转换的积木之一,也是在使用时最容易出BUG、最具代表性的积木。几乎所有积木都会对数据类型进行自动转换,有的甚至对输入的数据类型做了限制,如,无法直接在积木中输入字符。
if (typeof index !== 'number') {
if (index === 'all') {
return acceptAll ? Cast.LIST_ALL : Cast.LIST_INVALID;
}
if (index === 'last') {
if (length > 0) {
return length;
}
return Cast.LIST_INVALID;
} else if (index === 'random' || index === 'any') {
if (length > 0) {
return 1 + Math.floor(Math.random() * length);
}
return Cast.LIST_INVALID;
}
}
02
转数字类型失败
static toNumber (value) {
// If value is already a number we don't need to coerce it with
// Number().
if (typeof value === 'number') {
// Scratch treats NaN as 0, when needed as a number.
// E.g., 0 + NaN -> 0.
if (Number.isNaN(value)) {
return 0;
}
return value;
03
布尔值的规则
比较运算符的连续嵌套使用
初学者会这样写脚本的原因是将数学中的习惯带到编程里来了(别问笔者是怎么知道的,问就是笔者也犯过相同的错误。。)乍一看好像没有什么问题。但是要知道,程序是一步一步执行的,像这样嵌套关系的积木,也是先执行上层的,再执行下层的(相当于数学中的括号)。3>2的结果是true(不打引号以区分字符类型),再比较true和1的大小。到这一步,不论接下去的结果如何,都应该能发现,和数学中连续比较的的3>2>1不一样,这么写是错的,是肯定无法得到预期结果的。本着 Scratch 格物堂的“格物”精神,继续往下看。true和1怎么比大小?这还不简单吗?《Scratch 3.0的大小比较是如何进行的》一文中已经说得很清楚了,字符类型的数据在参与比较运算时,比较的是ASCII码或者说是Unicode码。而数字0的十进制编号是48,A是65,a是97。就算Scratch不区分大小写,二十六个字母怎么的也是排在数字后面,字母是肯定大于数字的,“true”>1肯定是true。
“true”>1
看似没毛病
但是一运行整个脚本。。。
于是开始倒推哪个环节出了问题。
用《Debug in Scratch —— Part 1》中提到的“气泡输出法”进行排查:
曾经引以为傲的“气泡输出法”
说“true”
还是没问题
细心的读者可能会发现,上文中的true,是什么时候被打上引号的?true在参与比较运算时,真的是“true”吗?我们想当然地以为true被转换成了字符类型。但是不是字符类型还能是什么?NaN,难道被转成了0吗?0>1的确是false没错,好像说通了。但是换几个用例再次进行测试,发现结果又不太一样。篇幅有限,不再进行引导。直接上测试印证猜测
测试1:
布尔类型(true)参数参与算术运算(+空字符)
测试2:
布尔类型(false)参数参与算术运算(+空字符)
这两个小测试足以证明,
true被转换为数字类型时,会被转换为1;false在被转换为数字类型时,会被转换为0.
在上文中的比较运算符的连续嵌套使用示例中,第二次其实是在比较1>1,结果自然是false了。至于为什么“气泡输出法”会失效,这是因为这类积木会将参数转换为字符类型。
04
实例
1号:“不是我。”
2号:“小偷是3号。”
3号:“小偷是4号。”
4号:“3号在说谎”
4名嫌疑人中,有且仅有1人在说谎
05
总结
但是隐式转换带来的负面影响也同样地明显。相比强类型的语言,Scratch在数据类型这方面来说显得不够严谨。其次,和其他弱类型语言一样,隐式转换总是被打上“它们的存在将导致错误的发生”的标签。一如上文中的3>2>1。Scratch数据类型隐式转换所造成的BUG数不胜数,细分下去种类繁多。并且就像他的名字一样,十分隐蔽,极难排查,即使像上文中那样运用一般的debug方法,也很有可能会因为再次发生隐式转换而失效。
转自公众号:
Scratch格物堂