js中判斷變量是否相等的幾種方式

無論是在開發中,還是面試時,在js中判斷變量是否相等,都是一個比較常見的問題。比較典型的有,== === 的區別?判斷變量是否相等有哪些方式?

這裏打算總結一下判斷變量是否相等的方式,以及做一些簡單的分析。

判斷變量相等的方式

判斷變量是否相等,大致有以下一些方式

  • == 以及 ===
  • 利用數組中的toString方法;
  • ES6中的Object.is方法;
  • 利用JSON.stringify,轉換爲String類型來比較;
  • 自定義方法實現;

=====

==爲轉換類型比較運算符,===爲嚴格比較運算符,在數據類型相同的情況下,使用==一般不會造成問題。

let num = 5;
let num2 = 5;

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

但在數據類型不一致的情況下,==會做一些隱性的類型轉換。

let num = 5;
let str = '5';

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

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

null == undefined;//true
null === undefined;//false

隱性轉換類型可以帶來一些便利性,但也有可能造成不易發現的bug,所以還是更推薦使用===來進行比較,這也是TS之所以流行的原因之一。

此外,上面列舉的都是基本數據類型的比較,而在用===比較引用類型時,會存在一定的侷限性。

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方法就比較有用了,這一方法也能用作數組比較。

let arr = [1,3,5,7,9];
let arr2 = [1,3,5,7,9];

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

不過也存在一定的侷限性,不能用來比較二維及以上的數組、不能包含nullundefinedobjectfunction等,否則容易出錯,如下

[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中新增的方法,與===非常類似,同樣用作比較兩個值是否相等。

Object.is(1,1);//true
Object.is('str','str');//true
Object.is({},{});//false

不同的是在判斷+0-0NaNNaN時的區別。

+0 === -0 //true
NaN === NaN //false

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

在處理兼容性問題時,polyfill可以這麼寫。

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字符串,得出的字符串便可以用作對象的比較。

let obj = {name: 'lin', age: 24};
let obj2 = {name: 'lin', age: 24};

obj === obj2;//false
JSON.stringify(obj) === JSON.stringify(obj2);//true

JSON.stringify彌補了===無法準確比較對象的侷限,不過它也有一定的侷限性,在遇到undefinedfunction以及symbol值時會忽略。

另外,值得一提的是利用JSON.parseJSON.stringify可實現對象深拷貝,侷限性同上。

自定義方法

上面介紹的方法各有其用處及侷限,如果要找一個覆蓋更多使用場景的方法,無疑需要自己造輪子了,不過這裏更推薦underscoreisEqual方法,畢竟是現有經得起考驗的。

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);
};
_.isEqual(NaN,NaN);//true
_.isEqual(1,'1');//false
_.isEqual({},{});//true

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

這裏推薦我的前一篇文章,【總結】做一個自己的前端js工具庫,把常用方法封裝到自己的工具庫中,可以便於後續使用。

何時使用它們

不一樣的場景可能有不一樣的需求,如果只比較基本數據類型,那麼===就足夠了,如果想“一勞永逸”,那麼付出一定的成本使用自定義方法無疑是有必要的。

在日常反覆的開發中,還是要多多探索吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章