乐趣区

关于前端:Vue性能提升之Objectfreeze


在 Vue 的文档中介绍数据绑定和响应时,特意标注了对于通过 Object.freeze() 办法的对象无奈进行更新响应。因而,特意去查了 Object.freeze() 办法的具体含意。

含意

Object.freeze() 办法用于解冻对象,禁止对于该对象的属性进行批改(因为 数组实质也是对象,因而该办法能够对数组应用)。在 Mozilla MDN 中是如下介绍的:

能够解冻一个对象。一个被解冻的对象再也不能被批改;解冻了一个对象则不能向这个对象增加新的属性,不能删除已有属性,不能批改该对象已有属性的可枚举性、可配置性、可写性,以及不能批改已有属性的值。此外,解冻一个对象后该对象的原型也不能被批改

该办法的返回值是其参数自身。

须要留神的是以下两点

  1. Object.freeze() 和 const 变量申明不同,也不承当 const 的性能。

    const 和 Object.freeze()齐全不同

  • const 的行为像 let。它们惟一的区别是,const 定义了一个无奈重新分配的变量。通过 const 申明的变量是具备块级作用域的,而不是像 var 申明的变量具备函数作用域。
  • Object.freeze()承受一个对象作为参数,并返回一个雷同的不可变的对象。这就意味着咱们不能增加,删除或更改对象的任何属性。
  • const 和 Object.freeze()并不同,const 是避免变量重新分配,而 Object.freeze()是使对象具备不可变性。

以下代码是正确的:

  1. Object.freeze() 是“浅解冻”,以下代码是失效的:

实例

惯例用法

显著看到,a 的 prop 属性未被扭转,即便从新赋值了。

延长

“ 深解冻 ”

要齐全解冻具备嵌套属性的对象,您能够编写本人的库或应用已有的库来解冻对象,如 Deepfreeze 或 immutable-js

// 深解冻函数.
function deepFreeze(obj) {

  // 取回定义在 obj 上的属性名
  var propNames = Object.getOwnPropertyNames(obj);

  // 在解冻本身之前解冻属性
  propNames.forEach(function(name) {var prop = obj[name];

    // 如果 prop 是个对象,解冻它
    if (typeof prop == 'object' && prop !== null)
      deepFreeze(prop);
  });

  // 解冻本身(no-op if already frozen)
  return Object.freeze(obj);
}
复制代码

其实就是个简略的递归办法。然而波及到一个很重要,然而在写业务逻辑的时候很少用的知识点 Object.getOwnPropertyNames(obj)。咱们都晓得在 JS 的 Object 中存在原型链属性,通过这个办法能够获取所有的非原型链属性。

利用 Object.freeze() 晋升性能

除了组件上的优化,咱们还能够对 vue 的依赖革新动手。初始化时,vue 会对 data 做 getter、setter 革新,在古代浏览器里,这个过程实际上挺快的,但依然有优化空间。

Object.freeze() 能够解冻一个对象,解冻之后不能向这个对象增加新的属性,不能批改其已有属性的值,不能删除已有属性,以及不能批改该对象已有属性的可枚举性、可配置性、可写性。该办法返回被解冻的对象。

当你把一个一般的 JavaScript 对象传给 Vue 实例的  data  选项,Vue 将遍历此对象所有的属性,并应用  Object.defineProperty  把这些属性全副转为 getter/setter,这些 getter/setter 对用户来说是不可见的,然而在外部它们让 Vue 追踪依赖,在属性被拜访和批改时告诉变动。

但 Vue 在遇到像 Object.freeze() 这样被设置为不可配置之后的对象属性时,不会为对象加上 setter getter 等数据劫持的办法。参考 Vue 源码

Vue observer 源码

性能晋升成果比照

在基于 Vue 的一个 big table benchmark 里,能够看到在渲染一个一个 1000 x 10 的表格的时候,开启Object.freeze() 前后从新渲染的比照。

big table benchmark

开启优化之前

开启优化之后

在这个例子里,应用了 Object.freeze()比不应用快了 4 倍

为什么Object.freeze() 的性能会更好

不应用Object.freeze() 的 CPU 开销

应用 Object.freeze()的 CPU 开销

比照能够看出,应用了 Object.freeze() 之后,缩小了 observer 的开销。

Object.freeze()利用场景

因为 Object.freeze()会把对象解冻,所以比拟适宜展现类的场景,如果你的数据属性须要扭转,能够从新替换成一个新的 Object.freeze()的对象。

