[Effective JavaScript 笔记]第60条:支持方法链

无状态的API的部分能力是将复杂操作分解为更小的操作的灵活性。一个很好的例子是字符串的replace方法。由于结果本身也是字符串,可以对前一个replace操作重复执行替换。这种模式的一个常见用例是在将字符串插入到HTML前替换字符串的特殊字符字母。

function escapeBasicHTML(str){
    return str.replace(/&/g,"&")
              .replace(/< /g,"&lt;")
              .replace(/>/g,"&gt;")
              .replace(/"/g,"&quot;")
              .replace(/‘/g,"&apos;");
}

对replace的第一次调用返回一个将所有特殊字符"&"替换为HTML字符串的转义序列“&”的字符串;以此类推。这种重复的方法调用风格叫做方法链。这种风格不需要保存中间结果为变量,更简洁。jquery就是一个很好的例子。

function escapeBasicHTML(str){
    var str2=str.replace(/&/g,"&amp;")
    var str3=str2.replace(/< /g,"&lt;")
    var str4=str3.replace(/>/g,"&gt;")
    var str5=str4.replace(/"/g,"&quot;")
    var str6=str5.replace(/‘/g,"&apos;");
    return str6;
}

消除临时变量使代码变得更加可读,中间结果只是得到最终结果中的一个重要的步骤而已。
如果一个API产生了一个接口对象,调用这个接口对象的方法产生的对象如果具有相同的接口,那么就可以使用方法链。如前面50条和51条中描述的数组迭代方法就是另一个链式API。

var users=records.map(function(record){
    return record.username;
})
.filter(function(username){
    return !!username;
})
.map(function(username){
    return username.toLowerCase();
});

因为数组的每一种迭代方法,返回得都是一个数组。可以方便地再使用数组方法进行处理。
这种风格非常灵活,并且对于API的使用者富有表现力,所以将API设计为支持这种风格是值得的。
通常情况下,
无状态的API中,如果API不修改对象,而是返回一个新对象,则链式得到了自然的结果。因此,API的方法提供了更多相似方法集的对象。
有状态的API中,这里的技巧是方法在更新对象时返回this,而不是undefined。这使得通过链式方法调用的序列来对同一个对象执行多次更新成为可能。

element.setBackgroundColor(‘yellow‘)
       .setColor(‘red‘)
       .setFontWeight(‘bold‘);

有状态的API的方法有时被称为流畅式。如果更新方法没返回this,那么API的调用者不得不每次重复该对象的名称。如果该对象被简单命名为一个变量,这没有什么区别。但当结合无状态的方法用于检索更新的对象,方法链非常简洁,并且代码更可读。jquery中的方法普遍采用这种方法。它有一组(无状态的)方法用于从用户界面元素中查询网页,还有一组(有状态的)方法用于更新这些元素。

$(‘#notification‘)
   .html(‘Server not responding.‘)
   .removeClass(‘info‘)
   .addClass(‘error‘);

通过调用有状态的html,removeClass,addClass方法而返回相同对象来支持流畅式,不用创建临时变量存储jQuery函数执行查询的结果。如果觉得这种风格不太习惯,也可以添加中间变量来存储各函数的返回值。

var element=$(‘#notification‘);
element.html(‘Server not responding.‘);
element.removeClass(‘info‘);
element.addClass(‘error‘);

通过支持方法链,API允许程序员按自己的喜好选择风格。如果方法返回undefined,用户会被强迫使用更啰嗦的风格。

提示

  • 使用方法链来连接无状态的操作
  • 通过在无状态的方法中返回新对象来支持方法链
  • 通过在有状态的方法中返回this来支持方法链
时间: 2024-10-17 00:32:48

[Effective JavaScript 笔记]第60条:支持方法链的相关文章

[Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+1}" 反射获取函数源代码的功能很强大,使用函数对象的toString方法有严重的局限性.toString方法的局限性ECMAScript标准对函数对象的toString方法的返回结果(即该字符串)并没有任何要求.这意味着不同的js引擎将产生不同的字符串,甚至产生的字符串与该函数并不相关. 如果函数

[Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式传递给eval函数以达到同样的功能.程序员面临一个选择:应该将代码表示为函数还是字符串?毫无疑问,应该将代码表示为函数.字符串表示代码不够灵活的一个重要原因是:它们不是闭包. 闭包回顾 看下面这个图 js的函数值包含了比调用它们时执行所需要的代码还要多的信息.而且js函数值还在内部存储它们可能会引用

[Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //"number" typeof "s";//"string" typeof null;//"object":ECMAScript把null描述为独特的类型,但返回值却是对象类型,有点困惑. 可以使用Object.prototype.t

[Effective JavaScript 笔记]第44条:使用null原型以防止原型污染

第43条中讲到的就算是用了Object的直接实例,也无法完全避免,Object.prototype对象修改,造成的原型污染.防止原型污染最简单的方式之一就是不使用原型.在ES5之前,并没有标准的方式创建一个空原型的新对象. 尝试 设置构造函数的原型属性为null或undefined function C(){} C.prototype=null; 结果 实例化该构造函数仍然得到是Object的实例. var c=new C(); Object.getPrototypeOf(c) === null

[Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

构建异步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

[Effective JavaScript 笔记]第50条:迭代方法优于循环

"懒"程序员才是好程序员.复制和粘贴样板代码,一但代码有错误,或代码功能修改,那么程序在修改的时候,程序员需要找到所有相同功能的代码一处处进行修改.这会使人重复发明轮子,而且在别人看代码的时候无法在更高层次都看待问题的解决方案.太容易陷入细节. for循环 js中的for循环在进行一些细微变化时,可以引入不同的行为.编程的时候对于边界条件的判断往往会导致一些简单的错误.下面的一些for循环的细微变化导致边界条件的变化. for(var i=0;i<=n;i++){...} //包

[Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合

对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置不同的属性与顺序无关,都会以大致相同的效率产生相同的结果.也就是说访问属性a和访问属性b,没有哪个访问更快之说.ES标准并未规定属性存储的任何特定顺序,甚至于枚举对象也未涉及.for...in循环会挑选一定的顺序来枚举对象的属性,标准允许js引擎自由选择一个顺序,它们的选择会微妙地改变程序行为.如要

[Effective JavaScript 笔记]第29条:避免使用非标准的栈检查属性

许多js环境都提供检查调用栈的功能.调用栈是指当前正在执行的活动函数链.在某些旧的宿主环境中,每个arguments对象含有两个额外的属性:arguments.callee和arguments.caller.前者指向使用该arguments对象被调用的函数.后者指向调用该arguments对象被调用的函数的函数.许多环境支持arguments.callee,但它除了允许匿名函数递归地引用自身之外,没有更多的用途了.(高3中认为使用arguments.callee可以解除函数体内的代码和函数名之间

[Effective JavaScript 笔记]第15条:当心局部块函数声明笨拙的作用域

嵌套函数声明.没有标准的方法在局部块里声明函数,但可以在另一个函数的顶部嵌套函数声明. 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