JavaScript 中的方法链是什么?它是如何工作的?以及如何使用它?
方法链是一种流行的编程技巧,可以帮助你编写更简洁、更易读的代码。在本教程中,你将学习 JavaScript 中的方法链是什么以及它的工作原理。你还将学习如何使用方法链来提高代码的质量和可读性。
JavaScript 方法链简介
你用过像 jQuery 这样的库吗?如果是,那你可能见过类似的情况。这里指的是在同一行中,两个或多个方法以层叠的方式依次调用。如今,这种做法在纯 JavaScript 中也很常见。你可以在数组、字符串和 Promise 中看到它。
在所有这些情况下,流程都相同。首先,引用你要操作的对象。其次,使用所需的方法。但是,不是单独使用这些方法,而是按顺序使用。你实际上是将它们串联起来。让我们来看一些例子来说明这一点。
方法链示例
假设你想操作一个字符串。有两种方法可以实现。第一种方法不使用方法链。这需要你分别对字符串调用每个方法,并且每次都需要引用该字符串。第二种方法是使用方法链。
在这种情况下,你需要依次使用所有需要的字符串方法。你可以用一行代码完成,也可以用多行代码,这取决于你的需求。此外,你只需要在代码开头引用一次字符串。结果相同,但代码量却截然不同。
// Method chaining with string.
let myStr = ' - Hello-world. '
// Without method chaining:
myStr = myStr.toLowerCase()
myStr = myStr.replace(/-/g, ' ')
myStr = myStr.trim()
// With method chaining:
myStr = myStr.toLowerCase().replace(/-/g, ' ').trim()
// Alternative with method chaining and multiple lines:
myStr = myStr
.toLowerCase()
.replace(/-/g, ' ')
.trim()
// Log the value of "myStr" variable.
console.log(myStr)
// Output:
// 'hello world.'
如果你有一个数组,并且想用几个数组方法来操作它,情况也是如此。你也可以选择这两种方法:一种是不使用方法链,另一种是使用方法链,代码更简洁。就像处理字符串一样,结果是一样的,只是代码量不同。
// Method chaining with array.
let myArray = [1, 7, 3, null, 8, null, 0, null, '20', 15]
// Without method chaining:
myArray = myArray.filter(el => typeof el === 'number' && isFinite(el))
myArray = myArray.sort((x, y) => x - y)
// With method chaining:
myArray = myArray.filter(el => typeof el === 'number' && isFinite(el)).sort((x, y) => x - y)
// Alternative with method chaining and multiple lines:
myArray = myArray
.filter(el => typeof el === 'number' && isFinite(el))
.sort((x, y) => x - y)
// Log the value of "myArray" variable.
console.log(myArray)
// Output:
// [ 0, 1, 3, 7, 8 ]
Promise 是一个很好的例子,因为它们几乎必须通过方法链才能正常工作。首先,你需要创建一个 Promise。然后,你需要添加相应的处理函数。这些处理函数对于处理 Promise 解析后获取的值至关重要。当然,除非你使用async 函数和await关键字。
// Create a Promise
const myPromise = new Promise((resolve, reject) => {
// Create a fake delay
setTimeout(function() {
// Resolve the promise with a simple message
resolve('Sorry, no data.')
}, 1000)
})
// With method chaining:
myPromise.then((data) => console.log(data)).catch(err => console.log(error))
// Alternative with method chaining and multiple lines:
myPromise
.then((data) => console.log(data))
.catch(err => console.log(error))
// Output:
// 'Sorry, no data.'
JavaScript 中的方法链是如何运作的
你已经了解方法链的运作方式。更重要的问题是,它是如何工作的?答案很简单。它之所以有效,是因为 `this`关键字this。没错,我们说的就是那个臭名昭著的`this`关键字。关于 ` this`,this有很多东西值得学习。为了保持本教程的简洁,我们先不深入探讨,而是用最简单的方式来讲解。
假设你有一个对象。如果你this在该对象内部使用 `get()` 方法,它指向的仍然是该对象本身。如果你随后创建该对象的一个实例(或副本),`get()` 方法this指向的仍然是该实例(或副本)。当你使用字符串或数组方法时,你实际上是在操作一个对象。
const myObj = {
name: 'Stuart',
age: 65,
sayHi() {
// This here refers to 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: ƒ
// }
如果是字符串,你处理的是基本数据类型。然而,你使用的方法(例如 `get()`)toLowerCase()存在于对象的原型上String。仅仅在某个对象上添加一个新方法并不足以实现链式调用。还有一个关键要素,那就是对象本身this。
要使链式调用生效,方法必须返回它所操作的对象。它必须返回一个对象this。可以把它想象成接力棒。场上有一些跑者,位置各不相同。但是,他们不能同时跑。一次只能有一个人跑。当前正在跑的跑者完成他的部分后,必须将接力棒传递给下一个跑者。
只有当这种情况发生时,也就是下一位跑者接到接力棒时,他才能跑自己的部分。在我们的例子中,每个方法都代表一位跑者。接力棒会被返回this,也就是该方法正在操作的对象。如果没有接力棒,就没有this返回值,下一位跑者就无法接力,链式调用也就无法工作。
如何在 JavaScript 中实现方法链
以上是理论部分。现在,让我们来实践一下。要实现链式调用,你需要三样东西。首先,你需要一个对象。其次,这个对象需要一些你可以稍后调用的方法。第三,这些方法必须返回对象本身。this如果你想让它们可以链式调用,它们就必须返回对象本身。
让我们创建一个简单的对象来比喻人。这个人将具有几个属性:状态name、age兴趣和state兴趣。状态state将指定这个人当前处于什么状态。要改变这个状态,将有几个方法:改变walk()、切换、sleep()改变和改变兴趣。eat()drink()work()exercise()
由于我们希望所有这些方法都能链式调用,因此它们最终都必须返回结果this。此外,还有一个实用方法。该方法会将当前状态记录到控制台。当您使用某个方法更改角色状态时,它也会调用此方法,以便您可以在控制台中看到新的状态。
// Create person object.
const person = {
name: 'Jack Doer',
age: 41,
state: null,
logState() {
console.log(this.state)
},
drink() {
// Change person's state.
this.state = 'Drinking.'
// Log current person's state.
this.logState()
// Return this to make the method chainable.
return this
},
eat() {
// Change person's state.
this.state = 'Eating.'
// Log current person's state.
this.logState()
// Return this to make the method chainable.
return this
},
exercise() {
// Change person's state.
this.state = 'Exercising.'
// Log current person's state.
this.logState()
// Return this to make the method chainable.
return this
},
sleep() {
// Change person's state.
this.state = 'Sleeping.'
// Log current person's state.
this.logState()
// Return this to make the method chainable.
return this
},
walk() {
// Change person's state.
this.state = 'Walking.'
// Log current person's state.
this.logState()
// Return this to make the method chainable.
return this
},
work() {
// Change person's state.
this.state = 'Working.'
// Log current person's state.
this.logState()
// Return this to make the method chainable.
return this
}
}
// Let's have some fun.
person
.drink() // Output: 'Drinking.'
.exercise() // Output: 'Exercising.'
.eat() // Output: 'Eating.'
.work() // Output: 'Working.'
.walk() // Output: 'Walking.'
.sleep() // Output: 'Sleeping.'
// Alternative on a single line:
person.drink().exercise().eat().work().walk().sleep()
// Output:
// 'Drinking.'
// 'Exercising.'
// 'Eating.'
// 'Working.'
// 'Walking.'
// 'Sleeping.'
方法、链式调用、this 函数和箭头函数
使用箭头函数this也意味着一件事:你不能用箭头函数创建链式方法。原因是,在箭头函数中,`this`this并不绑定到对象实例,而是this指向全局对象。window如果你尝试返回`this`,this它返回的是全局对象window,而不是对象本身。
另一个问题是如何在箭头函数内部访问和修改对象属性。由于 `wuck`this是全局对象,window你不能直接引用该对象及其属性。你实际上是在尝试引用 `wuck` 本身window及其属性。
如果你坚持使用箭头函数,有一种方法可以绕过这个问题。this你不能再使用 `{{` 来引用对象,而应该直接使用对象名来引用。你需要将this箭头函数内部所有出现的 `{{` 替换为对象名。
// Create person object.
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
}
}
// Let's have some fun.
person
.drink() // Output: 'Drinking.'
.exercise() // Output: 'Exercising.'
.eat() // Output: 'Eating.'
.work() // Output: 'Working.'
.walk() // Output: 'Walking.'
.sleep() // Output: 'Sleeping.'
这样做的一个潜在缺点是你会失去所有灵活性。如果你复制对象,所有箭头函数仍然会硬编码到原始对象上。如果你同时使用 ` Object.assign()`和`Object.create()`创建副本,就会发生这种情况。
// Create original person object.
const person = {
name: 'Jack Doer',
age: 41,
state: null,
logState() {
// Log the whole object.
console.log(this)
},
drink: () => {
person.state = 'Drinking.'
person.logState()
return person
},
eat: () => {
person.state = 'Eating.'
person.logState()
return person
}
}
// Let person eat.
person.eat()
// Output:
// {
// name: 'Jack Doer',
// age: 41,
// state: 'Eating.',
// logState: ƒ,
// drink: ƒ,
// eat: ƒ
// }
// Create new object based on person object.
const newPerson = new Object(person)
// Change the "name" and "age" properties.
newPerson.name = 'Jackie Holmes'
newPerson.age = 33
// Let newPerson drink.
// This will print Jack Doer not Jackie Holmes.
newPerson.drink()
// Output:
// {
// name: 'Jack Doer',
// age: 41,
// state: 'Drinking.',
// logState: ƒ,
// drink: ƒ,
// eat: ƒ
// }
但是,如果您使用Object()构造函数,就不会出现上述问题。如果您使用Object()带有new关键字的构造函数,您将创建一个独立的副本作为新对象。当您对该副本使用某些方法时,该方法只会影响该副本,而不会影响原始对象。
// Create original person object.
const person = {
name: 'Jack Doer',
age: 41,
state: null,
logState() {
// Log the whole object.
console.log(this)
},
drink: () => {
person.state = 'Drinking.'
person.logState()
return person
},
eat: () => {
person.state = 'Eating.'
person.logState()
return person
}
}
// Let person eat.
person.eat()
// Output:
// {
// name: 'Jack Doer',
// age: 41,
// state: 'Eating.',
// logState: ƒ,
// drink: ƒ,
// eat: ƒ
// }
// Create new object based on person object.
const newPerson = new Object(person)
// Change the "name" and "age" properties.
newPerson.name = 'Jackie Holmes'
newPerson.age = 33
// Let newPerson drink.
newPerson.drink()
// Output:
// {
// name: 'Jackie Holmes',
// age: 33,
// state: 'Drinking.',
// logState: ƒ,
// drink: ƒ,
// eat: ƒ
// }
所以,如果你坚持要使用箭头函数,并且想要复制对象?最好使用Object()构造函数和new关键字来创建这些副本。否则,省去这些麻烦,直接使用普通函数就好。
方法链和类
喜欢JavaScript 类吗?如果是,那我有个好消息要告诉你。即使你更喜欢使用类,也可以在 JavaScript 中使用方法链。过程与对象类似,只是语法略有不同。重要的是,每个可以链式调用的方法都必须返回一个值this。
// Create Person class.
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
}
}
// Create instance of Person class.
const joe = new Person('Joe', 55)
// Use method chaining.
joe
.drink() // Output: 'Drinking.'
.eat() // Output: 'Eating.'
.sleep() // Output: 'Sleeping.'
结论:JavaScript 中的方法链是什么,它是如何工作的,以及如何使用它
方法链是一种简单却非常实用的方法。它可以帮助你编写更简洁、更易读的代码。希望这篇教程能帮助你理解 JavaScript 中的方法链是什么以及它的工作原理。现在,就看你如何将所学的方法链知识运用到你的代码中了。
文章来源:https://dev.to/alexdevero/what-method-chaining-in-javascript-is-how-it-works-and-how-to-use-it-53dd