作者:4Ark

https://juejin.cn/post/7041049741458669576

故事背景

有一天上线后大佬反馈了一个问题,他刚发的动态在生成分享卡片的时候,卡片底部的小程序码丢失了,然而其他小伙伴都表示在自己手机上运行正常。事实上大佬也说除了这条动态以外,其它都是正常的。

说明这个 BUG 需要特定的动态卡片 + 特定的设备才能复现,所幸坐我对面的小姐姐手机与大佬是同款,也能复现 BUG,避免了作为社恐的我要去找大佬借手机测试的尴尬。

先交代一下项目背景,这是一个微信小程序项目,其中生成分享卡片功能用到的是一个叫 wxml2canvas[1] 的库,然而该库目前看上去已经「年久失修」,上面所说的 BUG 就是因为这个库,

本文分享一下排查该 BUG 的过程、以及如何从 ECMAScript 规范中找到关于 Object.keys() 返回顺序的规范定义,最后介绍一下在 V8 引擎中是如何处理对象属性的。

希望大家在阅读本文后,不会再因为搞不懂 Object.keys() 输出的顺序而犯错导致产生莫名其妙的 BUG。

TL;DR

本文很长,如果你不想阅读整篇文章,可以阅读这段摘要;如果你打算阅读整篇文章,那么你完全可以跳过本段。

如果阅读摘要时未能帮助你理解,可以跳转到对应章节进行详细阅读。

摘要:

  1. 这个 BUG 是如何产生的?

  • wxml2canvas 在绘制的时候,会根据一个叫做 sorted 的对象对它的 keys 进行遍历,该对象的 key 为节点的 top 值,value 为节点元素;问题就是出在这里,该库作者误以为 Object.keys() 总是会按照实际创建属性的顺序返回,然而当 key 为正整数的时候,返回顺序就不符合原本的预期了,会出现了绘制顺序错乱,从而导致这个 BUG 的产生。

  • 源码:src/index.js#L1146[2] 和 src/index.js#L829[3]

  1. 如何解决这个 BUG

  • 由于对象的 key 是一个数字,那么 key 有可能会是整数,也有可能是浮点数。但是预期行为是希望 Object.keys() 按照属性实际创建的顺序返回,那只要将所有 key 都强制转换为浮点数就好了。

  1. Object.keys() 是按照什么顺序返回值的?

  • Object.keys() 返回顺序与遍历对象属性时的顺序一样,调用的 [[OwnPropertyKeys]]() 内部方法。

  • 根据 ECMAScript 规范[4],在输出 keys 时会先将所有 key 为数组索引类型(正整数)从小到大的顺序排序,然后将所有字符串类型(包括负数、浮点数)的 key 按照实际创建的顺序来排序

  1. V8 内部是如何处理对象属性的?

  • V8 在存储对象属性时,为了提高访问效率,会分为常规属性(properties) 和 排序属性(elements)

    • 排序属性(elements) ,就是数组索引类型的属性(也就是正整数类型)。

    • 常规属性(properties) ,就是字符串类型的属性(也包括负数、浮点数)。

    • 以上两种属性都会存放在线性结构中,称为快属性

    • 然而这样每次查询都有一个间接层,会影响效率,所以 V8 引入对象内属性(in-object-properties) 。

  • V8 会为每一个对象关联一个隐藏类,用于记录该对象的形状,相同形状的对象会共用同一个隐藏类。

    • 当对象添加、删除属性的时候,会创建一个新的对应的隐藏类,并重新关联。

  • 对象内属性会将部分常规属性直接放在对象第一层,所以它访问效率是最高的。

    • 常规属性的数量少于对象初始化时的属性数量时,常规属性会直接作为对象内属性存放。

  • 虽然快属性访问速度快,但是从线性结构中添加或删除时执行效率会非常低,因此如果属性特别多、或出现添加和删除属性时,就会将常规属性从线性存储改为字典存储,这就是慢属性

可以看一下这两张图帮助理解:

V8 常规属性和排序属性

V8 对象内属性、快属性和慢属性

图片出处:《图解 Google V8》[5]

如何解决该 BUG

由于是特定的动态 + 特定的设备才能复现问题,可以很轻易地排除掉网络原因,通过在 wxml2canvas 输出绘制的节点列表,也能看到小程序码相关的节点。

既然 wxml2canvas 已经接受到小程序码的节点,却没有绘制出来,那么问题自然就出在 wxml2canvas 内部,不过已经见怪不怪了,在我加入项目以后就已经多次因为这操蛋的 wxml2canvas 出现各种问题而搞得头皮发麻,有机会一定要替换掉这个库,但由于已经有很多页面在依赖这个库,现在也只能硬着头皮上。

首先怀疑是小程序码节点的坐标位置不太对,通过对比,发现位置相差不大,排除该原因。