Javascript 对象冻结

批改 React props React 生成的对象是不能批改 props 的, 但实际中遇到须要批改 props 的状况. 如果间接批改, js 代码将报错, 起因是 props 对象被解冻了, 能够用 Object.isFrozen()来检测, 其后果是 true. 阐明该对象的属性是只读的.

那么, 有办法将 props 对象冻结, 从而进行批改吗?

事实上, 在 javascript 中, 对象解冻后, 没有方法再冻结, 只能通过克隆一个具备雷同属性的新对象, 通过批改新对象的属性来达到目标.

能够这样:

ES6: Object.assign({}, frozenObject);
lodash: _.assign({}, frozenObject);
复制代码

来看理论代码:

function modifyProps(component) {
  let condictioin = this.props.condictioin,
    newComponent = Object.assign({}, component),
    newProps = Object.assign({}, component.props)
  
  if (condictioin) {if (condictioin.add) newProps.add = true
    if (condictioin.del) newProps.del = true
  }
  newComponent.props = newProps
  
  return newComponent
}
复制代码

锁定对象的办法

  • Object.preventExtensions()

no new properties or methods can be added to the project 对象不可扩大, 即不能够新增属性或办法, 但能够批改 / 删除

  • Object.seal()

same as prevent extension, plus prevents existing properties and methods from being deleted 在下面的根底上,对象属性不可删除, 但能够批改

  • Object.freeze()

same as seal, plus prevent existing properties and methods from being modified 在下面的根底上,对象所有属性只读, 不可批改

以上三个办法别离可用 Object.isExtensible(), Object.isSealed(), Object.isFrozen()来检测

Object.freeze() 阻止 Vue 无奈实现 响应式零碎

当一个 Vue 实例被创立时,它向 Vue 的响应式零碎中退出了其 data 对象中能找到的所有的属性。当这些属性的值产生扭转时,视图将会产生“响应”,即匹配更新为新的值。然而如果应用 Object.freeze(),这会阻止批改现有的属性,也意味着响应零碎无奈再追踪变动。

具体应用方法举例:

<template>
  <div>
     <p>freeze 后会扭转吗
        {{obj.foo}}
     </p>
      <!-- 两个都不能批改??为什么?第二个实践上应该是能够批改的 -->
      <button @click="change"> 点我确认 </button>
  </div>
</template>

<script>
var obj = {foo: '不会变'}
Object.freeze(obj)
export default {
  name: 'index',
  data () {
    return {obj: obj}
  },
  methods: {change () {this.obj.foo = '扭转'}
  }
}
</script>
复制代码

运行后:

从报错能够看出只读属性 foo 不能进行批改,Object.freeze()解冻的是值,你依然能够将变量的援用替换掉, 将上述代码更改为:

<button @click="change"> 点我确认 </button>

change () {
      this.obj = {foo: '会扭转'}
    }
复制代码

Object.freeze()是 ES5 新增的个性,能够解冻一个对象,解冻指的是不能向这个对象增加新的属性,不能批改其已有属性的值,不能删除已有属性,以及不能批改该对象已有属性的可枚举性、可配置性、可写性。避免对象被批改。如果你有一个微小的数组或 Object,并且确信数据不会批改,应用 Object.freeze()能够让性能大幅晋升。

实际心得和技巧

Object.freeze()是 ES5 新增的个性,能够解冻一个对象,避免对象被批改。

vue 1.0.18+ 对其提供了反对,对于 data 或 vuex 里应用 freeze 解冻了的对象,vue 不会做 getter 和 setter 的转换。

如果你有一个微小的数组或 Object,并且确信数据不会批改,应用 Object.freeze()能够让性能大幅晋升。在我的理论开发中,这种晋升大概有 5~10 倍,倍数随着数据量递增。

并且,Object.freeze()解冻的是值,你依然能够将变量的援用替换掉。举个例子:

<p v-for="item in list">{{item.value}}</p>
复制代码
new Vue({
    data: {
        // vue 不会对 list 里的 object 做 getter、setter 绑定
        list: Object.freeze([{ value: 1},
            {value: 2}
        ])
    },
    created () {
        // 界面不会有响应
        this.list[0].value = 100;

        // 上面两种做法,界面都会响应
        this.list = [{ value: 100},
            {value: 200}
        ];
        this.list = Object.freeze([{ value: 100},
            {value: 200}
        ]);
    }
退出移动版