当前位置: 首页 > news >正文

高性能JavaScript(数据存取)

数据存取分为4各部分

  存取位置

  作用域及改变作用域

  原型以及原型链

  缓存对象成员值

 

存取位置

JavaScript 有4中基本的数据存取位置

字面量:字面量代表自身,不存于特定的位置。比如这个的匿名函数 $btn.click(function(){... ...});

本地变量:本地变量使用var声明,一个字面量和一个局部变量中存取数据的性能差异是很小的。

数组成员:以数字为索引。

对象成员:以字符串作为索引。

每一种数据存储的位置都有不同的读写消耗,一般而言,从字面量和局部变量获取数据的速度要快于从对象或数组的属性中获取数据的速度,但在性能方面很大程度上取决于浏览器本身。

 

管理作用域

作用域简而言之:内层作用域可以访问外层作用域的变量,而反之,内层作用域的变量对外层是不可见的。

一切都要从Function构造器说起。在javascript中,万物皆对象,函数也是对象,由Function构造函数产生。在函数初始化时,Function构造函数既会为函数添加一些可以由程序员操作的属性,比如:prototype和name,还会为其添加一些程序员无法访问,仅供javascript引擎访问的属性,其中有一个属性叫做[[scope]],指向的就是该函数的作用域链

function add(a, b){ return a+b }

当函数add()创建时,他的作用域链中就会有一个全局对象,这个对象包括 window,document,navigator。

var Total= add(5,10)

执行函数add时会创建一个执行环境(执行上下文),并把函数add的[[scope]]属性复制一份作为执行环境自己的作用域链,之后会创建一个包含运行该函数的所有的局部变量,参数以及this的活动对象,并把它推送到自己作用域的顶端。

 

改变作用域(with)

一般而言,作用域一旦确定就无法改变,但js 提供了两种方式可以用来改变作用域:with和catch子语句。

ES5的严格模式下明文规定禁止使用with。为什么?看下面一段代码就明白了:

var obj = {
    nickname:'Kyle',
    age: 21
};
 function foo() {
    var bar = 'bar';
    var nickname = 'Agent';
    with(obj){
        console.log(nickname); // Kyle
        console.log(age); // 21
        console.log(bar); // bar
    }
}
foo();

使用with 语句的本质是将括号中的对象,直接添加到函数的执行上下文,而且是最顶端,这就使得原本的局部变量跑到作用域链的第二个对象中了。增加了访问的代价,导致严重的性能损耗。

而且,访问nickname 时,根据作用域链由上而下的原则,obj的nickname 属性先被找到,立即返回结果,局部变量 nickname 就被遮蔽了。

这也是ES5 的严格模式中杜绝with 的原因。

 

改变作用域(catch子语句)

在try 语句中出现 error,执行会自动跳转到 catch 中,并把一个异常对象推到作用域的首位。

注意:一旦catch 子语句执行完毕,作用域链就会返回到之前的状态。

使用函数委托的方式能够把catch子句对性能的损耗降低到最小:

