如何判断Javascript对象是否存在

2023-11-04

Javascript语言的设计不够严谨,很多地方一不小心就会出错。

举例来说,请考虑以下情况。

现在,我们要判断一个全局对象myObj是否存在,如果不存在,就对它进行声明。用自然语言描述的算法如下:

  if (myObj不存在){

    声明myObj;

  }

你可能会觉得,写出这段代码很容易。但是实际上,它涉及的语法问题,远比我们想象的复杂。判断一个Javascript对象是否存在,有超过50种写法。只有对Javascript语言的实现细节非常清楚,才可能分得清它们的区别。

第一种写法

根据直觉,你可能觉得可以这样写:

  if (!myObj) {

    myObj = { };

  }

但是,运行这段代码,浏览器会直接抛出ReferenceError错误,导致运行中断。请问错在哪里?

对了,if语句判断myObj是否为空时,这个变量还不存在,所以才会报错。改成下面这样,就能正确运行了。

  if (!myObj) {

    var myObj = { };

  }

为什么加了一个var以后,就不报错了?难道这种情况下,if语句做判断时,myObj就已经存在了吗?

要回答这个问题,就必须知道Javascript解释器的工作方式。Javascript语言是"先解析,后运行",解析时就已经完成了变量声明,所以上面的代码实际等同于:

  var myObj;

  if (!myObj) {

    var myObj = { };

  }

因此,if语句做判断时,myObj确实已经存在了,所以就不报错了。这就是var命令的"代码提升"(hoisting)作用。Javascript解释器,只"提升"var命令定义的变量,对不使用var命令、直接赋值的变量不起作用,这就是为什么不加var会报错的原因。

第二种写法

除了var命令,还可以有另一种改写,也能得到正确的结果:

  if (!window.myObj) {

    myObj = { };

  }

window是javascript的顶层对象,所有的全局变量都是它的属性。所以,判断myobj是否为空,等同于判断window对象是否有myobj属性,这样就可以避免因为myObj没有定义而出现ReferenceError错误。不过,从代码的规范性考虑,最好还是对第二行加上var:

  if (!window.myObj) {

    var myObj = { };

  }

或者写成这样:

  if (!window.myObj) {

    window.myObj = { };

  }

第三种写法

上面这种写法的缺点在于,在某些运行环境中(比如V8、Rhino),window未必是顶层对象。所以,考虑改写成:

  if (!this.myObj) {

    this.myObj = { };

  }

在全局变量的层面中,this关键字总是指向顶层变量,所以就可以独立于不同的运行环境。

第四种写法

但是,上面这样写可读性较差,而且this的指向是可变的,容易出错,所以进一步改写:

  var global = this;

  if (!global.myObj) {

    global.myObj = { };

  }

用自定义变量global表示顶层对象,就清楚多了。

第五种写法

还可以使用typeof运算符,判断myObj是否有定义。

  if (typeof myObj == "undefined") {

    var myObj = { };

  }

这是目前使用最广泛的判断javascript对象是否存在的方法。

第六种写法

由于在已定义、但未赋值的情况下,myObj的值直接等于undefined,所以上面的写法可以简化:

  if (myObj == undefined) {

    var myObj = { };

  }

这里有两个地方需要注意,首先第二行的var关键字不能少,否则会出现ReferenceError错误,其次undefined不能加单引号或双引号,因为这里比较的是undefined这种数据类型,而不是"undefined"这个字符串。

第七种写法

上面的写法在"精确比较"(===)的情况下,依然成立:

  if (myObj === undefined) {

    var myObj = { };

  }

第八种写法

根据javascript的语言设计,undefined == null,所以比较myObj是否等于null,也能得到正确结果:

  if (myObj == null) {

    var myObj = { };

  }

不过,虽然运行结果正确,但是从语义上看,这种判断方法是错的,应该避免。因为null指的是已经赋值为null的空对象,即这个对象实际上是有值的,而undefined指的是不存在或没有赋值的对象。因此,这里只能使用"比较运算符"(==),如果这里使用"精确比较运算符"(===),就会出错。

第九种写法

还可以使用in运算符,判断myObj是否为顶层对象的一个属性:

  if (!('myObj' in window)) {

    window.myObj = { };

  }

