JavaScript motsvarighet till isset()


I PHP kan du göra if(isset($array['foo'])) { ... }. I JavaScript använder man ofta if(array.foo) { ... } för att göra samma sak, men detta är inte exakt samma uttryck. Villkoret kommer också att utvärderas till false om array.foo faktiskt finns men är false eller 0 (och troligen andra värden också).

Vad är den perfekta motsvarigheten till PHP:s isset i JavaScript?

I en vidare bemärkelse skulle en allmän, komplett guide om hur JavaScript hanterar variabler som inte existerar, variabler utan värde, etc. vara bekväm.


Uppdatering: För 11 år och 11 månader sedan publicerade jag denna fråga, och wow, den får fortfarande mycket aktivitet. Nu är jag ganska säker på att när jag skrev detta ville jag bara veta hur man kontrollerar om en egenskap finns i en associativ array (dvs. dictionary), och som sådan involverar de korrekta (för mig) svaren hasOwnProperty eller in-operatorn. Jag var inte intresserad av att kontrollera lokala eller globala variabler.

Men även om jag minns det väl, är den avsikten inte riktigt tydlig i frågan som den är skriven, eller ens direkt motsagd av den! Jag nämnde aldrig den associativa arrayen, och PHP:s isset gör också de där andra sakerna. Låt detta bli en lärdom för oss alla om hur viktigt det är att korrekt ange sina krav i en fråga, och också hur globala variabler, lokala variabler, objektets egenskaper, dictionary-nycklar och liknande inte är Huey, Dewey och Louie.

Under tiden (heh) har många många människor också gett svar i den riktningen, så för er som hittade denna fråga genom Google, ja, jag är glad att min vaghet hjälpte på något sätt antar jag. Hur som helst, ville bara förtydliga det.

Svar #1

Jag använder vanligtvis typeof-operatorn:

if (typeof obj.foo !== 'undefined') {
  // your code here
}

Det kommer att returnera "undefined" antingen om egenskapen inte finns eller om dess värde är undefined.

(Se även: Skillnaden mellan undefined och att inte vara definierad.)

Det finns andra sätt att ta reda på om en egendom finns på ett objekt, som hasOwnProperty-metoden:

if (obj.hasOwnProperty('foo')) {
  // your code here
}

Och in-operatorn:

if ('foo' in obj) {
  // your code here
}

Skillnaden mellan de två sista är att hasOwnProperty-metoden kommer att kontrollera om egenskapen existerar fysiskt på objektet (egenskapen är inte ärvd).

Operatorn in kommer att kontrollera alla egenskaper som är åtkomliga upp i prototypkedjan, t.ex.:

var obj = { foo: 'bar'};

obj.hasOwnProperty('foo'); // true
obj.hasOwnProperty('toString'); // false
'toString' in obj; // true

Som du kan se, returnerar hasOwnProperty false och in-operatorn returnerar true när man kontrollerar toString-metoden, denna metod är definierad längre upp i prototypkedjan, eftersom obj ärver från Object.prototype.

Svar #2

Åldrig tråd, men det finns nya sätt att köra ett motsvarande isset().

ESNext (Steg 4 december 2019)

Två nya syntaxer tillåter oss att avsevärt förenkla användningen av isset()-funktionen:

  • Valfri kedjning(?.)
  • Nullsammanfogningoperator(??)

Vänligen läs dokumentationen och tänk på webbläsarkompatibiliteten.

Svar

Se nedan för förklaring. Notera att jag använder StandardJS-syntax

Exempel på användning

// IMPORTANT pass a function to our isset() that returns the value we're
// trying to test(ES6 arrow function)
isset(() => some) // false

// Defining objects
let some = { nested: { value: 'hello' } }

// More tests that never throw an error
isset(() => some) // true
isset(() => some.nested) // true
isset(() => some.nested.value) // true
isset(() => some.nested.deeper.value) // false

// Less compact but still viable except when trying to use `this` context
isset(function () { return some.nested.deeper.value }) // false

Svarsfunktion

/**
 * Checks to see if a value is set.
 *
 * @param   {Function} accessor Function that returns our value
 * @returns {Boolean}           Value is not undefined or null
 */
function isset (accessor) {
  try {
    // Note we're seeing if the returned value of our function is not
    // undefined or null
    return accessor() !== undefined && accessor() !== null
  } catch (e) {
    // And we're able to catch the Error it would normally throw for
    // referencing a property of undefined
    return false
  }
}

NPM-paket

Denna svarsfunktion finns som isset-php-paketet på NPM. Paketet innehåller några förbättringar såsom typkontroll och stöd för flera argument.

npm install --save isset-php

Den fullständiga dokumentationen finns i README.

