所有分类
  • 所有分类
  • 后端开发

JavaScript中的数字到底是相等?揭秘背后的硬道理

哈喽!告诉大家一件小事儿,不知道你有没有想过,在JavaScript里,-0其实就是等于+0喔?不过这俩可都不是真真的0。要是对这个问题感兴趣的话,可以去看看Harmony egal proposal那篇文章,详细解释就在那里。

你造吗?其实JavaScript中的数组就像个小纸条夹!尽管外表看似相同,但是它们可不会说“我们是同一份哟~”。如果你想搞清楚为啥子,那就赶紧去瞅瞅它们的源头~

var eq = function (a, b, aStack, bStack) {
	// Identical objects are equal. `0 === -0`, but they aren't identical.
	// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
	//除了0 === -0这个特例之外,其余所有a === b的例子都代表它们相等。
	//应当判断0 !== -0,但是JavaScript中0 === -0。
	//下面这行代码就是为了解决这个问题。
	//当a !== 0或者1/a === 1/b时返回true,一旦a === 0并且1/a !== 1/b就返回false。
	//而a === 0且1/a !== 1/b就代表a,b有一个为0,有一个为-0。
	if (a === b) return a !== 0 || 1 / a === 1 / b;
	//一旦a、b不严格相等,就进入后续检测。
	//a == b成立但是a === b不成立的例子中需要排除null和undefined,其余例子需要后续判断。
	// `null` or `undefined` only equal to itself (strict comparison).
	//一旦a或者b中有一个为null就代表另一个为undefined,这种情况可以直接排除。
	if (a == null || b == null) return false;
	// `NaN`s are equivalent, but non-reflexive.
	//自身不等于自身的情况,一旦a,b都为NaN,则可以返回true。
	if (a !== a) return b !== b;
	// Exhaust primitive checks
	//如果a,b都不为JavaScript对象,那么经过以上监测之后还不严格相等的话就可以直接断定a不等于b。
	var type = typeof a;
	if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
	//如果a,b是JavaScript对象,还需要做后续深入的判断。
	return deepEq(a, b, aStack, bStack);
};

var deepEq = function (a, b, aStack, bStack) {
	// Unwrap any wrapped objects.
	//如果a,b是_的一个实例的话,需要先把他们解包出来再进行比较。
	if (a instanceof _) a = a._wrapped;
	if (b instanceof _) b = b._wrapped;
	// Compare `[[Class]]` names.
	//先根据a,b的Class字符串进行比较,如果两个对象的Class字符串都不一样,
	//那么直接可以认为两者不相等。
	var className = toString.call(a);
	if (className !== toString.call(b)) return false;
	//如果两者的Class字符串相等,再进一步进行比较。
	//优先检测内置对象之间的比较,非内置对象再往后检测。
	switch (className) {
		// Strings, numbers, regular expressions, dates, and booleans are compared by value.
		//如果a,b为正则表达式,那么转化为字符串判断是否相等即可。
		case '[object RegExp]':
		// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
		case '[object String]':
			// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
			// equivalent to `new String("5")`.
			//如果a, b是字符串对象,那么转化为字符串进行比较。因为一下两个变量:
			//var x = new String('12');
			//var y = new String('12');
			//x === y是false,x === y也是false,但是我们应该认为x与y是相等的。
			//所以我们需要将其转化为字符串进行比较。
			return '' + a === '' + b;
		case '[object Number]':
			//数字对象转化为数字进行比较,并且要考虑new Number(NaN) === new Number(NaN)应该要成立的情况。
			// `NaN`s are equivalent, but non-reflexive.
			// Object(NaN) is equivalent to NaN.
			if (+a !== +a) return +b !== +b;
			// An `egal` comparison is performed for other numeric values.
			//排除0 === -0 的情况。
			return +a === 0 ? 1 / +a === 1 / b : +a === +b;
		case '[object Date]':
		//Date类型以及Boolean类型都可以转换为number类型进行比较。
		//在变量前加一个加号“+”,可以强制转换为数值型。
		//在Date型变量前加一个加号“+”可以将Date转化为毫秒形式;Boolean类型同上(转换为0或者1)。
		case '[object Boolean]':
			// Coerce dates and booleans to numeric primitive values. Dates are compared by their
			// millisecond representations. Note that invalid dates with millisecond representations
			// of `NaN` are not equivalent.
			return +a === +b;
		case '[object Symbol]':
			return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
	}
 
	var areArrays = className === '[object Array]';
	//如果不是数组对象。
	if (!areArrays) {
		if (typeof a != 'object' || typeof b != 'object') return false;
 
		// Objects with different constructors are not equivalent, but `Object`s or `Array`s
		// from different frames are.
		//比较两个非数组对象的构造函数。
		var aCtor = a.constructor, bCtor = b.constructor;
		if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
			_.isFunction(bCtor) && bCtor instanceof bCtor)
			&& ('constructor' in a && 'constructor' in b)) {
			return false;
		}
	}
	// Assume equality for cyclic structures. The algorithm for detecting cyclic
	// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
 
	// Initializing stack of traversed objects.
	// It's done here since we only need them for objects and arrays comparison.
	//初次调用eq函数时,aStack以及bStack均未被传递,在循环递归的时候,会被传递进来。
	//aStack和bStack存在的意义在于循环引用对象之间的比较。
	aStack = aStack || [];
	bStack = bStack || [];
	var length = aStack.length;
	
	while (length--) {
		// Linear search. Performance is inversely proportional to the number of
		// unique nested structures.
		if (aStack[length] === a) return bStack[length] === b;
	}
 
	// Add the first object to the stack of traversed objects.
	//初次调用eq函数时,就把两个参数放入到参数堆栈中去,保存起来方便递归调用时使用。
	aStack.push(a);
	bStack.push(b);
 
	// Recursively compare objects and arrays.
	//如果是数组对象。
	if (areArrays) {
		// Compare array lengths to determine if a deep comparison is necessary.
		length = a.length;
		//长度不等,直接返回false认定为数组不相等。
		if (length !== b.length) return false;
		// Deep compare the contents, ignoring non-numeric properties.
		while (length--) {
			//递归调用。
			if (!eq(a[length], b[length], aStack, bStack)) return false;
		}
	} else {
		// Deep compare objects.
		//对比纯对象。
		var keys = _.keys(a), key;
		length = keys.length;
		// Ensure that both objects contain the same number of properties before comparing deep equality.
		//对比属性数量,如果数量不等,直接返回false。
		if (_.keys(b).length !== length) return false;
		while (length--) {
			// Deep compare each member
			key = keys[length];
			if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
		}
	}
	// Remove the first object from the stack of traversed objects.
	//循环递归结束,把a,b堆栈中的元素推出。
	aStack.pop();
	bStack.pop();
	return true;
};

