第43条中讲到的就算是用了Object的直接实例,也无法完全避免,Object.prototype对象修改,造成的原型污染.防止原型污染最简单的方式之一就是不使用原型.在ES5之前,并没有标准的方式创建一个空原型的新对象. 尝试 设置构造函数的原型属性为null或undefined function C(){} C.prototype=null; 结果 实例化该构造函数仍然得到是Object的实例. var c=new C(); Object.getPrototypeOf(c) === null…
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //"number" typeof “s”;//"string" typeof null;//"object":ECMAScript把null描述为独特的类型,但返回值却是对象类型,有点困惑. 可以使用Object.prototype.toString.ca…
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaScript笔记]第3条:当心隐式的强制转换> 因为这个会在比较之前对两个值都进行隐式转换.字符串“1.0e0”被解析成1,而{valueOf:function(){return true;}}会通过调用自身的valueOf进行转化得到true,然后再转化为数字,得到1; 很容易使用强制转换完成一些工作.如…
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式传递给eval函数以达到同样的功能.程序员面临一个选择:应该将代码表示为函数还是字符串?毫无疑问,应该将代码表示为函数.字符串表示代码不够灵活的一个重要原因是:它们不是闭包. 闭包回顾 看下面这个图 js的函数值包含了比调用它们时执行所需要的代码还要多的信息.而且js函数值还在内部存储它们可能会引用…
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+1}" 反射获取函数源代码的功能很强大,使用函数对象的toString方法有严重的局限性.toString方法的局限性ECMAScript标准对函数对象的toString方法的返回结果(即该字符串)并没有任何要求.这意味着不同的js引擎将产生不同的字符串,甚至产生的字符串与该函数并不相关. 如果函数…
之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangsan=22; js的对象操作总是经继承的方式工作的.即使是一个空的对象字面量也是继承了Object.protoype属性. var dict={}; 'zhangsan' in dict;//false 'lisi' in dict;//false 'wangwu' in dict;//false'…
构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.txt',function(file){ console.log('file:'+file); }); 基于promise的API不接收回调函数作为参数.相反,它返回一个promise对象,该对象通过其自身的then方法接收回调函数. var p=downloadP('file.txt'); p.th…
设想有downloadAsync函数的一种变种,它持有一个缓存(实现为一个Dict)来避免多次下载同一个文件.在文件已经被缓存的情况下,立即调用回调函数是最优选择. var cache=new Dict(); function downloadCachingAsync(url,onsuccess,onerror){ if(cache.has(url)){ onsuccess(cache.get(url)); return; } return downloadAsync(url,function(…
第63条建议使用工具函数downloadAllAsync接收一个URL数组并下载所有文件,结果返回一个存储了文件内容的数组,每个URL对应一个字符串.downloadAllAsync并不只有清理嵌套回调函数的好处,其主要好处是并行下载文件.我们可以在同一个事件循环中一次启动所有文件的下载,而不用等待每个文件完成下载.并行逻辑是微妙的,很容易出错.下面有实现有一个隐藏的缺陷. function downloadAllAsync(urls,onsuccess,onerror){ var result…
第61条解释了异步API怎样帮助我们防止一段程序阻塞应用程序的事件队列.使用下面代码,可以很容易使一个应用程序陷入泥潭. while(true){} 而且它并不需要一个无限循环来写一个缓慢的程序.代码需要时间来运行,而低效的算法或数据结构可能导致运行长时间的计算.效率不是js唯一关注的.基于事件的编程的确强加了一些特殊的约束.为了保持客户端应用程序的高度交互性和确保所有传入的请求在服务器应用程序中得到充分的服务,保持事件循环的每个轮次尽可能短是至关重要.否则,事件队列会滞销,其增长速度会超过分发…
管理异步编程的一个是错误处理.同步代码中只要使用try语句块包装一段代码很容易一下子处理所有的错误. try{ f(); g(); h(); } catch(e){ //这里用来下得出现的错误 } try语句块 但对于异步的代码,多步的处理通常会被分隔到事件队列的单独轮次中,因此,不可能将它们包装在一个try语句块中.事实上异步的API甚至根本不可能抛出异常,因为,当一个异步的错误发生时,没有一个明显的执行上下文来抛出异常!相反,异步的API倾向于将错误表示为回调函数的特定参数,或使用一个附加的…
第21条讲述使用可变参数的函数average.该函数可处理任意数量的参数并返回这些参数的平均值. 如何创建可变参数的函数 1.实现固定元数的函数 书上的版本 function averageOfArray(a){ for(var i=0,sum=0,n=a.length;i<n;i++){ sum+=a[i]; } return sum/n } 使用ES5 Array.reduce方法的版本 function averageOfArrayES5(a){ if({}.toString.call(a…
对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置不同的属性与顺序无关,都会以大致相同的效率产生相同的结果.也就是说访问属性a和访问属性b,没有哪个访问更快之说.ES标准并未规定属性存储的任何特定顺序,甚至于枚举对象也未涉及.for...in循环会挑选一定的顺序来枚举对象的属性,标准允许js引擎自由选择一个顺序,它们的选择会微妙地改变程序行为.如要…
undefined值很特殊,每当js无法提供具体的值时,就会产生undefined. undefined值场景 未赋值的变量的初始值即为undefined. var x; x;//undefined 访问对象不存在的属性也会产生undefined. var obj={}; obj.x;//undefined 一个函数体结尾使用未带参数的return语句,或未使用return语句都会返回值undefined. function f(){ return; } function g(){} f();/…
异步程序的操作顺序 61条讲述了异步API如何执行潜在的代价高昂的I/O操作,而不阻塞应用程序继续处理其他输入.理解异步程序的操作顺序刚开始有点混乱.例如,下面的代码会在打印"finished"之前打印“starting”,即使这两个动作的程序源文件中以相反的顺序呈现. downloadAsync('file.txt',function(file){ console.log('finished'); }); console.log('starting'); downloadAsync调…
假设需要有这样一个函数,接收一个URL的数组并尝试依次下载每个文件直到有一个文件被成功下载.如果API是同步的,使用循环很简单实现. function downloadOneSync(urls){ for(var i=0,n=urls.length;i< n;i++){ try{ return downloadSync(urls[i]); }catch(e){} } throw new Error('all downloads failed.'); } 在异步情况下,上面的这种方式就无法正确工作…
JavaScript数值型类型只有数字 js只有一种数值型数据类型,不管是整数还是浮点数,js都把归为数字. typeof 17;   // “number” typeof 98.6; // “number” typeof –2.1; // “number” js中的所有数字都是双精度浮点数.是由IEEE754标准制定的64位编码数字(这个是什么东东,不知道,回头查一下吧) 那么js是如何表达整数的,双精度浮点数可以完美地表示高达53位精度的整数(没有什么概念,没处理过多大的数据,没用完过!),…
js对类型错误出奇的宽容 3+true;  //4 3*””;  //0 3+[]; //3 3+[3]; //33 以上表达式在许多语言早就变红了.而js不但不报错还给你个结果. 极少情况会产生即时错误,非函数对象或试图选择null的属性. “hello”(1);//error:not a function null.x; //error:cannot read property ‘x’of null 大多数情况是不会抛错的,按照多种多样的方式进行着转换. 运算操作 算术运算符在计算之前会尝试…
分号可以省略 js可以在语句结束不强制加分号.(建议还是添加,不添加分号往往会出现不易发现的BUG) function Point(x,y){ this.x=x||0; this.y=y||0; } Point.prototype.isOrigin=function(){ return this.x===0 && this.y===0 } 上面代码可以运行,是由于js可以自动插入分号,它是一种程序解析技术.能推断出某些上下文中省略的分号,然后有效地自动地将分号“插入”到程序中. ECMAS…
js函数会根据上下文改变其含义. function double(x){return x*2;} 这是一个函数声明,也可以是一个命名函数表达式(named function expression),取决于它出现的地方. 声明一个函数,并绑定一个当前作用域的变量. 同一段函数代码也可以作为一个表达式. var f=function double(x){return x*2;} 根据ECMAScript规范,该函数绑定到变量f,而不是变量double.这里给函数表达式命名并不是必要的的,可以直接使用…
嵌套函数声明.没有标准的方法在局部块里声明函数,但可以在另一个函数的顶部嵌套函数声明. function f(){return "global"} function test(x){ var result=[]; function f(){return "local";}//block-local if(x){ result.push(f()); } result.push(f()); return result; } test(true);//["loc…
不好的实践 函数或方法的接收者(即绑定到特殊关键字this的值)是由调用者的语法决定的.方法调用语法将方法被查找的对象绑定到this变量,(可参阅之前文章<理解函数调用.方法调用及构造函数调用之间的不同>).有时需要使用自定义接收者来调用函数,因为该函数可能并不是期望的接收者对象的属性.可以将方法作为一个新的属性添加到接收者对象中.使用如下代码: var obj={ temporary:function(a,b,c){console.log('obj')} } var f=function(a…
apply()方法定义 函数的apply()方法和call方法作用相同,区别在于接收的参数的方式不同.apply()方法接收两个参数,一个是对象,一个是参数数组. apply()作用 1.用于延长函数的作用域 示例: var color='red'; var o={color:'blue'}; function sayColor(){ console.log(this.color); } sayColor();//"red" sayColor.apply(o);//"blue…
bind方法的作用,除了有绑定函数到对象外,我们来看看bind方法的一些其它应用. 简单示例 例子:假设有一个装配URL字符串的简单函数.代码如下 function simpleURL(protocol,domain,path){ return protocol+'://'+domain+'/'+path; } 要将特定站点的路径字符串构建为绝对路径URL.可以使用ES5中数组的map方法来实现.如下 var paths=['wengxuesong/','wengxuesong/p/556048…
ES5引入Object.getPrototypeOf函数作为获取对象原型的标准API,但由于之前的很多js引擎使用了一个特殊的__proto__属性来达到相同的目的.但有些浏览器并不支持这个__proto__属性,所以并不是完全兼容的.例如对于拥有null原型的对象,不同的环境结果就不同了. var empty=Object.create(null); '__proto__' in empty;//一些环境会返回false,另一些会返回true 这就导致结果的不一致,从而影响到依赖这个判断的相关…
当使用函数作为一个构造函数时,程序依赖于调用者是否记得使用new操作符来调用该构造函数.注意:该函数假设接收者是一个全新的对象. 一个例子 function User(name,pwd){ this.name=name; this.pwd=pwd; } 当调用者,忘记使用new关键字时,那么这个函数的接收者是全局对象. var u=User('wengxuesong','asdfasdfadf'); u;//undefinedthis.name;//'wengxuesong'this.pwd;/…
js的对象系统并没有特别鼓励或强制信息隐藏.所有的属性名都是一个字符串,任意一个程序都可以简单地通过访问属性名来获取相应的对象属性.例如,for...in循环.ES5的Object.keys()和Object.getOwnPropertyNames()函数等特性都能轻易地获取对象的所有属性名. 怎么处理私有属性 编码规范 js程序员诉诸于编码规范,而不是任何绝对的强制机制.例如,一些程序员使用命名规范给私有属性名前置或后置一个下划线字符(_).这并没有强制隐藏,而只是表明对对象的正确行为操作的一…
CSVReader示例 需求 CSV(逗号分隔型取值)文件格式是一种表格数据的简单文本表示 张三,1982,北京,中国 小森,1982,东京,日本 吉姆,1958,纽约,美国 现需要编写一个简单的.可定制的读取CSV数据的类.这里的分隔符是基于逗号的,但也可以处理一些其它字符作为分隔符. 分析 构造函数需要一个可选的分隔器字符数组并构造出一个自定义的正则表达式以将每一行分成不同的条目. function CSVReader(separators){ this.separators=separat…
假设想给上节讲的场景图库添加收集诊断信息的功能.这对于调试和性能分析很有用. 38条示例续 给每个Actor实例一个唯一的标识数. 添加标识数 function Actor(scene,x,y){ this.scene=scene; this.x=x; this.y=y; this.id=++Actor.nextID; scene.register(this); } Actor.nextID=0; 现在我们需要对Actor的子类做同样的事.假设,Alien类代表太空飞船的敌人.除了其角色标识数外…
41条对违反抽象原则行为的讨论之后,下面聊一聊终极违例.由于对象共享原型,因此每一个对象都可以增加.删除或修改原型的属性.这个有争议的实践通常称为猴子补丁. 猴子补丁示例 猴子补丁的吸引力在于其强大.数组缺少一个有用的方法吗?你自己就可以增加它. Array.prototype.split=function(i){ return [this.slice(0,i),this.slice(i)]; } 很完美,现在可以在任意的数组上调用这个方法了.但当多个库以不兼容的方式给同一个原型打猴子补丁时,另…