方法鏈是一種流行的編程方法,可以幫助你寫出更簡潔易讀的代碼。在本文中我們一起學習 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.'
總結
方法鏈是非常有用的,它可以幫你編寫更短、更易讀的代碼。
![173382ede7319973.gif 173382ede7319973.gif]()
本文首發微信公眾號:前端先鋒
歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章
![歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章 歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章]()
歡迎繼續閱讀本專欄其它高贊文章:
查看原文
方法鏈是一種流行的編程方法,可以幫助你寫出更簡潔易讀的代碼。在本文中我們一起學習 JavaScript 中的方法鏈是什么,以及它是怎樣工作的。另外我們還會探討如何使用方法鏈接來提高代碼的質量和可讀性。