Начните с вопроса на собеседовании - неявное преобразование js, наступающее на коллекцию pit

внешний интерфейс JavaScript

Напоминание впереди: длина статьи длинная, пожалуйста, поставьте лайк или добавьте ее в закладки, вы легко найдете ее, когда будете читать ее в следующий раз.

Когда дело доходит до неявного преобразования js, первая реакция многих людей: pit.

Действительно, для непосвященныхjs隐式转换Есть много непредсказуемых мест, и я считаю, что многие люди глубоко затронуты, поэтому в процессе разработки вы можете использовать===чтобы попытаться избежать неявных преобразований. Однако для более глубокого пониманияjavascript, в духе тяги к знаниям, давайте разбирать и анализировать на большом количестве примеровjs隐式转换,знакомыйjs隐式转换правила, сделайте это "явным" в ваших глазах.

Начните с вопроса интервью

Давайте посмотрим на классический вопрос интервью

определить переменнуюa, так что следующее выражение приводит кtrue

  a == 1 && a == 2 && a == 3

И такая операция? Давайте сначала попробуем определитьa = true?

  var a = true
  a == 1 && a == 2 && a == 3 // false

Однако это не оправдало ожиданий, словно задев слепое пятно знаний. . . Неважно, давайте сначала положим его, давайте заглянем еще в несколько ям

  [] == ![] // true

  [] == 0 // true
  
  [2] == 2 // true

  ['0'] == false // true

  '0' == false // true

  [] == false // true

  [null] == 0 // true

  null == 0 // false

  [null] == false // true

  null == false // false

  [undefined] == false // true

  undefined == false // false

Смущенный? Это не имеет значения! Далее я принесу вам полное пониманиеjavascript的隐式转换.

правила неявного преобразования javascript

1. ToString, ToNumber, ToBoolean, ToPrimitive

Нам нужно понять основные правила преобразования между типами данных js, такие как взаимное преобразование между числами, строками, логическими значениями, массивами и объектами.

1.1 ToString

что здесь сказаноToStringне возражаюtoString方法, но относится к операции преобразования значения другого типа в строковый тип.

Здесь мы обсуждаемnull,undefined,布尔型,数字,数组,普通对象Правила преобразования в строки.

  • ноль: преобразовать в"null"
  • undefined: преобразовать в"undefined"
  • Логический тип:trueа такжеfalseбыли преобразованы в"true"а также"false"
  • Тип числа: преобразование в строковую форму числа, например10Перевести в"10",1e21Перевести в"1e+21"
  • Массив: Преобразование в строку заключается в соединении всех элементов в соответствии с «,», что эквивалентно вызову массиваArray.prototype.join()такие методы, как[1, 2, 3]Перевести в"1,2,3", пустой массив[]Преобразовать в пустую строку в массивеnullилиundefined, будет рассматриваться как пустая строка
  • Обычный объект: преобразование в строку эквивалентно его непосредственному использованию.Object.prototype.toString(),вернуть"[object Object]"
  String(null) // 'null'
  String(undefined) // 'undefined'
  String(true) // 'true'
  String(10) // '10'
  String(1e21) // '1e+21'
  String([1,2,3]) // '1,2,3'
  String([]) // ''
  String([null]) // ''
  String([1, undefined, 3]) // '1,,3'
  String({}) // '[object Objecr]'

объектtoStringспособ удовлетворитьToStringправила эксплуатации.

Примечание. Упомянутые выше правила действуют по умолчанию, если вы измените настройки по умолчанию.toString()методы, которые приводят к разным результатам

1.2 ToNumber

ToNumberОтносится к операции преобразования других типов в числовые типы.

  • ноль: преобразовать в0
  • undefined: преобразовать вNaN
  • Строка: если это чистое число, оно будет преобразовано в соответствующее число, а пустые символы будут преобразованы в0, в противном случае это будет считаться ошибкой преобразования и преобразовано вNaN
  • Логический:trueа такжеfalseбыть преобразован в1а также0
  • Массивы. Массивы сначала преобразуются в примитивные типы, т.ToPrimitive, а затем обработаны в соответствии с указанными выше правилами в соответствии с преобразованным исходным типом, примерноToPrimitive, который будет описан ниже
  • Объект: обработка того же массива
  Number(null) // 0
  Number(undefined) // NaN
  Number('10') // 10
  Number('10a') // NaN
  Number('') // 0 
  Number(true) // 1
  Number(false) // 0
  Number([]) // 0
  Number(['1']) // 1
  Number({}) // NaN