const isset = require('isset-php')
let val = ''

// This will evaluate to true so the text will be printed.
if (isset(() => val)) {
  console.log('This val is set so I will print.')
}

Förklaring

PHP

Observera att i PHP kan du referera till vilken variabel som helst på vilken nivå som helst - att till och med försöka komma åt en icke-array som en array returnerar ett enkelt true eller false:

// Referencing an undeclared variable
isset($some); // false

$some = 'hello';

// Declared but has no depth(not an array)
isset($some); // true
isset($some['nested']); // false

$some = ['nested' => 'hello'];

// Declared as an array but not with the depth we're testing for
isset($some['nested']); // true
isset($some['nested']['deeper']); // false

JavaScript

I JavaScript har vi inte den friheten; vi får alltid ett fel om vi gör samma sak eftersom motorn omedelbart försöker komma åt värdet av deeper innan vi kan omsluta det i vår isset()-funktion så...

// Common pitfall answer(ES6 arrow function)
const isset = (ref) => typeof ref !== 'undefined'

// Same as above
function isset (ref) { return typeof ref !== 'undefined' }

// Referencing an undeclared variable will throw an error, so no luck here
isset(some) // Error: some is not defined

// Defining a simple object with no properties - so we aren't defining
// the property `nested`
let some = {}

// Simple checking if we have a declared variable
isset(some) // true

// Now trying to see if we have a top level property, still valid
isset(some.nested) // false

// But here is where things fall apart: trying to access a deep property
// of a complex object; it will throw an error
isset(some.nested.deeper) // Error: Cannot read property 'deeper' of undefined
//         ^^^^^^ undefined

Fler misslyckandealternativ:

// Any way we attempt to access the `deeper` property of `nested` will
// throw an error
some.nested.deeper.hasOwnProperty('value') // Error
//   ^^^^^^ undefined

// Similar to the above but safe from objects overriding `hasOwnProperty`
Object.prototype.hasOwnProperty.call(some.nested.deeper, 'value') // Error
//                                        ^^^^^^ undefined

// Same goes for typeof
typeof some.nested.deeper !== 'undefined' // Error
//          ^^^^^^ undefined

Och några fungerande alternativ som snabbt kan bli överflödiga:

// Wrap everything in try...catch
try {
  if (isset(some.nested.deeper)) {
    // ...
  }
} catch (e) {}

try {
  if (some.nested.deeper !== undefined && some.nested.deeper !== null) {
    // ...
  }
} catch (e) {}

// Or by chaining all of the isset which can get long
isset(some) && isset(some.nested) && isset(some.nested.deeper) // false
//                        ^^^^^^ returns false so the next isset() is never run

Slutsats

Alla de andra svaren - även om de flesta är genomförbara...

  1. Anta att du bara kontrollerar om variabeln inte är undefined, vilket är okej för vissa användningsfall men kan fortfarande orsaka ett fel
  2. Anta att du bara försöker komma åt en egenskap på översta nivån, vilket återigen är bra för vissa användningsfall
  3. Tvinga dig att använda ett mindre än idealiskt tillvägagångssätt i förhållande till PHP:s isset()
    t.ex. isset(some, 'nested.deeper.value')
  4. Använd eval() som fungerar men som jag personligen undviker

Jag tror att jag täckte mycket av det. Det finns några punkter som jag tar upp i mitt svar som jag inte berör eftersom de - även om de är relevanta - inte är en del av frågan (t.ex. kortslutning). Om det behövs kan jag dock uppdatera mitt svar med länkar till några av de mer tekniska aspekterna baserat på efterfrågan.

Jag spenderade alldeles för mycket tid på detta, så förhoppningsvis hjälper det folk.

Tack för att du läste!

Svar #3

Referens till KÄLLA

    module.exports = function isset () {
  //  discuss at: http://locutus.io/php/isset/
  // original by: Kevin van Zonneveld (http://kvz.io)
  // improved by: FremyCompany
  // improved by: Onno Marsman (https://twitter.com/onnomarsman)
  // improved by: Rafał Kukawski (http://blog.kukawski.pl)
  //   example 1: isset( undefined, true)
  //   returns 1: false
  //   example 2: isset( 'Kevin van Zonneveld' )
  //   returns 2: true

  var a = arguments
  var l = a.length
  var i = 0
  var undef

  if (l === 0) {
    throw new Error('Empty isset')
  }

  while (i !== l) {
    if (a[i] === undef || a[i] === null) {
      return false
    }
    i++
  }

  return true
}

phpjs.org är mestadels pensionerat till förmån för locutus Här är den nya länken http://locutus.io/php/var/isset

Svar #4

if (!('foo' in obj)) {
  // not set.
}

Relaterade artiklar