在SQL中,我们可以看到字符串是否在这样的列表中:

Column IN ('a', 'b', 'c')

用JavaScript实现这一点的好方法是什么?这样做太笨拙了:

if (expression1 || expression2 || str === 'a' || str === 'b' || str === 'c') {
   // do something
}

我不确定它的表现或清晰度:

if (expression1 || expression2 || {a:1, b:1, c:1}[str]) {
   // do something
}

或者可以使用switch 功能:

var str = 'a',
   flag = false;

switch (str) {
   case 'a':
   case 'b':
   case 'c':
      flag = true;
   default:
}

if (expression1 || expression2 || flag) {
   // do something
}

但这是一个可怕的烂摊子.有什么 idea 吗?

In this case, I have to use Internet Explorer 7 as it's for a corporate intranet page. So ['a', 'b', 'c'].indexOf(str) !== -1 won't work natively without some syntax sugar.

推荐答案

EcmaScript 6及以上版本

如果您使用的是ES6或更高版本,最干净的方法是构造一个项目数组并使用Array.includes:

['a', 'b', 'c'].includes('b')

这比indexOf有一些固有的好处,因为它可以正确地测试列表中是否存在NaN,并且可以匹配缺失的数组元素,例如[1, , 2]undefined中的中间元素.includes也适用于JavaScript typed arrays,比如Uint8Array.

如果您担心浏览器支持(例如IE或Edge),您可以 Select check Array.includes at CanIUse.Com,如果您想要针对缺少includes的浏览器或浏览器版本,我建议使用polyfill.io填充.

更高的性能

请注意,Array.includes()的时间随数组中元素的数量而变化:它的性能为O(n).如果您需要更高的性能,并且不会重复构建项目集(但会重复判断项目是否包含某些元素),那么应该使用Set.

const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')

请注意,您可以将任何可迭代项传递给Set构造函数(支持for...of的任何项).您还可以使用Array.from(set)Set转换为array.

没有数组

不建议这样做,但您可以按如下方式向字符串添加新的isInList属性:

if (!String.prototype.isInList) {
  Object.defineProperty(String.prototype, 'isInList', {
    get: () => function(...args) {
      let value = this.valueOf();
      for (let i = 0, l = args.length; i < l; i += 1) {
        if (arguments[i] === value) return true;
      }
      return false;
    }
  });
}

然后像这样使用:

'fox'.isInList('weasel', 'fox', 'stoat') // true
'fox'.isInList('weasel', 'stoat') // false

你可以花Number.prototype英镑做同样的事情.

请注意,Object.defineProperty不能在IE8和更早版本中使用,也不能在其他浏览器的非常旧版本中使用.然而,它是一个比String.prototype.isInList = function() { ... }好得多的解决方案,因为使用这样的简单赋值将在String.prototype上创建一个可枚举属性,这更有可能 destruct 代码.

Array.indexOf

如果您使用的是现代浏览器,则indexOf始终有效.但是,对于IE8和更早版本,您将需要一个多边形填充.

如果indexOf返回-1,则该项不在列表中.但是请注意,该方法不会正确判断NaN,虽然它可以匹配显式undefined,但它不能像数组[1, , 2]中那样将缺失的元素匹配到undefined.

Polyfill for indexOf or includes in IE, or any other browser/version lacking support

如果您不想使用上面提到的像polyfill.io这样的服务,您可以始终在自己的源代码中包含符合标准的自定义多边形填充.例如,Mozilla开发者网络(Mozilla Developer Network)有一个适用于indexOf的版本.

在这种情况下,我必须为Internet Explorer 7制定一个解决方案,我"推出了自己的"更简单版本的indexOf()函数,它不符合标准:

if (!Array.prototype.indexOf) {
   Array.prototype.indexOf = function(item) {
      var i = this.length;
      while (i--) {
         if (this[i] === item) return i;
      }
      return -1;
   }
}

