A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 逆风TO 黑马粉丝团   /  2019-12-23 11:55  /  5576 人查看  /  150 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 逆风TO 于 2019-12-23 11:56 编辑

无论是在开发中,还是面试时,在js中判断变量是否相等,都是一个比较常见的问题。比较典型的有,== 和 === 的区别?判断变量是否相等有哪些方式?
这里打算总结一下判断变量是否相等的方式,以及做一些简单的分析。
判断变量相等的方式
判断变量是否相等,大致有以下一些方式

== 以及 ===;
利用数组中的toString方法;
ES6中的Object.is方法;
利用JSON.stringify,转换为String类型来比较;
自定义方法实现;
== 及 ===
==为转换类型比较运算符,===为严格比较运算符,在数据类型相同的情况下,使用==一般不会造成问题。
[JavaScript] 纯文本查看 复制代码
let num = 5;
let num2 = 5;

num == num2;//true
num === num2;//true

但在数据类型不一致的情况下,==会做一些隐性的类型转换。
[JavaScript] 纯文本查看 复制代码
let num = 5;
let str = '5';

num == str;//true
num === str;//false

'' == false;//true
'' === false;//false

null == undefined;//true
null === undefined;//false
[size=2]
[/size][p=26, null, left][color=rgb(77, 77, 77)][font="][size=2]隐性转换类型可以带来一些便利性,但也有可能造成不易发现的bug,所以还是更推荐使用===来进行比较,这也是TS之所以流行的原因之一。[/size][/font][/color][/p][p=26, null, left][color=rgb(77, 77, 77)][font="][size=2]此外,上面列举的都是基本数据类型的比较,而在用===比较引用类型时,会存在一定的局限性。[/size][/font][/color][/p][p=26, null, left][color=rgb(77, 77, 77)][font="][size=2][mw_shl_code=javascript,true]let a = {xx: 1};
let b = a;

a === b;//true

let c = {xx: 1};
let d = {xx: 1};

c === d;//false

在比较引用类型时,===比较的是变量的引用是否相同,而非值,当引用不同时,就会返回false。

由此可见,===并不是一枚无往不利的银弹,在比较对象是否相等时,还需要借助其他更可靠的方法。

Array toString方法

前端给后端传参时,后端有时会要求多个参数,隔开,Array toString方法就比较有用了,这一方法也能用作数组比较。

[JavaScript] 纯文本查看 复制代码
let arr = [1,3,5,7,9];
let arr2 = [1,3,5,7,9];

arr.toString() === arr2.toString();//true "1,3,5,7,9"

不过也存在一定的局限性,不能用来比较二维及以上的数组、不能包含null、undefined、object、function等,否则容易出错,如下

[JavaScript] 纯文本查看 复制代码
[1,3,5,[2,4,6]].toString();//"1,3,5,2,4,6"
[1,null,undefined,'',2].toString();//"1,,,,2"
[{xx:2},window,1,2].toString();//"[object Object],[object Window],1,2"

Object.is方法

Object.is是ES6中新增的方法,与===非常类似,同样用作比较两个值是否相等。

[AppleScript] 纯文本查看 复制代码
Object.is(1,1);//true
Object.is('str','str');//true
Object.is({},{});//false

不同的是在判断+0和-0、NaN和NaN时的区别。

[AppleScript] 纯文本查看 复制代码
+0 === -0 //true
NaN === NaN //false

Object.is(+0, -0) //false
Object.is(NaN, NaN) //true

在处理兼容性问题时,polyfill可以这么写。

[AppleScript] 纯文本查看 复制代码
if (!Object.is) {
  Object.is = function(x, y) {
    if (x === y) {
      // 针对+0 不等于 -0的情况
      return x !== 0 || 1 / x === 1 / y;
    } else {
     // 针对NaN的情况
      return x !== x && y !== y;
    }
  };
}

JSON.stringify

JSON.stringify方法用于把对象或者数组转换为一个 JSON字符串,得出的字符串便可以用作对象的比较。

JSON.stringify弥补了===无法准确比较对象的局限,不过它也有一定的局限性,在遇到undefined、function以及symbol值时会忽略。

另外,值得一提的是利用JSON.parse、JSON.stringify可实现对象深拷贝,局限性同上。

自定义方法

上面介绍的方法各有其用处及局限,如果要找一个覆盖更多使用场景的方法,无疑需要自己造轮子了,不过这里更推荐underscore的isEqual方法,毕竟是现有经得起考验的。

[JavaScript] 纯文本查看 复制代码
var eq, deepEq;
eq = function(a, b, aStack, bStack) {
  if (a === b) return a !== 0 || 1 / a === 1 / b;
  if (a == null || b == null) return false;
  if (a !== a) return b !== b;
  var type = typeof a;
  if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
  return deepEq(a, b, aStack, bStack);
};

deepEq = function(a, b, aStack, bStack) {
  if (a instanceof _) a = a._wrapped;
  if (b instanceof _) b = b._wrapped;
  var className = toString.call(a);
  if (className !== toString.call(b)) return false;
  switch (className) {
  case '[object RegExp]':
  case '[object String]':
    return '' + a === '' + b;
  case '[object Number]':
    if ( + a !== +a) return + b !== +b;
    return + a === 0 ? 1 / +a === 1 / b: +a === +b;
  case '[object Date]':
  case '[object Boolean]':
    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;

    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;
    }
  }

  aStack = aStack || [];
  bStack = bStack || [];
  var length = aStack.length;
  while (length--) {
    if (aStack[length] === a) return bStack[length] === b;
  }

  aStack.push(a);
  bStack.push(b);

  if (areArrays) {
    length = a.length;
    if (length !== b.length) return false;
    while (length--) {
      if (!eq(a[length], b[length], aStack, bStack)) return false;
    }
  } else {
    var keys = _.keys(a),
    key;
    length = keys.length;
    if (_.keys(b).length !== length) return false;
    while (length--) {
      key = keys[length];
      if (! (_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
    }
  }
  aStack.pop();
  bStack.pop();
  return true;
};

_.isEqual = function(a, b) {
  return eq(a, b);
};

[JavaScript] 纯文本查看 复制代码
_.isEqual(NaN,NaN);//true
_.isEqual(1,'1');//false
_.isEqual({},{});//true

underscore的isEqual方法大致如上,感兴趣的可以自己稍加修改,移植到自己的常用方法中。


这里推荐我的前一篇文章,【总结】做一个自己的前端js工具库,把常用方法封装到自己的工具库中,可以便于后续使用。


何时使用它们

不一样的场景可能有不一样的需求,如果只比较基本数据类型,那么===就足够了,如果想“一劳永逸”,那么付出一定的成本使用自定义方法无疑是有必要的。

在日常反复的开发中,还是要多多探索吧。


150 个回复

倒序浏览
辛苦了  加油哦!
回复 使用道具 举报
感谢分享
回复 使用道具 举报
66666666666666666
回复 使用道具 举报
六六六六六六六六六六六六六六六六六六六六六六六六
回复 使用道具 举报
哇  厉害了!!!!
回复 使用道具 举报
棒棒的加油哦
回复 使用道具 举报
感谢分享   谢谢楼主的分享
回复 使用道具 举报
66666666666666666
回复 使用道具 举报
666666666666666666666
回复 使用道具 举报
看到代码就头大的我表示佩服
回复 使用道具 举报
辛苦分享~
回复 使用道具 举报
谢谢分享
回复 使用道具 举报
加油加油            
回复 使用道具 举报
666666666666666
回复 使用道具 举报
6666666666666666666666
回复 使用道具 举报
66666666666666666666666666666666666
回复 使用道具 举报
6666666666666666666
回复 使用道具 举报
回复 使用道具 举报

棒棒的66666
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马