想写出高效的javascript类库却无从下手;

尝试阅读别人的类库,却理解得似懂给懂;

打算好好钻研js高级函数,但权威书上的内容太零散,

即使记住“用法”,但到要“用”的时候却没有想“法”。

也许你和我一样,好像有一顾无形的力量约束着我们的计划,让我们一再认为知识面的局限性,致使我们原地踏步,难以向前跨越。

这段时间,各种作业、课程设计、实验报告,压力倍增。难得挤出一点点时间,绝不睡懒觉,整理总结往日所看的书,只为了可以离写自己的类库近一点。

本文参考自《javascript语言精粹》和《Effective JavaScript》。例子都被调试过,理解过后,我想把一些“深奥”的道理说得浅显一点点。

1.变量作用域

作用域对于程序员来说就像氧气。它无处不在,甚至,你往往不会去想他。但当它被污染时(例如使用全局对象),你会感觉到窒息(例如应用响应变慢)。javascript核心作用域规则很简单,被精心设计,且很强大。有效地使用javascript需要掌握变量作用域的一些基本概念,并了解一些可能导致难以捉摸的、令人讨厌的问题的极端情况。

1.1尽量少用全局变量

javascript很容易在全局命名空间中创建变量。创建全局变量毫不费力,因为它不需要任何形式的声明,而且能被整个程序的所有代码自动地访问。

对于我们这些初学者,遇到某些需求(例如,传输的数据被记录下来、等待某时机某函数调用时使用;或者是某函数被经常使用)时,好不犹豫想到全局函数,甚至大一学到的C语言面向过程思想太根深蒂固,系统整整齐齐地都是满满函数。定义全局变量会污染共享的公共命名空间,并可能导致意外的命名冲突。全局变量也不利于模块化,因为它会导致程序中独立组件间的不必要耦合。严重地说,过多的全局(包括样式表,直接定义div或者a的样式),整合到多人开发过称将会成为灾难性错误。这就是为什么jQuery的所有代码都被包裹在一个立即执行的匿名表达式——自调用匿名函数。当浏览器加载完jQuery文件后,自调用匿名函数立即开始执行,初始化jQuery的各个模块,避免破坏和污染全局变量以至于影响到其他代码。

(function(window,undefined){var jQuery = ...//...window.jQuery = window.$ = jQuery;
})(window);

另外,你或许会认为,“先怎么怎么写,日后再整理”比较方便,但优秀的程序员会不断地留意程序的结构、持续地归类相关的功能以及分离不相关的组件,并这些行为作为编程过称中的一部分。

由于全局命名空间是javascript程序中独立的组件经行交互的唯一途径,因此,利用全局命名控件的情况是不可避免的。组件或程序库不得不定义一些全局变量。以便程序中的其他部分使用。否则最好使用局部变量。

this.foo ;//undefined
foo = " global foo";
this.foo ;//"global foo"var foo = "global foo";
this.foo = "changed";
foo ;//changed

javascript的全局命名空间也被暴露在程序全局作用域中可以访问的全局对象,该对象作为this关键字的初始值。在web浏览器中,全局对象被绑定在全局window变量。这就意味你创建全局变量有两种方法:在全局作用域内使用var声明他,或者将其加入到全局对象中。使用var声明的好处是能清晰地表达全局变量在程序范围中的影响。

鉴于引用为绑定的全局变量会导致运行时错误,因此,保存作用域清晰和简洁会使代码的使用者更容易理解程序声明了那些全局变量。

由于全局对象提供了全局环境的动态反应机制,所以可以使用它查询一个运行环境,检测在这个平台下哪些特性可用。

eg.ES5引入了一个全局的JSON对象来读写JSON格式的数据。

if(!this.JSON){this.JSON = {parse : ..,stringify : ...    }
}

如果你提供了JSON的实现,你当然可以简单无条件地使用自己的实现。但是由宿主环境提供的内置实现几乎更适合的,因为它们是用C语言写进浏览器的。因为它们按照一定的标准对正确性和一致性进行了严格检查,并且普遍来说比第三方实现提供更好的性能。

