想写出高效的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 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。

如何编写高质量JS代码的更多相关文章

  1. html5 拖拽及用 js 实现拖拽功能的示例代码

    这篇文章主要介绍了html5 拖拽及用 js 实现拖拽,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. HTML5适合的情人节礼物有纪念日期功能

    这篇文章主要介绍了HTML5适合的情人节礼物有纪念日期功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. Swift与Js通过WebView交互

    开发环境:Swfit2.3XCode8.2基础概念jscontext,jscontext是代表JS的执行环境,通过-evaluateScript:方法就可以执行一JS代码JSValue,JSValue封装了JS与ObjC中的对应的类型,以及调用JS的API等JSExport,JSExport是一个协议,遵守此协议,就可以定义我们自己的协议,在协议中声明的API都会在JS中暴露出来,才能调用Swif

  4. Swift WKWebView的js调用swift

    最近项目需求,需要用到JavaScriptCore和WebKit,但是网上的资源有限,而且比较杂,都是一个博客复制另外一个博客,都没有去实际敲代码验证,下面给大家分享一下我的学习过程。

  5. Swift WKWebView的swift调用js

    不多说,直接上代码:在html里面要添加的的代码,显示swift传过去的参数:这样就实现了swift给js传参数和调用!

  6. swift - WKWebView JS 交互

    本文介绍WKWebView怎么与js交互,至于怎么用WKWebView这里就不介绍了HTML代码APP调JS代码结果JS给APP传参数首先注册你需要监听的js方法名2.继承WKScriptMessageHandler并重写userContentController方法,在该方法里接收JS传来的参数3.结果

  7. swift 开发UIWebView跟JS的交互

    前言作为小白的我,才开始入门IOS,选择了swift来进行入门学习,学习做着公司一个简单的小小项目,该项目需要进行跟H5进行交互,然后我就开始研究了UIWebView的使用,其实基本原理跟Android的一样,因为我是Android开发的,所以就顺水推舟了。))//这里设置你需要加载的地址}overridefuncdidReceiveMemoryWarning(){super.didReceiveMemoryWarning()//disposeofanyresourcesthatcanberecreate

  8. 手把手教你swift项目集成cocos2dx-js模块

    前几天在swift项目中集成了Lua模块,使得在swift工程中用Lua写游戏逻辑成为了可能,具体工程及配置见手把手教你swift项目添加cocos2dx-lua,由于公司最近要把js做的小游戏集成到iOS原生应用中,于是我们将解锁另外一个场景,好了,下面开始;同样的,首先你得有一个swift项目我们从头开始,建立一个swift项目;我们默认你已经可以自己创建一个全新的swift项目了,这很简单,不是么?

  9. swift – 与WKWebView进行JavaScript同步本地通信

    使用WKWebView可以在JavaScript和Swift/Obj-C本机代码之间进行同步通信吗?

  10. swift2 – 将本地css和js文件加载到WKWebView中

    在Swift2.1.1&Xcode7.1我的代码使用WKWebView并将index.html加载为本地文件,但无法加载index.css和其他javascript文件,如head标记所示.我最好的猜测是baseURL不正确,如果是这样,我该如何正确设置baseURL?谢谢}在阅读了有关文件URL的here之后,我能够解决问题.这是代码

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部