try{ // some error }catch(err){ handleError(err) };

这样做只执行了一条语句,并且没有访问局部变量,所以作用域链的临时改变就不会影响代码性能。

 

闭包

闭包的理解:闭包就是一个能访问函数内部变量的函数。

function test(){
    var bar = 'hello';
    return function(){
        alert(bar);
    }
}
test()(); // hello

由于闭包的[[scope]]属性包含了与执行环境作用域链相同的对象的引用,因此会产生副作用,通常来说,函数的活动对象会随着执行环境一同销毁。但引入闭包时,引用仍然存在于闭包的[[scope]]的属性中,因此激活对象无法被销毁,这就意味着脚本中的闭包和非闭包函数相比,需要更多的内存开销。

function test(){
    var bar = 'hello',
          foo = 'foo';
    return function(){
        alert(bar);
    }
}
test()();// 弹出hello

foo永远也不会被使用到,但是它仍然始终存在于活动对象中,这样就会导致内存泄露。

 

原型

对象可以有两种成员类型:实例成员和原型成员,实例成员直接存在与对象中的实例中,原型成员则是从对象原型继承而来

var book = {
    Tiitle: ‘hello’,
    Nums: 666
}
alert(book.toString()) //[obj,obj]

在这段代码中,book 有两个实例成员 title和nums,这里面并没有定义toString方法,但是被顺利执行,没有抛出错误。原因就是 toString 是由对象book 继承来的原型成员。

 

原型链

原型链在我们试图从某个对象获取某个属性(或方法)时发挥作用。如果那个属性刚好像下面这样存在于这个对象之中,那无需多虑,直接返回即可。

var student = {name : 'Jack'}

student.name // =>Jack

但是,如果这个属性不直接存在于这个对象中,那么javascript会在这个对象的构造器的prototype属性,也就是这个对象的__proto__属性中进行查找。

如果找到就返回,否则继续往下面找。由于prototype属性一定是一个对象,因此原型链或者说查找中的最后一站是Object.prototype 若是还是查不到就会返回 undefined。

每深入一层原型链斗会增加性能的消耗,再加上遍历原型链的影响,这让性能问题更加严重。

 

缓存对象成员值

举个例子:

Function abc(ele,name1,name2)
{
       Return ele.name == name1 || ele.name == name2;
}

上面的例子中 ele.name 的值并没有被改变,但是却被读取了两次,可以将ele.name保存到局部变量中,这样读取一次就行了,也是因为局部变量的读取速度快很多。

Function abc(ele,name1,name2)
{
        var name = ele.name;
       Return name  == name1 || name  == name2;
}

 

小结

访问字面量和局部变量的速度最快,相反访问数组元素和对象成员相对较慢。

由于局部变量存在于作用域链的起始位置,因此访问局部变量比访问跨作用域变量更快,变量在在作用域链中的位置越深,访问的所需时间越长。

避免使用with 语句,因为改变执行环境作用于连, catch 也是 小心使用。

嵌套的对象成员会明显影响性能。

属性或方法在原型链中的位置越深,访问速度也就越慢。

通过局部变量减少对象成员的访问次数,以及提高速度,改善性能。

 

转载于:https://www.cnblogs.com/wyhlightstar/p/10218807.html

相关文章:

  • 通过cmake在Android中调用c语言,且三方应用通过so库调用c语言
  • request设置属性 一般当做下一个页面的结果集
  • Niagara基于javascript的控件开发
  • SpringBoot之打成war包部署到Tomcat
  • 基本数据类型中的浮点类型
  • MyBaits 常见面试题
  • 洛谷p1072 gcd,质因数分解
  • 大结局---Miracl库下完全实现SM2加密算法
  • php封装生成随机数函数
  • 洛谷P3372 【模板】线段树 1
  • python3 练习题100例 (二十九)猴子吃桃问题
  • Floyd判断环算法总结
  • freemarker导出定制excel
  • [bzoj1324]Exca王者之剑_最小割
  • Spring Boot 学习笔记(二)第一个 Spring boot 程序
  • @jsonView过滤属性
  • 【Leetcode】104. 二叉树的最大深度
  • 10个最佳ES6特性 ES7与ES8的特性
  • chrome扩展demo1-小时钟
  • fetch 从初识到应用
  • HTTP请求重发
  • Laravel 实践之路: 数据库迁移与数据填充
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • Python学习之路16-使用API
  • text-decoration与color属性
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • vue2.0项目引入element-ui
  • vue学习系列(二)vue-cli
  • Windows Containers 大冒险: 容器网络
  • windows-nginx-https-本地配置
  • XML已死 ?
  • 从0到1:PostCSS 插件开发最佳实践
  • 简析gRPC client 连接管理
  • 力扣(LeetCode)965
  • 前端面试之CSS3新特性
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 如何正确理解,内页权重高于首页?
  • #DBA杂记1
  • #define与typedef区别
  • #include
  • #Spring-boot高级
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (分布式缓存)Redis持久化
  • (附源码)ssm高校实验室 毕业设计 800008
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (七)Java对象在Hibernate持久化层的状态
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • (转载)OpenStack Hacker养成指南
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • ******IT公司面试题汇总+优秀技术博客汇总
  • .CSS-hover 的解释