然后对比所有节点的绘制顺序,发现了一个不太寻常的点,在复现 BUG 的手机上,绘制小程序码节点的时机是比较靠前的,但由于它在卡片底部,所以在正常情况下,应该是比较靠后才对。

于是通过查看相关代码,果然发现了其中的玄机:

在绘制的时候,通过遍历 sorted 对象,从上往下、从左到右依次绘制,但是通过对比两台手机的 Object.keys(),发现了它们的输出是不一样的,这时候我就明白怎么回事了。

先来说说这个 sorted 对象,它是一个 key 为节点 top 值,value 为所有相同 top 值(同一行)的元素数组。

下面是生成它的代码:

问题就发生在前面所说的 Object.keys() 这里,我们先来看个

【JS】1410- 一行 Object.keys() 引发的思考相关推荐

  1. 由一行文本输入框引发的思考

      文章是关于React组件之表单单行文本输入框的一些思考.可能大家第一反应都是,不就是一行<input/>嘛,没什么特别的吧?如果说到输入框的值的话,可能圈子里上大多数封装好的React ...

  2. js Object.keys()

    Object.keys() 在实际开发过程中,有时需要知道对象的所有属性,除了 (for...in...) 外,原生js 给我们提供了一个更优雅的方法: Object.keys( object). ☆ ...

  3. php object keys_原生js中Object.keys方法详解

    实际开发中,有时需要知道对象的所有属性,原生js提供了一个方法Object.keys(). Object.keys(obj)返回的是一个数组,该数组的所有元素都是字符串.这些元素是来自于给定的obj可 ...

  4. Reflect.ownKeys()与Object.keys()区别 以及 JS中的可枚举属性与不可枚举属性

    代码test1: var obj = {} Object.defineProperty(obj, 'method1', {value: function () {alert("Non enu ...

  5. JS的Object.keys

    简记:返回对象的所有属性 ☆  传入对象, 返回包含对象可枚举属性和方法的数组 var obj = {'a': 'Beijing', 'b': 'Haidian'}; console.log(Obje ...

  6. js Object.keys

    /*** Created by W.J.Chang on 2014/5/23.*/ d = 1; var a = {a:'b',c:'d'};// 枚举Keys for(var i in a) {co ...

  7. 从一次react异步setState引发的思考

    0. 前言 一个异步请求,当请求返回的时候,拿到数据马上setState并把loading组件换掉,很常规的操作.但是,当那个需要setState的组件被卸载的时候(切换路由.卸载上一个状态组件)去s ...

  8. 一个分组查询引发的思考

    一个分组查询引发的思考 我们在看项目代码或者SQL语句时, 往往会看到很多非常复杂的业务或者SQL 那么问题来了. 复杂SQL是如何写成的? 下面通过一个数据展示的需求来体会到复杂的SQL是如何书写的 ...

  9. Object.keys方法之详解

    记一道JavaScript面试题 问题: 如果我们使用JavaScript的"关联数组",我们怎么计算"关联数组"的长度? var counterArray = ...

最新文章

  1. python+OpenCV图像处理
  2. matlab龙格库塔法求通解,基于matlab及龙格库塔法求解布拉修斯方程.doc
  3. 搞懂分布式技术8:负载均衡原理剖析
  4. [经典算法] 河内塔
  5. Delphi 2010 新增功能之: TWICImage 类[4] - 图像的修剪
  6. java仿聊天室项目总结_Java团队课程设计-socket聊天室(个人总结)
  7. 机器学习(part4)--模型评估
  8. Laravel同时接收路由参数和查询字符串中的参数
  9. 山寨威武 仿冒Xoom先于行货获得Android 4.0升级
  10. jdk1.8 HashMap ConcurrentHashMap
  11. initBinder转换日期格式
  12. 使用Storyboard 创建ViewController
  13. log4j的详细介绍
  14. CART决策树算法的Python实现(注释详细)
  15. java使用阿里邮箱发送邮件
  16. python deap_在Python中用DEAP绘制多目标pareto边界
  17. Linux修仙之路——RAID技术
  18. Vagrant 手册之同步目录 - 基本用法
  19. C语言转义字符'\'
  20. 华东之旅--西塘第二天

热门文章

  1. 2022第七届少儿模特明星盛典 全国总决赛精彩直击
  2. python day 07
  3. 【中文】【吴恩达课后编程作业】Course 2 - 改善深层神经网络 - 第三周作业
  4. 如何提交一份高质量的缺陷报告
  5. 计算机组成课设sbb,计算机组成原理课设(完成版).doc
  6. python用unittest+HTMLTestRunner的框架测试并生成测试报告
  7. python基础读后感_Python基础教程【读书笔记】 - 2016/6/26
  8. Java后台框架篇--Struts与SSH
  9. Windows驱动_WSK驱动之四WSK例程
  10. 2020-11-02 很多人比起穷,更受不了慢慢变富