你不是?其实JavaScript里的“2”跟数字类型的2可并非一码事,头痛!既然不一样,那就当做它们不等好了。而且,Symbol类型的比较也是有点花头的,要用Symbol.prototype.valueOf先把它变成字符串再比。

别急着下结论!还剩最后一个问题。得等我们把所有的都查遍了,确认没有毛病了,才能确定两个数字是否相同;如果发现其中一个有问题,那就肯定是不同的。这些都是硬道理,咱得弄明白。

告诉你件事儿,当underscore遇到RegExp对象的时候,它会自动处理成字符串形式。咋回事儿?别急我给你解释下。其实,只要加上”+”号,就能把Date对象或Boolean对象转变成数字,是不是很神奇

给你分享个小秘诀!如果你看到两个东西感觉差不多,想要比对一下参数的话,别忘了先检查一下它们是不是同一类型哟?如果不是,就说明它们肯定不等。记住这个,以后再用Underscore的时候,就不会出错!

var a = {
  name: 'test'
};
a['test1'] = a;
var b = {
  name: 'test'
};
b['test1'] = b;
_.isEqual(a, b);
//true

嘿哥们,咱上次聊的那啥绝招,undersocre现在就能搞定ES6里出现的那些东西了!比如说Map和Set这俩新玩意儿,undersocre已经完全拿下。而且用_.isEqual来比较这俩是不是一个的话,放心,肯定没问题,早就经过严格测试了。

aStack = aStack || [];
bStack = bStack || [];
var length = aStack.length;
while (length--) {
  // Linear search. Performance is inversely proportional to the number of
  // unique nested structures.
  if (aStack[length] === a) return bStack[length] === b;
}

让我来告诉你,想知道他们两个是不是相等,首先要看他们的长度呀。真不巧,那就不用费劲儿比对,回头咱就说它们不一样好了。别小看这个方法,它是有点耍点小心思的。不管你传哪个数(比如Map或者Set),我们就是先把它们的成员(例如a和b的key值)抓出来看看;然后再看看是什么类型,如果是Map的话,咱们还得再看下他们的value域(也就是a和b的val值)。虽然有时候太死板,但是对于Set和 Map这类东西,还是好用的很。今天说的这些,都是关于JavaScript里“==”到底是什么意思的事情。你觉得怎么样?如果觉得有趣,就在这儿留言跟大家分享一下!别忘了给文章点赞

原文链接:https://www.icz.com/technicalinformation/web3/2024/03/11349.html,转载请注明出处~~~
0

评论0

请先
注意:请收藏好网址www.icz.com,防止失联!站内免费资源持续上传中…!赞助我们
显示验证码
没有账号?注册  忘记密码?