为啥相等检查不适用于数组[重复]
Posted
技术标签:
【中文标题】为啥相等检查不适用于数组[重复]【英文标题】:Why doesn't equality check work with arrays [duplicate]为什么相等检查不适用于数组[重复] 【发布时间】:2015-08-29 11:58:44 【问题描述】:我开始:
"1:2".split(':') == ["1","2"];
// false
然后尝试:
[1,2] == [1,2];
// false
最终:
[] == [];
// false
我后来发现:
"1:2".split(':').toString() == [1,2].toString();
// true
所以我已经解决了我最初的问题(有点)但是为什么数组不能相互匹配?
【问题讨论】:
==
和 ===
都不进行元素比较。他们都检查两个数组是否相同。
这是因为数组是 javascript 中的对象。你可能想看看这个***.com/questions/22395357/…
Why isn't [1,2,3]
equal to itself in Javascript?的可能重复
注意如果你只是想检查一个空数组你可以检查array.length === 0
【参考方案1】:
数组是 JavaScript 中通过引用传递的对象。这意味着当我初始化一个数组时:
var array = [1, 2, 3];
我已经在内存中创建了对该数组的引用。如果我接着说:
var otherArray = [1, 2, 3];
array 和 otherArray 在内存中有两个单独的地址,因此它们不会彼此相等(即使值相等。)要测试通过引用传递,您可以通过以下方式玩弄数组:
var copyArray = array;
在这种情况下,copyArray 引用的内存地址与数组相同,因此:
copyArray === array; //will evaluate to true
array.push(4); //copyArray will now have values [1, 2, 3, 4];
copyArray.push(5); //array will have values [1, 2, 3, 4, 5];
copyArray === array; //still true
为了测试数组或对象中值的相等性,您需要进行“深度比较”-对于数组,这将遍历两个数组以比较索引和值以查看两者是否相等-对于对象,它将遍历每个键并确保值相同。有关深度比较的更多信息,您可以查看带注释的 underscore.js 源代码:
http://underscorejs.org/docs/underscore.html
【讨论】:
“按引用传递”是函数调用中参数的属性(在 JavaScript 中不存在)。这里没有函数调用。【参考方案2】:如果我将此问题与 Python 中的问题联系起来:
输入:
a="1 2 3 4"
案例一:
a=input.split(' ')
输出:['1', '2', '3', '4']
案例二:
a=map(int,input.split(' '))
输出:[1, 2, 3, 4]
所以,错误在于类型,因为它可能会为:
"1:2".split(':').toString() == [1,2].toString(); //true
【讨论】:
【参考方案3】:我的猜测是,因为在设计 javascript 时,他们认为元素比较是一个很少使用的功能,因此从未将其放入语言中。
此功能在流行语言中相当少见; Java 不支持,C# 不支持,C++ stl 类型不支持。
与参考比较相比,元素比较是相当昂贵和复杂的。
在一个完美的世界中,一切都可以通过引用进行比较,因此每两个具有相同状态的对象都会有相同的引用,从而使我们能够非常便宜地检查相等性,只需将它们的内部虚拟地址与一个简单的比较数字比较。
不幸的是,我们并不生活在一个完美的世界中,上述情况仅适用于具有字符串池的字符串,以及其他一些相对内存昂贵的缓存选项。
Prolog 和 Haskell 等一些语言允许按值进行比较;例如
myProgram :-
Object1 = [1, "something", true, [1,[[], []], true,[false]]],
Object2 = [1, "something", false, [1,[[], []], true,[false]]],
Object3 = [1, "something", true, [1,[[], []], true,[false]]],
Object4 = [1, "something", false, [1,[[], []], true,[false]]],
(Object1 = Object2
-> write("first test passed.")
; write("first test failed\n")),
(Object1 = Object3
-> write("second test passed!\n")
; write("second test failed!\n")),
(Object2 = Object4
-> write("third test passed!\n")
; write("third test failed!")).
您可以使用任何语言实现自己的深度比较器,方法是使用从构造函数到该构造函数的比较器的映射。由于 JavaScript 没有从字符串到对象的映射,并且 JavaScript 客户端无法访问对象的内部唯一标识符,因此我们需要使用带有构造函数、比较器对的表,如下所示。
class MyList
constructor(a, b)
this.head_ = a;
this.tail_ = b;
getHead()
return this.head_;
getTail()
return this.tail_;
setHead(x)
this.head_ = x;
setTail(x)
this.tail_ = x;
equals(other)
if (typeof other !== 'object')
console.log(other, 'EEP');
return false;
if (!(other instanceof MyList))
console.log(other, 'EEP');
return false;
var h = this.head_;
var ho = other.getHead();
var cmp1 = comparatorof(h);
if (!cmp1(h, ho))
return false;
var t = this.tail_;
var to = other.getTail();
var cmp2 = comparatorof(t);
if (!cmp2(t, to))
return false;
return true;
var object1 =
0: "one",
1: "two",
2: ["one", "two", []],
something:
1: [false, true]
;
function strictComparator(a, b)
return a === b;
// given any object, tries finding a function for comparing
// that object to objects of the same kind. Kind being
// used loosely, since some objects like null resist being categorized,
// so these objects need special alteration of the comparatorof itself.
function comparatorof(x)
if (x === null || x === undefined)
return strictComparator;
x = Object(x); // promote primitives to objects
var ctor = x.constructor;
var c2ct = ctorToComparatorTable;
var n = c2ct.length;
for (var i = 0; i < n; ++i)
var record = c2ct[i];
var keyCtor = record[0];
if (keyCtor === ctor)
return record[1];
throw new TypeError('no comparator exists for ' + x);
var ctorToComparatorTable = [
[String, function(a, b)
return a == b;
],
[Object, function(a, b)
for (var key in a)
var avalue = a[key];
var bvalue = b[key];
var cmp = comparatorof(avalue);
if (!cmp(avalue, bvalue))
return false;
return true;
],
[Number, function(a, b)
return a == b;
],
[Boolean, function(a, b)
return a == b;
],
[Array, function(as, bs)
if (typeof bs !== 'object')
return false;
var nAs = as.length;
var nBs = bs.length;
if (nAs !== nBs)
return false;
for (var i = 0; i < nAs; ++i)
var a = as[i];
var b = bs[i];
var cmp = comparatorof(a);
if (!cmp(a, b))
return false;
return true;
],
[MyList, function(a, b)
return a.equals(b);
]
];
// true
console.log(comparatorof([])([new MyList(1, new MyList(2, 3))], [new MyList(1, new MyList(2, 3))]));
// true
console.log(comparatorof([])([, new MyList(1, new MyList(2, 3))], [, new MyList(1, new MyList(2, 3))]));
// false
console.log(comparatorof([])([, new MyList(1, new MyList(2, 3))], [, new MyList(1, new MyList(3, 3))]));
// true
console.log(comparatorof()(
1: 'one',
one: '1',
x: new MyList(1, )
,
1: 'one',
one: '1',
x: new MyList(1, )
));
// true
console.log(comparatorof(2)(
3,
3
));
//true
console.log(comparatorof(true)(
true,
true
));
//false
console.log(comparatorof([])(
[1, 2, new MyList(1, 2)], [1, 2, new MyList(4, 9)]
));
// true
console.log(comparatorof([])(
[1, 2, new MyList(1, 2), null], [1, 2, new MyList(1, 2), null]
));
// false
console.log(comparatorof(null)(
null,
undefined
));
// true
console.log(comparatorof(undefined)(
undefined,
undefined
));
// true
console.log(comparatorof(null)(
null,
null
));
一个大问题是 ES6 已经充满了与 JScript 和 Nashorn JJS 以及 ActionScript 不兼容的功能,该语言可能每隔几个月就被重新命名为一种全新的语言,考虑到这实际上是你得到的您向一种语言添加新功能会破坏与旧版本的兼容性,如果没有额外的 eval 层就无法复制。
这个问题可以追溯到很久以前,一方面你有一个像 Lisp 这样的最小语言,它“更容易”(仍然不能重载 ' 运算符)在不破坏向后兼容性的情况下引入“一流”特性,然后你拥有像 Perl 这样的语言,如果没有额外的 eval 层,就无法使用新的一流关键字进行扩展。 JavaScript 选择了第二种方法,并且通常通过使用 Math、Object 等辅助对象来引入新功能来绕过后果,但只要它想添加“第一类构造”,它就会破坏向后兼容性。
作为概念证明,在 Lisp 中,您可以重载 == 运算符,而在 JavaScript 中则不能,而在 Perl 中,您可以但通过保留关键字的机制。
【讨论】:
“因为在设计 javascript 时,他们认为元素比较是一个很少使用的功能,因此从未将其放入语言中。” 需要引用。 该死,我知道有人会抓住我编造的。我在猜测,因为它是一种附加的语言功能,当时我想不出任何其他脚本语言。很少有语言在语言级别提供列表匹配,这会引起一些混乱,因为类似 c 的语言通常通过引用比较而不是值比较来工作。不过很好,我会改写它,以便以后更慈善。【参考方案4】:Javascript 中对象的==
运算符仅检查对象是否是相同的实际对象引用,而不是检查它们是否是包含相同内容的两个独立对象。没有用于检查它们是否包含相同内容的内置运算符。您必须自己编写一个函数来进行这种比较。
只要数组元素只包含原始值(不包含其他对象),您的字符串转换就是比较两个数组的一种方法。如果数组元素可以包含其他元素,那么您必须确保这些对象本身也被转换为代表字符串。
而且,转换为字符串不会区分包含"4"
的数组元素与包含4
的数组元素,因为两者都转换为字符串表示中的"4"
。
【讨论】:
【参考方案5】:Javascript 数组是对象,您不能简单地使用相等运算符==
来了解这些对象的 content 是否相同。相等运算符只会测试两个对象是否实际上是完全相同的实例(例如myObjVariable==myObjVariable
,也适用于null
和undefined
)。
如果您需要检查两个数组是否相等,我建议您只遍历两个数组并验证所有元素是否具有相同的值(并且两个数组具有相同的长度)。
关于自定义对象的相等性,我将构建一个特定的 equals
函数并将其添加到您的类的原型中。
考虑到最终您将两个数组都转换为 String
并测试了结果字符串的相等性,您有一天可以考虑使用一种类似但更通用的技术,您会发现在多个地方都有描述:
JSON.stringify(OBJ1) === JSON.stringify(OBJ2)
好吧,不要。
虽然如果这些对象实例的属性顺序始终相同,这可能会起作用,但这为难以追踪的极其讨厌的错误打开了大门。始终倾向于更明确的方法,只需编写一个干净易读的函数来测试是否相等,检查所有必填字段。
【讨论】:
有趣的注释,myObjVariable == myObjectVariable
在所有 JS 实例中都是 true,除了 myObjVariable = NaN
... :)
有很多关于 NaN 的有趣注释,其中最著名的注释之一是 NaN.constructor === Number
。【参考方案6】:
对象的相等性会告诉您这两个对象是否同一个。
var a = [];
var b = a;
a === b; // True, a and b refer to the same object
[] === []; // False, two separate objects
您必须遍历数组以查看它们是否具有相同的元素。
见:How to check if two arrays are equal with JavaScript?
【讨论】:
【参考方案7】:一种方法是制作自己的Array
检查函数:
How to compare arrays in JavaScript?!
另一种方法是将Array
转换为String
和.join()
,然后比较字符串。然后将它们转换回Array
s 和.split()
。
【讨论】:
【参考方案8】:在 javascript 中,每个 []
都是 window.Array
类的一个实例。因此,您基本上是在尝试比较两个不同的对象。由于数组可以有任何没有。任何类型的元素,包括 Objects 和 Custom Objects 以及那些嵌套数组都可以再次拥有大量的属性和数组等等。
比较时会变得模棱两可,你永远无法确定你想对这些对象和嵌套属性做什么。因此,您通过比较尝试实现的目标可以通过许多其他方式完成。你只需要找出适合你的情况的正确方法。
【讨论】:
以上是关于为啥相等检查不适用于数组[重复]的主要内容,如果未能解决你的问题,请参考以下文章
为啥十六进制颜色不适用于 SVG 的 utf8 数据 URL [重复]