当初数据结构课程设计模拟串的基本操作,要求不能使用语言本身提供的方法。javascript对数组的基本操作实现得很好,如果只是出于一般的学习需要,模拟语言本身提供的方法的想法很好,但是如果真正投入开发,无需考虑第一时间选择使用javascript内置方法。

1.2避免使用with

with语句提供任何“便利“,让你的应用变得不可靠和低效率。我们需要对单一对象依次调用一系列方法。使用with语句可以很方便地避免对对象的重复引用:

function status(info){var widget = new Widget();with(widget){setBackground("blue");setForeground("white");setText("Status : "+info);show();}
}

使用with语句从模块对象中”导入“(import)变量也是很有诱惑力的。

function f(x,y){with(Math){return min(round(x),sqrt(y));//抽象引用
   }
}

事实上,javascript对待所有的变量都是相同的。javascript从最内层的作用域开始向外查找变量。with语言对待一个对象犹如该对象代表一个变量作用域,因此,在with代码块的内部,变量查找从搜索给定的变量名的属性开始。如果在这个对象中没有找到该属性,则继续在外部作用域中搜索。with块中的每个外部变量的引用都隐式地假设在with对象(以及它的任何原型对象)中没有同名的属性。而在程序的其他地方创建或修改with对象或其原型对象不一定会遵循这样的假设。javascript引擎当然不会读取局部代码来获取你使用了那些局部变量。javascript作用域可被表示为高效的内部数据结构,变量查找会非常快速。但是由于with代码块需要搜索对象的原型链来查找with代码里的所有变量,因此,其运行速度远远低于一般的代码块。

替代with语言,简单的做法,是将对象绑定在一个简短的变量名上。

function status(info){var w = new Widget();w.setBackground("blue");w.setForeground("white");w.setText("Status : "+info);w.show();}

其他情况下,最好的方法是将局部变量显式地绑定到相关的属性上。

function f(x,y){var    min    = Math.min,round  = Math.round,sqrt   = Math.sqrt;  return min(round(x),sqrt(y));
}

1.3熟练掌握闭包

理解闭包有单个概念:

a)javascript允许你引用在当前函数以外定义的变量。

function makeSandwich(){var magicIngredient = "peanut butter";function make(filling){return magicIngredient + " and " + filling;}return make("jelly");
}makeSandwich();// "peanut butter and jelly"

b)即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量

function makeSandwich(){var magicIngredient = "peanut butter";function make(filling){return magicIngredient + " and " + filling;}return make; 
}var f = sandwichMaker();
f("jelly");                      // "peanut butter and jelly"
f("bananas");               // "peanut butter and bananas"
f("mallows");               // "peanut butter and mallows"

javascriptd的函数值包含了比调用它们时所执行所需要的代码还要多的信息。而且,javascript函数值还在内部存储它们可能会引用的定义在其封闭作用域的变量。那些在其所涵盖的作用域内跟踪变量的函数被称为闭包

make函数就是一个闭包,其代码引用了两个外部变量:magicIngredient和filling。每当make函数被调用时,其代码都能引用这两个变量,因为闭包存储了这两个变量。

函数可以引用在其作用域内的任何变量,包括参数和外部函数变量。我们可以利用这一点来编写更加通用的sandwichMaker函数。

function makeSandwich(magicIngredient){function make(filling){return magicIngredient + " and " + filling;}return make;
}var f = sandwichMaker(”ham“);
f("cheese");                      // "ham and cheese"
f("mustard");               // "ham and mustard"

闭包是javascript最优雅、最有表现力的特性之一,也是许多习惯用法的核心。

c)闭包可以更新外部变量的值。事实上,闭包存储的是外部变量的引用,而不是它们的值的副本。因此,对于任何具有访问这些外部变量的闭包,都可以进行更新。

function box(){var val = undefined;return {set : function(newval) {val = newval;},get : function (){return val;},type : function(){return typeof val;}};
}var b = box();
b.type(); //undefined
b.set(98.6);
b.get();//98.6
b.type();//number