第十种写法

最后,使用hasOwnProperty方法,判断myObj是否为顶层对象的一个属性:

  if (!this.hasOwnProperty('myObj')) {

    this.myObj = { };

  }

总结

1. 如果只判断对象是否存在,推荐使用第五种写法。

2. 如果除了对象是否存在,还要判断对象是否有null值,推荐使用第一种写法。

3. 除非特殊情况,所有变量都应该使用var命令声明。

4. 为了跨平台,建议避免使用window表示顶层对象。

5. 在Javascript语言中,null和undefined容易产生混淆。在可能同时涉及两者的情况下,建议使用"精确比较"运算符(===)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何判断Javascript对象是否存在 的相关文章

  • Angular UI 模式的范围问题

    我无法理解 使用角度 UI 模式的范围 虽然这里不是很明显 但我已经正确设置了模块和所有内容 据我所知 但这些代码示例尤其是我发现错误的地方 index html 其中重要部分 div class btn group div
  • Firebase,只得到新的孩子[重复]

    这个问题在这里已经有答案了 var firebase new Firebase firebaseRef on child added function snapshot 这将接收所有元素 有没有办法在创建新的 Firebase 引用时不接收
  • 在 Internet Explorer 中使用什么来监视 jscript 内存使用情况

    我们正在调试 GWT 应用程序 在 Firefox 中运行正常 在 IE6 0 中开始运行正常 但一段时间后 它就会崩溃并开始爬行 经过一些测试后 我们怀疑存在一些内存问题 使用了太多内存 内存泄漏等 除了使用taskmanager和pro
  • 从函数返回函数的目的是什么?

    阅读一些遗留代码 发现 A prototype setSize function var v1 new Vector2 return function size var halfSize v1 copy size multiplyScala
  • Vue 3 Composition API 提供/注入在单文件组件中不起作用

    我正在使用 Composition API 在 VueJS 3 中创建一个库 我实现了提供 注入 如中所述docs https v3 vuejs org guide composition api provide inject html i
  • 如何正确清理来自 AngularJS 控制器的无效输入的表单?

    我有一个 AngularJS 表单 其中包含 除其他字段之外 类型之一url 后者很重要 因为这会强制相应的输入成为有效的 URL 在某些条件下 例如 要关闭具有此类表单的模式对话框 我想以编程方式清除该表单 为此 我实现了方法reset基
  • 想要动态处理与分页相关的页码显示:ReactJS

    我有一些分页逻辑工作得很好 唯一的问题是我只能让它显示并固定数量的页面可供选择 现在我已经把它放到了 5 页 但我希望它能够根据总记录动态更改 假设我有 100 条记录 每页限制为 10 条 将有 10 页 现在我只能让它以这种方式显示 第
  • 如何在网站上使用 svg 元素制作块的屏幕截图?

    我在网站上创建了一个构造函数 其本质是将所选元素及其颜色 svg中的元素 添加到访问者选择的背景和背景颜色 png中的背景 中 然后必须单击 保存 结果 按钮并仅执行工作区的屏幕截图 我写了这个脚本 但它需要屏幕截图 但只有背景 并忽略选定
  • 本地推送通知到在应用程序内运行 JS 代码的 Win8 Live Tile

    我正在尝试将更新发送到我的应用程序的磁贴 当应用程序运行时 这可以正常工作 例如 当用户单击按钮时 我可以轻松地将磁贴更新通知发送到磁贴 我无法解决的是当应用程序无法运行时如何更新磁贴 我找到的唯一选择是使用以下命令从远程 Web 服务器拉
  • IntersectionObserver是否支持水平滚动观察?

    我制作了几个垂直滚动 IntersectionObserver 模块 但我对水平滚动感兴趣 根将是 div 观察目标将是 img 我想观察当 img 放大但 div 保持视口宽度时的变化 我什至不确定移动 Safari 是否会将缩放后的图片
  • React autoFocus 将光标设置为输入值的开头

    我有一个受控输入 最初显示一个值 我已将该输入设置为自动聚焦 但当我希望它出现在末尾时 光标出现在输入的开头 我知道这可能是因为自动对焦是在值之前添加的 但我不能 100 确定 在输入字段末尾完成光标初始化的最佳方法是什么 var Test
  • JavaScript 继承;调用和原型

    要在Javascript中实现继承 通常需要执行以下两个步骤 假设我有一个基类 Animal var Animal function name this name name 我现在想从中派生一个子类 Dog 所以我想说 var Dog fu
  • JavaScript推送函数中的动态变量

    我在 JavaScript 中使用推送功能 var chartData for var i 0 i lt 3 i chartData push date new Date year s mon s date s hr s min s sec
  • 如何计算特定字符在字符串中出现的次数

    我正在尝试创建一个函数来查看数组中的任何字符是否在字符串中 如果是 有多少个 我尝试计算每一种模式 但是太多了 我尝试使用 Python 中的 in 运算符的替代方案 但效果不佳 function calc fit element var
  • 改变 JavaScript 中的顶部填充

    以下是我在 css 中设置顶部填充的方法 body font size font size px margin 0 padding 100px 0 20px 0 width 100 important 如何使用最简单的 javascript
  • 用于选择特定 div 中具有特定类的锚元素的 jQuery 选择器是什么

    我有一些这样的代码 我想选择每个 a 带有类的标签status在 div 中foo div a class status a div 你可以这样做 foo find status a
  • Flot 库将 y 轴设置为最小值 0 和最大值 24

    如何将 y 轴设置在 0 到 24 的范围内 这是我的代码 j plot j placeholder d1 xaxis mode time min new Date 2010 11 01 getTime max new Date 2011
  • 仅当显式选择行时才关闭 ui-bootstrap typeahead

    我创建了这个jsBin http jsbin com livuqafe 2 edit来证明我遇到的问题 如果您转到此处 请尝试输入 五 并继续 你的自然反应是输入 五 然后按 Tab 如果你想要 五百 你可以向下箭头一次 但是 在这种情况下
  • 如何通过索引访问 JSON 对象中的字段

    我知道这不是最好的方法 但我别无选择 我必须通过索引访问 JSONObject 中的项目 访问对象的标准方法是只写this objectName or this objectName 我还找到了一种获取 json 对象内所有字段的方法 fo
  • 测量窗口偏移

    有没有一种方法可以测量 jQuery 中窗口的偏移量 以便我可以比较 固定 元素和相对定位元素的位置 我需要能够知道窗口滚动了多远 以便我可以使用该图来计算固定元素的高度 相对于视口顶部 和相对对象的高度 相对于顶部 之间的差异文件的内容

