in

in 运算符如果指定的属性在指定对象或其原型链中,则返回 true

in 运算符不能用于在其他集合中搜索值。要测试某个值是否在数组中存在,请使用 Array.prototype.includes()。对于集合,请使用 Set.prototype.has()

试一试

语法

js
prop in object
#prop in object

参数

prop

表示属性名称的字符串或符号(非符号将被 强制转换为字符串)。也可以是 私有属性标识符

object

要检查其(或其原型链)是否包含具有指定名称(prop)的属性的对象。

异常

TypeError

如果 object 不是对象(即原始值),则抛出。

描述

in 运算符测试字符串或符号属性是否存在于对象或其原型链中。如果只想检查非继承属性,请改用 Object.hasOwn()

属性可能存在于对象中,但值为 undefined。因此,x in objobj.x !== undefined 不相同。为了使 in 在添加属性后返回 false,请使用 delete 运算符,而不是将该属性的值设置为 undefined

您还可以使用 in 运算符检查是否在对象中定义了特定的 私有类字段或方法。如果定义了该属性,则运算符返回 true,否则返回 false。这称为品牌检查,因为它当且仅当对象使用该类构造函数创建时返回 true,之后您可以安全地访问其他私有属性。

这是一个特殊的语法——in 运算符的左侧是属性标识符而不是表达式,但未加引号(因为否则它是字符串属性,而不是私有属性)。

因为访问与当前类无关的对象上的私有属性会抛出 TypeError 而不是返回 undefined,所以此语法允许您缩短

js
class C {
  #x;
  static isC(obj) {
    try {
      obj.#x;
      return true;
    } catch {
      return false;
    }
  }
}

js
class C {
  #x;
  static isC(obj) {
    return #x in obj;
  }
}

它通常还避免了需要处理错误以访问可能不存在的私有属性。

但是,in 运算符仍然要求在封闭类中预先声明私有属性——否则,它会抛出 SyntaxError(“私有字段 '#x' 必须在封闭类中声明”),与您尝试访问未声明的私有属性时抛出的错误相同。

js
class C {
  foo() {
    #x in this;
  }
}

new C().foo(); // SyntaxError: Private field '#x' must be declared in an enclosing class

示例

基本用法

以下示例显示了 in 运算符的一些用法。

js
// Arrays
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
0 in trees; // returns true
3 in trees; // returns true
6 in trees; // returns false
"bay" in trees; // returns false (you must specify the index number, not the value at that index)
"length" in trees; // returns true (length is an Array property)
Symbol.iterator in trees; // returns true

// Predefined objects
"PI" in Math; // returns true

// Custom objects
const mycar = { make: "Honda", model: "Accord", year: 1998 };
"make" in mycar; // returns true
"model" in mycar; // returns true

您必须在 in 运算符的右侧指定一个对象。例如,您可以指定使用 String 构造函数创建的字符串,但不能指定字符串字面量。

js
const color1 = new String("green");
"length" in color1; // returns true

const color2 = "coral";
// generates an error (color2 is not a String object)
"length" in color2;

将 in 运算符与已删除或未定义的属性一起使用

如果您使用 delete 运算符删除属性,则 in 运算符会为该属性返回 false

js
const mycar = { make: "Honda", model: "Accord", year: 1998 };
delete mycar.make;
"make" in mycar; // returns false

const trees = ["redwood", "bay", "cedar", "oak", "maple"];
delete trees[3];
3 in trees; // returns false

如果您将属性设置为 undefined 但未删除它,则 in 运算符会为该属性返回 true。

js
const mycar = { make: "Honda", model: "Accord", year: 1998 };
mycar.make = undefined;
"make" in mycar; // returns true
js
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
trees[3] = undefined;
3 in trees; // returns true

in 运算符将为 空数组插槽 返回 false,即使直接访问它返回 undefined

js
const empties = new Array(3);
empties[2]; // returns undefined
2 in empties; // returns false

为避免这种情况,请确保新数组始终填充非空值,或者不要写入超出数组末尾的索引。

js
const empties = new Array(3).fill(undefined);
2 in empties; // returns true

继承的属性

in 运算符会为原型链中的属性返回 true。如果您使用对象存储任意键值对,这可能不希望。

js
const ages = { alice: 18, bob: 27 };

function hasPerson(name) {
  return name in ages;
}

hasPerson("hasOwnProperty"); // true

您可以使用 Object.hasOwn() 来检查对象是否具有该键。

js
const ages = { alice: 18, bob: 27 };

function hasPerson(name) {
  return Object.hasOwn(ages, name);
}

hasPerson("hasOwnProperty"); // false

或者,您应该考虑使用 空原型对象Map 来存储 ages,以避免其他错误。

js
const ages = new Map([
  ["alice", 18],
  ["bob", 27],
]);

function hasPerson(name) {
  return ages.has(name);
}

hasPerson("hasOwnProperty"); // false

使用 in 运算符实现品牌检查

下面的代码片段演示了一个静态函数,该函数判断对象是否使用 Person 构造函数创建,因此可以安全地执行其他方法。

js
class Person {
  #age;
  constructor(age) {
    this.#age = age;
  }
  static isPerson(o) {
    return #age in o;
  }
  ageDifference(other) {
    return this.#age - other.#age;
  }
}

const p1 = new Person(20);
const p2 = new Person(30);
console.log(p1.ageDifference(p2)); // -10
console.log(Person.isPerson(p1)); // true

if (Person.isPerson(p1) && Person.isPerson(p2)) {
  console.log(p1.ageDifference(p2)); // -10
}

它有助于防止以下情况

js
const p2 = {};

p1.ageDifference(p2); // TypeError: Cannot read private member #age from an object whose class did not declare it

如果没有 in 运算符,您将不得不使用 try...catch 块来检查对象是否具有私有属性。

您还可以将其实现为类的 [Symbol.hasInstance]() 方法,以便您可以使用 instanceof 运算符执行相同的检查(默认情况下,它仅检查对象原型链中是否存在 Person.prototype)。

js
class Person {
  #age;
  constructor(age) {
    this.#age = age;
  }
  static [Symbol.hasInstance](o) {
    // Testing `this` to prevent false-positives when
    // calling `instanceof SubclassOfPerson`
    return this === Person && #age in o;
  }
  ageDifference(other) {
    return this.#age - other.#age;
  }
}

const p1 = new Person(20);
const p2 = new Person(30);

if (p1 instanceof Person && p2 instanceof Person) {
  console.log(p1.ageDifference(p2)); // -10
}

有关更多示例,请参阅 私有属性类指南

规范

规范
ECMAScript 语言规范
# sec-relational-operators

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