关于修改对象原型的说明

然而,我不认为修改String.prototypeArray.prototype是一个好的长期战略.在JavaScript中修改对象原型可能会导致严重的错误.你需要决定在自己的环境中这样做是否安全.需要注意的是,用for ... in迭代一个数组(当array.prototype添加了属性时),将返回新的函数名作为键之一:

Array.prototype.blah = function() { console.log('blah'); };
let arr = [1, 2, 3];
for (let x in arr) { console.log(x); }
// Result:
0
1
2
blah // Extra member iterated over!

您的代码现在可能可以工作,但当将来有人添加第三方JavaScript库或插件时,如果该库或插件不积极防范继承的密钥,一切都可能崩溃.

避免这种 destruct 的老方法是,在枚举过程中,判断每个值,看看对象是否真的将其作为非继承属性,使用if (arr.hasOwnProperty(x))only then处理x.

避免这一额外关键问题的新ES6方法有:

  1. of代替in,for (let x of arr).但是,根据输出目标和您的下层转换机的确切设置/功能,这可能不可靠.另外,除非您能保证您的所有代码和第三方库都严格遵循此方法,否则对于此问题,您可能只想使用上面所述的includes.

  2. 使用Object.defineProperty()在原型上定义新属性,因为这将使属性(默认情况下)不可枚举.只有当您使用的所有JavaScript库或模块也这样做时,这才真正解决了问题.

注意最后一个问题

最后,请注意,虽然多边形填充是有意义的,修改对象原型是一种有用的策略,但这种方法偶尔仍可能存在范围界定问题.

在浏览器中,每个不同的document对象都是自己的新全局范围,在浏览器JS中,可以创建新文档(例如用于屏幕外渲染或创建文档片段的文档)或获取对另一个页面的document对象的引用(例如通过使用命名目标链接的页面间通信),因此在某些情况下是可能的(罕见的?)对象原型不会有你期望的方法,尽管你总是可以对新的全局对象再次运行多边形填充...

在node中,修改global个对象的原型可能是安全的,但如果最终需要/导入同一软件包的两个版本,修改非全局导入对象的原型可能会导致 destruct ,因为两个版本的导入不会expose 相同的对象,因此不会有相同的对象原型.也就是说,在依赖项或子依赖项使用与预期版本不同的版本之前,您的代码可以正常工作,并且在不更改任何代码的情况下,简单的npm installyarn install可能会触发此问题.(解决这个问题有很多 Select ,比如Yarn 在package.json中的resolutions属性,但如果你有其他 Select ,这不是一件好事情.)

Javascript相关问答推荐

*ngFor和@代表输入decorator 和选角闭合

将json数组项转换为js中的扁平

每次子路由重定向都会调用父加载器函数

如何从JSON数组添加Google Maps标记—或者如何为数组添加参数到标记构造器

Chrome是否忽略WebAuthentication userVerification设置?""

jQuery s data()[storage object]在vanilla JavaScript中?'

当试图显示小部件时,使用者会出现JavaScript错误.

无法使用单击按钮时的useState将数据从一个页面传递到另一个页面

Reaction组件在本应被设置隐藏时仍显示

Nextjs 13.4 Next-Auth 4.2登录(&Quot;凭据&,{});不工作

使用Document.Evaluate() Select 一个包含撇号的HTML元素

在验证和提交表单后使用useNavigate()进行react 重定向,使用带有加载器和操作的路由

Phaser3 preFX addGlow不支持zoom

如何使用Astro优化大图像?

本地损坏的Java脚本

为什么我不能使用其同级元素调用和更新子元素?

P5play SecurityError:无法从';窗口';读取命名属性';Add';:阻止具有源的帧访问跨源帧

需要刷新以查看Mern堆栈应用程序中的更改

我想为我的Reaction项目在画布上加载图像/视频,图像正在工作,但视频没有

在JS/TS中构造RSA公钥