Vue性能提高之Object.freeze()

在 Vue 的文档中介绍数据绑定和响应时,特地标注了对于通过 Object.freeze() 方法的对象没法进行更新响应。所以,特地去查了 Object.freeze() 方法的具体含义。javascript

含义

Object.freeze() 方法用于冻结对象,禁止对于该对象的属性进行修改(因为数组本质也是对象,所以该方法能够对数组使用)。在 Mozilla MDN 中是以下介绍的:html

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

该方法的返回值是其参数自己。java

须要注意的是如下两点git

  1. Object.freeze() 和 const 变量声明不一样,也不承担 const 的功能。github

    const和Object.freeze()彻底不一样vuex

  • const的行为像 let。它们惟一的区别是, const定义了一个没法从新分配的变量。 经过 const声明的变量是具备块级做用域的,而不是像 var声明的变量具备函数做用域。
  • Object.freeze()接受一个对象做为参数,并返回一个相同的不可变的对象。这就意味着咱们不能添加,删除或更改对象的任何属性。
  • const和Object.freeze()并不一样,const是防止变量从新分配,而Object.freeze()是使对象具备不可变性。

如下代码是正确的:数组

  1. Object.freeze() 是“浅冻结”,如下代码是生效的:

实例

常规用法浏览器

明显看到,a 的 prop 属性未被改变,即便从新赋值了。bash

延伸

"深冻结"

要彻底冻结具备嵌套属性的对象,您能够编写本身的库或使用已有的库来冻结对象,如Deepfreezeimmutable-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 }
        ]);
    }
})
复制代码

vue的文档没有写上这个特性,但这是个很是实用的作法,对于纯展现的大数据,均可以使用Object.freeze提高性能。

相关文章
相关标签/搜索