该例子产生一个包含三个闭包的对象。这三个闭包是set,type和get属性,它们都共享访问val变量,set闭包更新val的值。随后调用get和type查看更新的结果。

1.4理解变量声明提升

javascript支持此法作用域(对变量foo的引用会被绑定到声明foo变量最近的作用域中),但不支持块级作用域(变量定义的作用域并不是离其最近的封闭语句或代码块)。

不明白这个特性将会导致一些微妙的bug:

function isWinner(player,others){var highest = 0;for(var i = 0,n = others.length ;i<n;i++){var player = others[i];if(player.score > highest){highest = player.score;}}return player.score > highest;
}

1.5 当心命名函数表达式笨拙的作用域

function double(x){ return x*2; }var f = function(x){ return x*2; }

同一段函数代码也可以作为一个表达式,却具有截然不同的含义。匿名函数和命名函数表达式的官方区别在于后者会绑定到与其函数名相同的变量上,该变量作为该函数的一个局部变量。这可以用来写递归函数表达式。

var f = function find(tree,key){//....return find(tree.left , key) || find(tree.right,key);
}

值得注意的是,变量find的作用域只在其自身函数中,不像函数声明,命名函数表达式不能通过其内部的函数名在外部被引用。

find(myTree,"foo");//error : find is not defined; 

var constructor = function(){ return null; }
var f= function(){return constructor();
};
f();//{}(in ES3 environments)

该程序看起来会产生null,但其实会产生一个新的对象。

因为命名函数变量作用域内继承了Object.prototype.constructor(即Oject的构造函数),就像with语句一样,这个作用域会因Object.prototype的动态改变而受到影响。在系统中避免对象污染函数表达式作用域的办法是避免任何时候在Object.prototype中添加属性,以避免使用任何与标准Object.prototype属性同名的局部变量。

在流行的javascript引擎中另外一个缺点是对命名函数表达式的声明进行提升。

var f = function g(){return 17;}
g(); //17 (in nonconformat environment)

一些javascript环境甚至把f和g这两个函数作为不同的对象,从而导致不必要的内存分配。

1.6 当心局部块函数声明笨拙的作用域

function f() {return "global" ; }function  test(x){function f(){return "local";}var result = [];if(x){result.push(f());}      result.push(f());result result;
}test(true);   //["local","local"]
test(false);  //["local"]

function f() {return "global" ; }function  test(x){var result = [];if(x){function f(){return "local";}result.push(f());}      result.push(f());result result;
}test(true);   //["local","local"]
test(false);  //["local"]

javascript没有块级作用域,所以内部函数f的作用域应该是整个test函数。一些javascript环境确实如此,但并不是所有javascript环境都这样,javascript实现在严格模式下将这类函数报告为错误(具有局部块函数声明的处于严格模式下的程序将报告成一个语法错误),有助于检测不可移植代码,为未来的标准版本在给局部块函数声明给更明智和可以的语义。针对这种情况,可以考虑在test函数内声明一局部变量指向全局函数f。

转载于:https://www.cnblogs.com/0603ljx/p/4059125.html

