/* 关于JS相等比较算法(==)的原理 比较运算x==y, 其中x和 y是值,产生true或者false。这样的比较按如下方式进行: 1.若Type(x)与Type(y)相同, 则 a.若Type(x)为Undefined, 返回true。 b.若Type(x)为Null, 返回true。 c.若Type(x)为Number, 则 i. 若x为NaN, 返回false。 ii. 若y为NaN, 返回false。 iii.若x与y为相等数值, 返回true。 iv. 若x 为 +0 且 y为−0, 返回true。 v. 若x 为 −0 且 y为+0, 返回true。 vi. 返回false。//出自http://www.cnblogs.com/ahjesus 尊重作者辛苦劳动成果,转载请注明出处,谢谢! d.若Type(x)为String, 则当x和y为完全相同的字符序列(长度相等且相同字符在相同位置)时返回true。 否则, 返回false。 e.若Type(x)为Boolean, 当x和y为同为true或者同为false时返回true。 否则, 返回false。 f.当x和y为引用同一对象时返回true。否则,返回false。 2.若x为null且y为undefined, 返回true。 3.若x为undefined且y为null, 返回true。 //出自http://www.cnblogs.com/ahjesus 尊重作者辛苦劳动成果,转载请注明出处,谢谢! 4.若Type(x) 为 Number 且 Type(y)为String, 返回comparison x == ToNumber(y)的结果。 5.若Type(x) 为 String 且 Type(y)为Number, 返回比较ToNumber(x) == y的结果。 6.若Type(x)为Boolean, 返回比较ToNumber(x) == y的结果。 7.若Type(y)为Boolean, 返回比较x == ToNumber(y)的结果。 8.若Type(x)为String或Number,且Type(y)为Object,返回比较x == ToPrimitive(y)的结果。 9. 若Type(x)为Object且Type(y)为String或Number, 返回比较ToPrimitive(x) == y的结果。 10. 返回false。 //出自http://www.cnblogs.com/ahjesus 尊重作者辛苦劳动成果,转载请注明出处,谢谢! */ function Equals(x, y) { if (typeof (x) == typeof (y)) {//若Type(x)与Type(y)相同, 则 if (typeof (x) == "Undefined") {//若Type(x)为Undefined, 返回true return true; } if (typeof (x) == "Null") {//若Type(x)为Null, 返回true return true; } //出自http://www.cnblogs.com/ahjesus 尊重作者辛苦劳动成果,转载请注明出处,谢谢! if (typeof (x) == "Number") {//若Type(x)为Number, 则 if (x == NaN) {//若x为NaN, 返回false return false; } if (y = NaN) {//若y为NaN, 返回false return false; } if (x == y) {//若x与y为相等数值, 返回true return true; } if (x == +0 && y == -0) {//若x 为 +0 且 y为−0, 返回true return true; } if (x == -0 && y == +0) {//若x 为 −0 且 y为+0, 返回true return true; } return false; //返回false } //出自http://www.cnblogs.com/ahjesus 尊重作者辛苦劳动成果,转载请注明出处,谢谢! if (typeof (x) == "String") {//若Type(x)为String, 则 var xChars = x.split(''); var yChars = y.split(''); var i = xChars.length; while (i--) { if (xChars[i] != yChars[i]) {//当x和y为完全相同的字符序列(长度相等且相同字符在相同位置)时返回true return false; } } return true; //否则, 返回false } if (typeof (x) == "Boolean") {//若Type(x)为Boolean, 则 if (x && y) {//当x和y为同为true或者同为false时返回true return true; } return false; //否则, 返回false } if (compare(a, b)) {//当x和y为引用同一对象时返回true return true; } return false; //否则,返回false } //出自http://www.cnblogs.com/ahjesus 尊重作者辛苦劳动成果,转载请注明出处,谢谢! if (x == null && y == undefined) {//若x为null且y为undefined, 返回true return true; } if (x == undefined && y == null) {//若x为undefined且y为null, 返回true return true; } if (typeof (x) == "Number" && typeof (y) == "String") {//若Type(x) 为 Number 且 Type(y)为String, 返回comparison x == ToNumber(y)的结果 return x == Number(y); } if (typeof (x) == "String" && typeof (y) == "Number") {//若Type(x) 为 String 且 Type(y)为Number, 返回比较ToNumber(x) == y的结果 return Number(x) == y; } if (typeof (x) == "Boolean") {//若Type(x)为Boolean, 返回比较ToNumber(x) == y的结果 return Number(x) == y; } if (typeof (y) == "Boolean") {//若Type(y)为Boolean, 返回比较x == ToNumber(y)的结果 return x == Number(y); } if ((typeof (x) == "String" || typeof (x) == "Number") && typeof (y) == "Object") {//若Type(x)为String或Number,且Type(y)为Object,返回比较x == ToPrimitive(y)的结果 return x == y; } if (typeof (x) == "Object" && (typeof (y) == "String" || typeof (y) == "Number")) {//若Type(x)为Object且Type(y)为String或Number, 返回比较ToPrimitive(x) == y的结果 return x == y; } //出自http://www.cnblogs.com/ahjesus 尊重作者辛苦劳动成果,转载请注明出处,谢谢! return false; //返回false } function compare(a, b) { var pt = /undefined|number|string|boolean/, fn = /^(function\s*)(\w*\b)/, cr = "constructor", cn = "childNodes", pn = "parentNode", ce = arguments.callee; if (pt.test(typeof a) || pt.test(typeof b) || a === null || b === null) { return a === b || (isNaN(a) && isNaN(b)); //为了方便,此处假定NaN == NaN } if (a[cr] !== b[cr]) { return false; } switch (a[cr]) { case Date: { return a.valueOf() === b.valueOf(); }; case Function: { return a.toString().replace(fn, '$1') === b.toString().replace(fn, '$1'); //硬编码中声明函数的方式会影响到toString的结果,因此用正则进行格式化 }; case Array: { if (a.length !== b.length) { return false; } for (var i = 0; i < a.length; i++) { if (!ce(a[i], b[i])) { return false; } } break; }; default: { var alen = 0, blen = 0, d; if (a === b) { return true; } if (a[cn] || a[pn] || b[cn] || b[pn]) { return a === b; } for (d in a) { alen++; } for (d in b) { blen++; } if (alen !== blen) { return false; } for (d in a) { if (!ce(a[d], b[d])) { return false; } } break; }; } return true; } function ToPrimitive(value) { /* 关于ToPrimitive()方法说明: ToPrimitive 运算符接受一个值,和一个可选的 期望类型 作参数。ToPrimitive 运算符把其值参数转换为非对象类型。如果对象有能力被转换为不止一种原语类 型,可以使用可选的 期望类型 来暗示那个类型。根据下表完成转换: ToPrimitive转换 输入类型 结果 Undefined 结果等于输入的参数(不转换)。 Null 结果等于输入的参数(不转换)。 Boolean 结果等于输入的参数(不转换)。 Number 结果等于输入的参数(不转换)。 String 结果等于输入的参数(不转换)。 Object 返回该对象的默认值。(调用该对象的内部方法[[DefaultValue]]一樣)。 [[DefaultValue]] : 对象共有的内部属性,取值范围SpecOp (Hint) → primitive Hint 是一个字符串。返回对象的默认值 //出自http://www.cnblogs.com/ahjesus 尊重作者辛苦劳动成果,转载请注明出处,谢谢! 当用字符串 hint 调用 O 的 [[DefaultValue]] 内部方法,采用以下步骤: 1. 令 toString 为用参数 "toString" 调用对象 O 的 [[Get]] 内部方法的结果。 2. 如果 IsCallable(toString) 是 true,则 a. 令 str 为用 O 作为 this 值,空参数列表调用 toString 的 [[Call]] 内部方法的结果。 b. 如果 str 是原始值,返回 str。 3. 令 valueOf 为用参数 "valueOf" 调用对象 O 的 [[Get]] 内部方法的结果。 4. 如果 IsCallable(valueOf) 是 true,则 a. 令 val 为用 O 作为 this 值,空参数列表调用 valueOf 的 [[Call]] 内部方法的结果。 b. 如果 val 是原始值,返回 val。 5. 抛出一个 TypeError 异常。 当用数字 hint 调用 O 的 [[DefaultValue]] 内部方法,采用以下步骤: 1. 令 valueOf 为用参数 "valueOf" 调用对象 O 的 [[Get]] 内部方法的结果。 2. 如果 IsCallable(valueOf) 是 true,则 a. 令 val 为用 O 作为 this 值,空参数列表调用 valueOf 的 [[Call]] 内部方法的结果。 b. 如果 val 是原始值,返回 val。 3. 令 toString 为用参数 "toString" 调用对象 O 的 [[Get]] 内部方法的结果。 4. 如果 IsCallable(toString) 是 true,则 a. 令 str 为用 O 作为 this 值,空参数列表调用 toString 的 [[Call]] 内部方法的结果。 b. 如果 str 是原始值,返回 str。 5. 抛出一个 TypeError 异常 当不用 hint 调用 O 的 [[DefaultValue]] 内部方法,O 是 Date 对象的情况下 仿佛 hint 是字符串一样解释它的行为,除此之外仿佛 hint 是数字一样解释它的行为。 上面说明的 [[DefaultValue]] 在原生对象中只能返回原始值。如果一个宿主对象 实现了它自身的 [[DefaultValue]] 内部方法,那么必须确保其 [[DefaultValue]] 内部方法只能返回原始值。 */ }