做者:Shadeed
譯者:前端小智
來源:dmitripavlutin
有夢想,有乾貨,微信搜索 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。javascript
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及個人系列文章。前端
定義並調用一個常規函數:java
function greet(who) { return `Hello, ${who}!`; } greet('World'); // => 'Hello, World!'
function
關鍵字後跟其名稱,參數和主體:function greet(who){...}
進行常規的函數定義。git
greet('World')
是常規的函數調用。函數greet('World')
接受參數中的數據。github
若是who
是一個對象的屬性呢?要方便訪問對象的屬性,咱們能夠將函數附加到該對象,換句話說,就是建立一個方法。面試
咱們將greet()
做爲對象world
的一種方法:windows
const world = { who: 'World', greet() { return `Hello, ${this.who}!`; }} world.greet(); // => 'Hello, World!'
greet() { ... }
如今是屬於world
對象的方法, world.greet()
是方法調用。瀏覽器
在greet()
方法內部,this
指向該方法所屬的對象—world
,這就是爲啥能夠this.who
訪問 word
屬性的緣由。微信
注意,this
也稱爲上下文。app
在上一個示例中,咱們使用this
來訪問該方法所屬的對象,可是 JS 沒有強制讓方法使用 this
。
所以,能夠將對象用做方法的命名空間:
const namespace = { greet(who) { return `Hello, ${who}!`; }, farewell(who) { return `Good bye, ${who}!`; } } namespace.greet('World'); // => 'Hello, World!' namespace.farewell('World'); // => 'Good bye, World!'
namespace
是一個包含2個方法的對象:namespace.greet()
和namespace.farewell()
。
如前所述,咱們能夠直接在對象字面量中定義方法
const world = { who: 'World', greet() { return `Hello, ${this.who}!`; }}; world.greet(); // => 'Hello, World!'
greet() { .... }
是在對象定義的方法,這種定義類型稱爲速記方法定義(從ES2015開始可用)。方法定義的語法也更長:
const world = { who: 'World', greet: function() { return `Hello, ${this.who}!`; } } world.greet(); // => 'Hello, World!'
greet: function() { ... }
是一個方法定義,注意附加的冒號和function
關鍵字。
方法只是一個函數,它做爲屬性存儲在對象上。 所以,咱們能夠向對象動態添加方法:
const world = { who: 'World', greet() { return `Hello, ${this.who}!`; } }; // A a new property holding a function world.farewell = function () { return `Good bye, ${this.who}!`; } world.farewell(); // => 'Good bye, World!'
在 JavaScript 中,類別語法定義了一個類別,該類別將用做其實例的模板。
類也能夠有方法:
class Greeter { constructor(who) { this.who = who; } greet() { console.log(this === myGreeter); // logs true return `Hello, ${this.who}!`; }} const myGreeter = new Greeter('World'); myGreeter.greet(); // => 'Hello, World!'
greet() { ... }
是在類內部定義的方法。
每次咱們使用new
操做符(例如myGreeter = new Greeter('World')
)建立一個類的實例時,均可以在建立的實例上調用方法。
myGreeter.greet()
是如何在實例上調用方法greet()
的方法。 重要的是方法內部的this
等於實例自己:this
等於greet() { ... }
方法內部的 myGreeter
。
JavaScript 特別有趣的是,在對象或類上定義方法只能算完成工做的一半。爲了維護方法的上下文,咱們必須確保將方法做爲方法調用。
咱們來看看爲何它很重要。
回憶一下有greet()
方法的world
對象。咱們測試一下greet()
做爲一個方法和一個常規函數調用時,this
值是什麼:
const world = { who: 'World', greet() { console.log(this === world); return `Hello, ${this.who}!`; } }; // 方法調用 world.greet(); // logs true const greetFunc = world.greet; // 常規函數調用 greetFunc(); // => logs false
world.greet()
是一個方法調用。對象world
,後面是一個點.
,最後是使方法調用的方法自己。
greetFunc
與world.greet
是同一個函數。但看成爲常規函數greetFunc()
調用時,這個在greet()
中的並不等於world
對象,而是全局對象(在瀏覽器中是window
)
咱們將諸如greetFunc = world.greet
之類的表達式命名爲將方法與其對象分離的方法。 調用分離的方法greetFunc()
時,this
等於全局對象。
將方法與其對象分離能夠採用不一樣的形式:
// 方法分離, this 丟失了! const myMethodFunc = myObject.myMethod; // 方法分離, this 丟失了! setTimeout(myObject.myMethod, 1000); // 方法分離, this 丟失了! myButton.addEventListener('click', myObject.myMethod) // 方法分離, this 丟失了! <button onClick={myObject.myMethod}>My React Button</button>
爲了不丟失方法的上下文,請確保使用方法調用world.greet()
或手動將方法綁定到對象greetFunc = world.greet.bind(this)
。
如上一節所述,常規函數調用已將this
解析爲全局對象。 常規函數是否能夠經過方法自定義 this
值?
歡迎使用如下間接函數調用:
myFunc.call(thisArg, arg1, arg2, ..., argN); myFunc.apply(thisArg, [arg1, arg2, ..., argN]);
函數對象上可用的方法。
myFunc.call(thisArg)
和 myFunc.apply(thisArg)
的第一個參數是間接調用的上下文(this
值)。 換句話說,咱們能夠手動指定函數內部 this
的值。
例如,讓咱們將greet()
定義爲一個常規函數,以及一個具有who
屬性的對象alien
:
function greet() { return `Hello, ${this.who}!`; } const aliens = { who: 'Aliens' }; greet.call(aliens); // => 'Hello, Aliens!' greet.apply(aliens); // => 'Hello, Aliens!'
greet.call(aliens)
和greet.apply(aliens)
都是間接的方法調用。這個在greet()
函數中的值等於aliens
對象。
最後,還有一種在對象上使函數做爲方法調用的第三種方法。 咱們能夠將函數綁定爲具備特定上下文。
可使用特殊方法建立綁定函數
const myBoundFunc = myFunc.bind(thisArg, arg1, arg2, ..., argN);
myFunc.bind(thisArg)
的第一個參數是函數要綁定到的上下文。
例如,讓咱們重用greet()
並將其綁定到aliens
上下文
function greet() { return `Hello, ${this.who}!`; } const aliens = { who: 'Aliens' }; const greetAliens = greet.bind(aliens); greetAliens(); // => 'Hello, Aliens!'
調用 greet.bind(aliens)
會建立一個新函數,該函數將 this
綁定到aliens
對象。
一樣,使用綁定函數能夠模擬方法調用。當調用綁定函數greetAliens()
時,this
等於該函數中的 aliens
。
不推薦使用箭頭函數做爲方法,緣由以下。
咱們將greet()
方法定義爲一個箭頭函數:
const world = { who: 'World', greet: () => { return `Hello, ${this.who}!`; } }; world.greet(); // => 'Hello, undefined!'
不幸的是,world.greet()
返回'Hello, undefined!
而不是咱們期待的'Hello, World!'
。
問題是箭頭函數內部的this
等於外部做用域的this
。 可是,此時,咱們想要的this
是world
對象。
上述箭頭功能內部 this
等於全局對象:window
。 'Hello, ${this.who}!'
結果是 Hello, ${windows.who}!
,最後是 'Hello, undefined!'
。
我喜歡箭頭功能, 可是它們不能用做方法。
該方法是一個屬於對象的函數。方法的上下文(this
)等於該方法所屬的對象。
還能夠在類上定義方法。這個類的方法內部等於實例。 JS 特有的一點是,僅僅定義一個方法是不夠的。咱們還須要確保使用方法調用。一般,方法調用具備如下語法
// Method invocation myObject.myMethod('Arg 1', 'Arg 2');
有趣的是,在 JS 中,咱們能夠定義一個常規函數,但不屬於一個對象,而後做爲一個任意對象的方法調用該函數。可使用間接函數調用或將函數綁定到特定上下文來實現這一點
// Indirect function invocation myRegularFunc.call(myObject, 'Arg 1', 'Arg 2'); myRegularFunc.apply(myObject, 'Arg 1', 'Arg 2'); // Bound function const myBoundFunc = myRegularFunc.bind(myObject); myBoundFunc('Arg 1', 'Arg 2');
我是小智,我要去刷碗了,咱們下期見~
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
原文:https://dmitripavlutin.com/ja...
有夢想,有乾貨,微信搜索 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。
本文 GitHub https://github.com/qq44924588... 已收錄,有一線大廠面試完整考點、資料以及個人系列文章。