如果你提供了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();
其他情况下,最好的方法是将局部变量显式地绑定到相关的属性上。 复制代码 代码如下: 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"]