随机推荐

  • Vue3-ElemenPlu,全栈开发后台系统-JWT方案讲解第三章-Koa架构设计接口方面实现mongdb安装配置工具函数的封装前台首页实现

    第三章 Koa架构设计 usr bin env node Module dependencies var app require app var debug require debug
  • elasticsearch script实战

    写在前面 大家在开发elasticsearch的时候都会遇到很多去怪的需求 如果我们已知的RestAPI无法帮助我们完成搜索 是就需要我们自己动手写脚本来辅助搜索 完成需求 浅谈elasticsearch script脚本使用机制 通过阅读
  • angular:angular重用策略与ionic重用策略浅谈

    angular默认重用策略 同一个路由地址互相跳转会复用 否则会重新创建component 无任何重用 ionic默认重用策略 同一个路由地址会复用 在离开当前路由时会缓存路由地址对应的组件 当再次遇到相同路由地址时会恢复 但是复用后 如果
  • Vue移动框鼠标拖拽自定义指令

    在Vue中通过自定义指令 实现指定的模块带有鼠标拖拽移动效果 移动框自定指令 Vue directive drag bind el gt let initX null let initY null el style cursor move
  • 3-3 OneHot编码

    3 3 OneHot编码 请参考 数据准备和特征工程 中的相关章节 调试如下代码 基础知识 import pandas as pd g pd DataFrame gender man woman woman man woman g gend
  • 【华为机试刷题笔记】HJ16-购物单

    王强决定把年终奖用于购物 他把想买的物品分为两类 主件与附件 附件是从属于某个主件的 下表就是一些主件与附件的例子 主件 附件 电脑 打印机 扫描仪 书柜 图书 书桌 台灯 文具 工作椅 无 如果要买归类为附件的物品 必须先买该附件所属的主
  • Django高级扩展之celery使用

    Celery是一个简单 灵活 可靠的分布式系统 用于处理大量消息 同时为操作提供维护此类系统所需的工具 是一个专注于实时处理的任务队列 同时还支持任务调度 目录 应用场景 问题 解决 celery架构图 安装 配置celery Settin
  • 每日一题 day 58(DP topic)

    文章目录 problem wrong approach approach memorize dp problem 518 Coin Change 2 You are given an integer array coins represen
  • git每次都弹窗提示输入用户名和密码的解决方法

    每次进行git操作时总是要输入用户名密码很麻烦 解决方法是 加上用户本地凭证 命令如下 git config global credential helper store 然后再进行git操作 比如 git pull 打个比方 然后会拉起输
  • 什么是僵尸进程(Zombie)?

    分析Android系统启动过程的时候 init进程调用 signal handler init 函数用于设置子进程信号处理函数 它被定义在 system core init signal handler cpp中 主要用于防止init的子进
  • macos 使用串口终端

    其实很简单 先用ls看看 dev tty 哪个是具体的串口 我这里使用的edison的板子 所以插到macos上后可以看到的设备节点为 只需要使用screen 命令即可 具体的命令格式如下 screen dev tty usbserial
  • 【Node】腾讯云OSS存储上传图片

    目录 准备工作 后端Koa 前端Vue 建议 准备工作 购买腾讯云OSS存储后需要获得以下几个变量 SecretId 秘钥id SecretKey 秘钥 Bucket 存储桶名称 可在腾讯云的图像界面创建存储桶 Region 购买时选择的区
  • 苹果电脑计算机找不到打印机,Mac_Mac怎么连接打印机?苹果电脑Mac打印机设置教程 ,Mac怎么连接打印机?Mac打印机 - phpStudy...

    Mac怎么连接打印机 苹果电脑Mac打印机设置教程 Mac怎么连接打印机 Mac打印机该怎么设置呢 对于大多数 USB 打印机 您只需将打印机连接到 Mac OS X 会自动找到打印机 并下载所有驱动程序 如有必要 然后 您就可以打印了 您
  • linux进程调度,优先级、进程nice值

    我自己补充一下 APUE8 16中讲到进程调度 UNIX系统历史上对进程提供的只是基于调度优先级的粗粒度的控制 调度策略和调度优先级是由内核确定的 但是内核可以通过调整nice值选择以更低优先级运行 通过调整nice值降低它对cpu的占有
  • telnet mysql3306端口失败

    在linux上telnet远程mysql端口失败 经过上网查找后 找到多种方法 1 我在本地的Navicat上新增了一个用户 主机名是linux的ip 也可以是 百分号代表这个用户可以在任何地方对mysql进行远程连接 2 登录mysql
  • mipsel-openwrt-linux交叉编译zlog日志库并测试

    mipsel openwrt linux交叉编译zlog日志库并测试 文章目录 mipsel openwrt linux交叉编译zlog日志库并测试 一 准备 二 交叉编译测试 1 mipsel openwrt linux交叉编译过程 ar
  • 机器学习集成模型学习——Bagging集成学习(三)

    Bagging bagging的集成方式是 用1个模型 元模型 然后将这个元模型分成多个相同模型 每个模型使用训练集的一部分进行训练 得到多个基模型 最后测试时分别跑每个模型 平均结果得出这个集成模型的最终预测结果 案例代码 from sk
  • VSCode将QT(shadow build)编译输出到指定文件

    VSCode将QT shadow build 编译输出到指定文件 一 啥是shadow build 实际上就是将编译输出的文件跟源码文件放在不同地方 也就是out of source 0 00 在qtcreator中设置就很方便很方便 直接
  • C#将字符串格式化为Json

    private string ConvertStringToJson string str 格式化json字符串 JsonSerializer serializer new JsonSerializer TextReader tr new
  • 如何判断Javascript对象是否存在

    Javascript语言的设计不够严谨 很多地方一不小心就会出错 举例来说 请考虑以下情况 现在 我们要判断一个全局对象myObj是否存在 如果不存在 就对它进行声明 用自然语言描述的算法如下 if myObj不存在 声明myObj 你可能