编写高质量JS代码上相关推荐

  1. 表示python代码块的是_编写高质量Python代码的59个有效方法,你用过几个

    欢迎点击右上角关注小编,除了分享技术文章之外还有很多福利,私信学习资料可以领取包括不限于Python实战演练.PDF电子文档.面试集锦.学习资料等. 这个周末断断续续的阅读完了<Effectiv ...

  2. iOS 编写高质量Objective-C代码(六)

    级别: ★★☆☆☆ 标签:「iOS」「Block」「Objective-C」 作者: MrLiuQ 审校: QiShare团队 前言: 这几篇文章是小编在钻研<Effective Objecti ...

  3. 编写高质量的代码,改善c#程序的157个建议_之1~10

    //编写高质量的代码,改善c#程序的157个建议 # 1.正确操作字符串 ## 1 * 确保尽量少的装箱 * 避免分配额外的内存空间 1. 会进行一次装箱 ````C# string str1=&qu ...

  4. 《编写高质量Python代码的59个有效方法》——第19条:用关键字参数来表达可选的行为...

    本节书摘来自华章社区<编写高质量Python代码的59个有效方法>一书中的第19条:用关键字参数来表达可选的行为,作者[美]布雷特·斯拉特金(Brett Slatkin),更多章节内容可以 ...

  5. 如何编写高质量的代码二 - 类的设计

    2019独角兽企业重金招聘Python工程师标准>>> 如何设计类, 关键点: 类的接口应该提供一贯的抽象.很多问题都是因为违背了这个简单的原则. 类的接口应该隐藏某些东西,比如系统 ...

  6. 编写高质量的代码—从命名入手(命名…

     编写高质量的代码-从命名入手(命名规范) 笔者从事开发多年,有这样一种感觉,查看一些开源项目,如Spring.Apache Common等源码是一件赏心悦目的事情,究其原因,无外两点:1)代码质量非 ...

  7. python中如何编写代码输入多个数据并把它们放在一个列表中去_编写高质量Python代码的59个有效方法,你用过几个...

    欢迎点击右上角关注小编,除了分享技术文章之外还有很多福利,私信学习资料可以领取包括不限于Python实战演练.PDF电子文档.面试集锦.学习资料等. 这个周末断断续续的阅读完了<Effectiv ...

  8. python中如何编写代码输入多个数据并把它们放在一个列表中去_这59条编写高质量Python代码的方法你知道吗?...

    这个周末断断续续的阅读完了<Effective Python之编写高质量Python代码的59个有效方法>,感觉还不错,具有很大的指导价值. 下面将以最简单的方式记录这59条建议,并在大部 ...

  9. [编写高质量iOS代码的52个有效方法](五)接口与API设计(下)

    [编写高质量iOS代码的52个有效方法](五)接口与API设计(下) 参考书籍:<Effective Objective-C 2.0> [英] Matt Galloway 先睹为快 19. ...

最新文章

  1. 实践|超简单实现OCR文字识别应用
  2. Python/word.so: undefined symbol: _ZNK4Word7reverseEv
  3. 深入理解Android 卷I 提供预定
  4. 计算机管理ap,如何设置AC功能管理无线AP
  5. 地理必修一三大类岩石_高中地理 | 必备基础知识点干货
  6. leecode刷题(4)-- 存在重复数组
  7. 嵌入式系统功能概要设计说明_嵌入式软件架构设计实际该怎么做?
  8. [剑指offer] 旋转数组的最小数字
  9. 新版的Lync客户端无法用于老版的OCS服务器
  10. php实用教程第3版郑阿奇课后答案_SQL Server 实用教程(第3版)课后实验答案 郑阿奇主编的 邮箱344561468@qq.com...
  11. 软件测试复盘思路个人总结
  12. 随机过程通过线性系统matlab,随机信号分析实验:随机过程通过线性系统的分析.doc...
  13. 【代码审计】代码安全测试的方法
  14. Win11不能拖拽图片到任务栏软件上快速打开怎么办
  15. Word2013出现未响应的解决方法
  16. 4.7开发者日:北极光创投吴峰的投资只管杀不管埋
  17. 如何用 Python 爬取需要登录的网站?
  18. 控制性详细规划图纸成果及深度要求
  19. 什么是Linkerd
  20. C#人民币大小写金额转换(C#版本)

热门文章

  1. makefile高级应用
  2. Jumpserver0.4.0基于Centos7安装
  3. RESTful Request:GET/PUT/DELETE/POST/HEAD/OPTIONS
  4. Ubuntu 出现apt-get: Package has no installation cand
  5. lenovo X230热键功能
  6. win7 IE9 internet explorer[IE] [IE 9]已停止工作
  7. shell 编程中的判断
  8. Real Application Cluster 10g安装与配置(下)
  9. 出版新书《从毕业生到程序员 使用C#开发商业软件》
  10. 杭电 1060 Leftmost Digit