方法链是一种流行的编程方法,能够帮助你写出更简洁易读的代码。在本文中咱们一块儿学习 JavaScript 中的方法链是什么,以及它是怎样工做的。另外咱们还会探讨如何使用方法连接来提升代码的质量和可读性。javascript
你必定曾经用过 jQuery 之类的库,可能看到过相似的东西。在进行级联时主要有两种方法:一种是一个接一个的执行方法,另外一种是在同一行上。在纯 JavaScript 中这种作法也很广泛。你能够在数组、字符串和 promise 看到它。前端
在这些状况下全部的过程都是相同的。首先引用要使用的对象。而后根据须要使用多种方法。但不是单独使用这些方法,而要一个接一个地使用。基本上是把它们连接在一块儿。先看一些例子。java
在处理字符串时有两种方法。第一个种不用方法链,这要求必须在字符串上分别使用每一个方法,这样必须每次都引用这个字符串。程序员
第二种方式是用方法链。这时能够用全部想要的字符串方法。写出的代码也能够是单行或多行,这取决于你的习惯。并且只须要引用一次字符串。尽管结果相同,可是代码量却有很大的差别。面试
// 在字符串上使用方法链的例子 let myStr = ' - Hello-world. ' // 不用方法链: myStr = myStr.toLowerCase() myStr = myStr.replace(/-/g, ' ') myStr = myStr.trim() // 用方法链: myStr = myStr.toLowerCase().replace(/-/g, ' ').trim() // 多行方法链的写法: myStr = myStr .toLowerCase() .replace(/-/g, ' ') .trim() // 查看 "myStr" 的值 console.log(myStr) // Output: // 'hello world.'
在数组上也能用方法链:编程
// 在数组上使用方法链的例子 let myArray = [1, 7, 3, null, 8, null, 0, null, '20', 15] // 不用方法链: myArray = myArray.filter(el => typeof el === 'number' && isFinite(el)) myArray = myArray.sort((x, y) => x - y) // 使用方法链: myArray = myArray.filter(el => typeof el === 'number' && isFinite(el)).sort((x, y) => x - y) // 多行方法链的写法: myArray = myArray .filter(el => typeof el === 'number' && isFinite(el)) .sort((x, y) => x - y) // 查看 "myArray" 的值. console.log(myArray) // Output: // [ 0, 1, 3, 7, 8 ]
Promise 是一个很好的例子,由于在使用时差很少全都是方法链。首先建立一个 promise,而后添加适当的处理函数。segmentfault
// 建立 Promise const myPromise = new Promise((resolve, reject) => { // 建立一个假延迟 setTimeout(function() { // 用一条简单的消息解决诺言 promise resolve('Sorry, no data.') }, 1000) }) // 使用方法链: myPromise.then((data) => console.log(data)).catch(err => console.log(error)) // 多行方法链的写法: myPromise .then((data) => console.log(data)) .catch(err => console.log(error)) // Output: // 'Sorry, no data.'
接下来研究它是怎样工做的。答案很简单,是由于 this
。数组
假设有一个对象。若是在该对象内使用 this
,它会引用这个对象。若是建立该对象的实例或副本,则 this
将会引用这个实例或副本。当你使用某些字符串或数组方法时,其实是在用一个对象。promise
const myObj = { name: 'Stuart', age: 65, sayHi() { // 这里的 this 是 myObj 的引用 return `Hi my name is ${this.name}.` }, logMe() { console.log(this) } } myObj.sayHi() // Output: // 'Hi my name is Stuart.' myObj.logMe() // Output: // { // name: 'Stuart', // age: 65, // sayHi: ƒ, // logMe: ƒ // }
若是是字符串,则使用的是原始数据类型。可是你所使用的方法例如 toLowerCase()
,存在于 String
对象的原型中。在对象上使用方法链还有一个关键要素: this
。服务器
为了使链起做用,方法必须返回与其一块儿使用的对象,也就是必须返回 this
。就像接力赛跑时的接力棒同样。
为了使方法链有效,必须知足三个条件:首先,须要一些对象。其次,该对象须要一些之后能够调用的方法。第三,这些方法必须返回对象自己,它们必须返回 this
才能使用方法链。
让咱们建立一个简单的对象 person
。 person
有 name
, age
和 state
属性。state
用来表示当前处于什么状态。要想改变这个状态,须要用到几个方法:walk()
, sleep()
, eat()
, drink()
, work()
和 exercise()
。
因为咱们但愿全部这些方法都是可链的,因此它们都必须返回 this
。另外代码中还有一个用来把当前状态记录到控制台的工具方法。
// 建立 person 对象 const person = { name: 'Jack Doer', age: 41, state: null, logState() { console.log(this.state) }, drink() { // 修改 person 的 state. this.state = 'Drinking.' // 把状态输出到控制台 this.logState() // 返回 this 值。 return this }, eat() { this.state = 'Eating.' this.logState() return this }, exercise() { this.state = 'Exercising.' this.logState() return this }, sleep() { this.state = 'Sleeping.' this.logState() return this }, walk() { this.state = 'Walking.' this.logState() return this }, work() { this.state = 'Working.' this.logState() return this } } // person .drink() // Output: 'Drinking.' .exercise() // Output: 'Exercising.' .eat() // Output: 'Eating.' .work() // Output: 'Working.' .walk() // Output: 'Walking.' .sleep() // Output: 'Sleeping.' // 写在一行上 person.drink().exercise().eat().work().walk().sleep() // Output: // 'Drinking.' // 'Exercising.' // 'Eating.' // 'Working.' // 'Walking.' // 'Sleeping.'
必须使用 this
也意味着没法使用箭头函数建立方法链。由于在箭头函数中,this
没有绑定到对象的实例,而是全局对象 window
的引用。若是返回 this
,那么返回的不是对象自己而是 window
。
另外一个问题是从箭头函数内部访问和修改对象属性。因为 this
是全局对象 window
,因此不能用它来引用对象及其属性。
若是你必定要使用箭头函数,必须想办法绕过这种方法。不用 this
来引用该对象,必须直接经过其名称引用该对象,也就是用对象名替换全部出如今箭头功能内的 this
。
// 建立 person 对象 const person = { name: 'Jack Doer', age: 41, state: null, logState() { console.log(this.state) }, drink: () => { person.state = 'Drinking.' person.logState() return person }, eat: () => { person.state = 'Eating.' person.logState() return person }, exercise: () => { person.state = 'Exercising.' person.logState() return person }, sleep: () => { person.state = 'Sleeping.' person.logState() return person }, walk: () => { person.state = 'Walking.' person.logState() return person }, work: () => { person.state = 'Working.' person.logState() return person } } // person .drink() // Output: 'Drinking.' .exercise() // Output: 'Exercising.' .eat() // Output: 'Eating.' .work() // Output: 'Working.' .walk() // Output: 'Walking.' .sleep() // Output: 'Sleeping.'
这样作的缺点是灵活性很差。若是若是用Object.assign() 和 Object.create()复制对象,全部箭头函数仍然会硬链接到原始对象。
// 建立原始 person 对象 const person = { name: 'Jack Doer', age: 41, state: null, logState() { // 打印整个对象 console.log(this) }, drink: () => { person.state = 'Drinking.' person.logState() return person }, eat: () => { person.state = 'Eating.' person.logState() return person } } // 让 person eat person.eat() // Output: // { // name: 'Jack Doer', // age: 41, // state: 'Eating.', // logState: ƒ, // drink: ƒ, // eat: ƒ // } // 基于person对象建立新对象。 const newPerson = new Object(person) // 修改 "name" 和 "age" 属性 newPerson.name = 'Jackie Holmes' newPerson.age = 33 // 让 newPerson drink。 // 这会打印 Jack Doer 而不是 Jackie Holmes。 newPerson.drink() // Output: // { // name: 'Jack Doer', // age: 41, // state: 'Drinking.', // logState: ƒ, // drink: ƒ, // eat: ƒ // }
可是,若是用 Object() 构造函数,就不会发生上述问题。若是用 new 关键字的和 Object()
构造造函数,将会建立独立的新对象。当你对这个新对象使用某个方法时,它将仅对这个新对象有效,而对原始对象无效。
// 建立原始 person 对象 const person = { name: 'Jack Doer', age: 41, state: null, logState() { // 打印整个对象 console.log(this) }, drink: () => { person.state = 'Drinking.' person.logState() return person }, eat: () => { person.state = 'Eating.' person.logState() return person } } // 让 person eat. person.eat() // Output: // { // name: 'Jack Doer', // age: 41, // state: 'Eating.', // logState: ƒ, // drink: ƒ, // eat: ƒ // } // 基于 person 对象建立新对象 const newPerson = new Object(person) // 修改 "name" 和 "age" 属性 newPerson.name = 'Jackie Holmes' newPerson.age = 33 // 让 newPerson drink. newPerson.drink() // Output: // { // name: 'Jackie Holmes', // age: 33, // state: 'Drinking.', // logState: ƒ, // drink: ƒ, // eat: ƒ // }
若是你必定要用箭头功能,并想要复制对象的话,最好用 Object()
构造函数和 new
关键字建立这些副本。不然只须要用常规函数就够了。
若是你喜欢使用 JavaScript 类,也能够在JavaScript中使用方法连接。除了语法略又不一样外,整个过程和对象是同样的。可是要注意全部可链的方法都必须返回 this
。
// 建立 Person 类 class Person { constructor(name, age) { this.name = name this.age = age this.state = null } logState() { console.log(this.state) } drink() { this.state = 'Drinking.' this.logState() return this } eat() { this.state = 'Eating.' this.logState() return this } sleep() { this.state = 'Sleeping.' this.logState() return this } } // 建立 Person 类的实例 const joe = new Person('Joe', 55) // 使用方法链 joe .drink() // Output: 'Drinking.' .eat() // Output: 'Eating.' .sleep() // Output: 'Sleeping.'
方法链是很是有用的,它能够帮你编写更短、更易读的代码。