1.3 ToBoolean

ToBooleanОтносится к операциям, которые преобразуют другие типы в логические типы.

ложное значение только в jsfalse,null,undefined,空字符,0а такжеNaN, другие значения преобразуются в логические значения какtrue.

  Boolean(null) // false
  Boolean(undefined) // false
  Boolean('') // flase
  Boolean(NaN) // flase
  Boolean(0) // flase
  Boolean([]) // true
  Boolean({}) // true
  Boolean(Infinity) // true

1.4 ToPrimitive

ToPrimitiveОтносится к операции преобразования типов объектных типов (таких как объекты, массивы) в примитивные типы.

  • Когда тип объекта необходимо преобразовать в примитивный тип, он сначала ищет тип объекта.valueOfметод, еслиvalueOfметод возвращает значение примитивного типа, тогдаToPrimitiveРезультат - это значение
  • еслиvalueOfне существует илиvalueOfЕсли метод возвращает значение, не являющееся примитивным типом, он попытается вызвать функцию объекта.toStringметод, то есть будет следовать за объектомToStringправила, затем используйтеtoStringвозвращаемое значение какToPrimitiveрезультат.

Примечание. Для различных типов объектовToPrimitiveПравила разные, напримерDate对象Мы сначала позвонитьtoString, вы можете обратиться кстандарт ECMA

еслиvalueOfа такжеtoStringЕсли ни один из них не возвращает значение исходного типа, будет выдано исключение.

  Number([]) // 0
  Number(['10']) //10

  const obj1 = {
    valueOf () {
      return 100
    },
    toString () {
      return 101
    }
  }
  Number(obj1) // 100

  const obj2 = {
    toString () {
      return 102
    }
  }
  Number(obj2) // 102

  const obj3 = {
    toString () {
      return {}
    }
  }
  Number(obj3) // TypeError

Как упоминалось ранее, тип объектаToNumberпридет первымToPrimitive, а затем в соответствии с преобразованным оригинальным типомToNumber

  • Number([]), сначала будет вызван пустой массивvalueOf, но сам возвращаемый массив не является примитивным типом, поэтому он продолжит вызыватьtoString,получать空字符串, эквивалентноNumber(''), поэтому преобразованный результат"0"
  • По аналогии,Number(['10'])эквивалентноNumber('10'), получил ответ10
  • obj1изvalueOfметод возвращает примитивный тип100,такToPrimitiveРезультат100
  • obj2нетvalueOf, но существуетtoStringи возвращает примитивный тип, такNumber(obj2)Результат102
  • obj3изtoStringВозвращаемый метод не является примитивным типом и не можетToPrimitive, поэтому выдает ошибку

Увидев это, вы думаете, что освоили его полностью? Не забывайте, что этот вопрос интервью и эта куча запутанных суждений еще не решены.В духе жажды знаний, продолжайте читать.

2. Неявные правила преобразования для нестрогих сравнений на равенство (==)

宽松相等(==)а также严格相等(===)Разница в том, что при сравнении будет выполнено нечеткое равенство隐式转换. Теперь рассмотрим правила конвертации в разных случаях.

2.1 Сравнение равенства между логическими типами и другими типами

  • если только布尔类型Для участия в сравнении布尔类型Значение сначала преобразуется в数字类型
  • согласно с布尔类型изToNumberправило,trueПеревести в1,falseПеревести в0
  false == 0 // true
  true == 1 // true
  true == 2 // false

Кому-то может показаться, что цифры2является истинностным значением, поэтомуtrue == 2Это должно быть правдой, теперь понять, булевой типtrueУчастие в сравнениях на равенство сначала преобразуется в числа.1, эквивалентно1 == 2, результат конечноfalse

мы обычно используемifПри судействе обычно пишут так

  const x = 10
  if (x) {
    console.log(x)
  }

здесьif(x)изxбудет преобразован в логический тип здесь, поэтому код может выполняться нормально. Но если написать так:

  const x = 10
  if (x == true) {
    console.log(x)
  }

Код не будет выполняться должным образом, потому чтоx == trueэквивалентно10 == 1

