共计 6363 个字符,预计需要花费 16 分钟才能阅读完成。
办法链是一种风行的编程办法,能够帮忙你写出更简洁易读的代码。在本文中咱们一起学习 JavaScript 中的办法链是什么,以及它是怎么工作的。另外咱们还会探讨如何应用办法链接来进步代码的品质和可读性。
JavaScript 中办法链
你肯定已经用过 jQuery 之类的库,可能看到过相似的货色。在进行级联时次要有两种办法:一种是一个接一个的执行办法,另一种是在同一行上。在纯 JavaScript 中这种做法也很广泛。你能够在数组、字符串和 promise 看到它。
在这些状况下所有的过程都是雷同的。首先援用要应用的对象。而后依据须要应用多种办法。但不是独自应用这些办法,而要一个接一个地应用。基本上是把它们链接在一起。先看一些例子。
办法链的例子
在解决字符串时有两种办法。第一个种不必办法链,这要求必须在字符串上别离应用每个办法,这样必须每次都援用这个字符串。
第二种形式是用办法链。这时能够用所有想要的字符串办法。写出的代码也能够是单行或多行,这取决于你的习惯。而且只须要援用一次字符串。只管后果雷同,然而代码量却有很大的差别。
// 在字符串上应用办法链的例子 | |
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,而后增加适当的处理函数。
// 创立 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
将会援用这个实例或正本。当你应用某些字符串或数组办法时,实际上是在用一个对象。
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
。就像接力赛跑时的接力棒一样。
在 JavaScript 中实现办法链
为了使办法链无效,必须满足三个条件:首先,须要一些对象。其次,该对象须要一些当前能够调用的办法。第三,这些办法必须返回对象自身,它们必须返回 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
也意味着无奈应用箭头函数创立办法链。因为在箭头函数中,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.' |
总结
办法链是十分有用的,它能够帮你编写更短、更易读的代码。
本文首发微信公众号:前端先锋
欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章
欢送持续浏览本专栏其它高赞文章:
- 深刻了解 Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13 个帮你进步开发效率的古代 CSS 框架
- 疾速上手 BootstrapVue
- JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
- WebSocket 实战:在 Node 和 React 之间进行实时通信
- 对于 Git 的 20 个面试题
- 深刻解析 Node.js 的 console.log
- Node.js 到底是什么?
- 30 分钟用 Node.js 构建一个 API 服务器
- Javascript 的对象拷贝
- 程序员 30 岁前月薪达不到 30K,该何去何从
- 14 个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩大插件
- Node.js 多线程齐全指南
- 把 HTML 转成 PDF 的 4 个计划及实现
- 更多文章 …