2.2 Сравнение равенства между числовыми и строковыми типами

  • когда数字类型а также字符串类型При сравнении равенства字符串类型будет преобразован в数字类型
  • судя по строкеToNumberПравила, если это строка в виде чистого числа, она преобразуется в соответствующее число, а нулевой символ преобразуется в0, в противном случае это будет считаться ошибкой преобразования и преобразовано вNaN
  0 == '' // true
  1 == '1' // true
  1e21 == '1e21' // true
  Infinity == 'Infinity' // true
  true == '1' // true
  false == '0' // true
  false == '' // true

Являются ли результаты сравнения выше, чем вы ожидали? По правилам строки преобразуются в числа, а логические значения преобразуются в числа, так что результат очевиден.

не обсуждается здесьNaN, потому чтоNaNи не равен никакому значению, в том числе самому себе.

2.3 Сравнение равенства между объектными типами и примитивными типами

  • когда对象类型а также原始类型При сравнении равенства对象类型последуетToPrimitiveПравила преобразуются в原始类型
  '[object Object]' == {} // true
  '1,2,3' == [1, 2, 3] // true

Посмотрите на пример, приведенный в начале статьи

  [2] == 2 // true

множество[2]является типом объекта, поэтомуToPrimitiveоперация, то есть вызов сначалаvalueOfпозвони сноваtoString, по массивуToStringправила работы, получат результаты"2", в то время как строка"2"2При сравнении он будет сначала преобразован в числовой тип, поэтому окончательный результатtrue.

  [null] == 0 // true
  [undefined] == 0 // true
  [] == 0 // true

По указанному выше массивуToStringПравила работы, элементы массиваnullилиundefined, элемент рассматривается как空字符串обработка, а пустой массив[]также преобразован в空字符串, поэтому приведенный выше код эквивалентен

  '' == 0 // true
  '' == 0 // true
  '' == 0 // true

空字符串будут преобразованы в числа0, так что результатtrue.

попробуйте метод valueOf

  const a = {
    valueOf () {
      return 10
    }
    toString () {
      return 20
    }
  }
  a == 10 // true

объектToPrimitiveСначала будет вызвана операцияvalueOfметод иaизvalueOfметод возвращает значение примитивного типа, поэтомуToPrimitiveРезультат операции естьvalueOfвозвращаемое значение метода10.

Кстати говоря, вы думали о первых вопросах интервью? объект каждый раз и примитивный тип делать==При сравнении будет сделано один разToPrimitiveоперацию, то можем ли мы определить содержащуюvalueOfобъект метода, а затем реализовать его, накопив определенное значение?

попробуй

  const a = {
    // 定义一个属性来做累加
    i: 1,
    valueOf () {
      return this.i++
    }
  }
  a == 1 && a == 2 && a == 3 // true

Результат правильный, как и следовало ожидать. Конечно, когда нет определенияvalueOfметод, использованиеtoStringметод тоже возможен.

  const a = {
    // 定义一个属性来做累加
    i: 1,
    toString () {
      return this.i++
    }
  }
  a == 1 && a == 2 && a == 3 // true

2.4 Сравнение нулевого, неопределенного и других типов

  • nullа такжеundefinedРезультат свободного равенства верен, как мы все знаем

Второй,nullа такжеundefinedявляются ложными значениями, то

  null == false // false
  undefined == false // false

Это отличается от того, что я думал? Зачем? первый,falseПеревести в0,а потом? нет тогда,ECMAScript规范оговорено вnullа такжеundefinedдруг друга宽松相等(==), а также равно самому себе, но не всем другим значениям宽松相等(==).

наконец

Теперь смотрим на предыдущий кусок кода и становится понятно, что многое

  [] == ![] // true

  [] == 0 // true
  
  [2] == 2 // true

  ['0'] == false // true

  '0' == false // true

  [] == false // true

  [null] == 0 // true

  null == 0 // false

  [null] == false // true

  null == false // false

  [undefined] == false // true

  undefined == false // false

Наконец, я хочу сказать всем, не отвергайте слепо неявное преобразование javascript, вы должны научиться его использовать.В вашем коде может быть много неявных преобразований, но вы их игнорируете.Причина, это может помочь нам глубоко понимать javascript.

(Прошло много времени с тех пор, как я прочитал это, это была тяжелая работа, но я пишу уже давно, пожалуйста, ставьте палец вверх и уходите)