箭头函数的this指向谁_高阶函数
NodeJS 系列文章,本篇是第一篇,首先,預(yù)計將后續(xù)高頻使用邏輯串一遍,依次是高階函數(shù),promise以及事件機(jī)制。本篇主要是高階函數(shù)。
call、bind、apply
- call、apply 都是改變 this 指向,區(qū)別是接受參數(shù)的方式不一樣,call 一個一個傳參數(shù),apply 接收一個數(shù)組。
- bind 會創(chuàng)建一個新函數(shù),稱為綁定函數(shù),當(dāng)調(diào)用這個綁定函數(shù)時,綁定函數(shù)會以創(chuàng)建它時傳入 bind 方第一個參數(shù)作為 this,傳入 bind 的第二個以及以后的參數(shù)加上綁定函數(shù)運(yùn)行時本身的參數(shù)按照順序作為原函數(shù)的參數(shù)來調(diào)用原函數(shù)。
- 當(dāng)你希望改變上下文環(huán)境之后并非立即執(zhí)行,而是回調(diào)執(zhí)行的時候,使用 bind() 方法,bind 是返回的函數(shù)。
- apply、call 則會立即執(zhí)行函數(shù)。
this 指向
this 指向問題,誰調(diào)用,我就指向誰。
全局上下文
非嚴(yán)格模式和嚴(yán)格模式中this都是指向頂層對象(瀏覽器中是window)。
console.log(this === window); // true'use strict'console.log(this === window); // truethis.name = 'Hiraku';console.log(this.name); // Hiraku函數(shù)上下文
普通函數(shù)調(diào)用模式
var name = '驍媽';var func = function() {console.log(this === window, this.name); // true 驍媽(瀏覽器)}; func();在 ES5 中,全局變量是掛載在頂層對象(瀏覽器是 window )中。
const name1 = 'mahongqin';const func1 = function() {console.log(this === window, this.name1); // true undefined};const func2 = () => {console.log(this === window, this.name1); // true undefined};func1();func2();let 沒有給頂層對象中(瀏覽器是 window )添加屬性,window.name1 和 window.func1,window.func2 都是 undefined。
嚴(yán)格模式中,普通函數(shù)中的 this 則表現(xiàn)不同,表現(xiàn)為 undefined。
'use strict';var name2 = '驍媽';var func3 = function() {console.log(this === window); // false}; func3();call,apply 作用之一就是改變函數(shù)的 this 指向為第一個參數(shù)。 如果第一個參數(shù)是 undefined 或者 null,非嚴(yán)格模式下,是指向 window。嚴(yán)格模式下,就是指向第一個參數(shù)。
對象中的函數(shù)(方法)調(diào)用模式
var name3 = 'Hiraku_Ma';var func4 = function() {console.log(this, this.name);}var obj = {name: 'mhq',func: func4,attr: {name: 'mhq-gs',func: func4,}}obj.func(); // Object{} obj 'mhq'obj.attr.func(); // Object{} obj.attr 'mhq-gs'// 用call類比則為:obj.func.call(obj); // 'mhq'// 用call類比則為:obj.attr.func.call(obj.attr); // 'mhq-gs'函數(shù)賦值之后變成普通函數(shù)。
構(gòu)造函數(shù)調(diào)用模式
function Example(type){this.type = type;console.log(this);// return this; } var exp = new Example('mhq');使用 new 操作符調(diào)用函數(shù),會自動執(zhí)行以下步驟:
- 創(chuàng)建了一個全新的對象。
- 這個對象會被執(zhí)行[[Prototype]](也就是proto)鏈接。
- 生成的新對象會綁定到函數(shù)調(diào)用的 this。
- 通過 new 創(chuàng)建的每個對象將最終被[[Prototype]]鏈接到這個函數(shù)的 prototype 對象上。
- 如果函數(shù)沒有返回對象類型 Object( 包含 Functoin, Array, Date, RegExg, Error ),那么 new 表達(dá)式中的函數(shù)調(diào)用會自動返回這個新的對象。
new 操作符調(diào)用時,this 指向生成的新對象。 new 調(diào)用時的返回值,如果沒有顯式返回對象或者函數(shù),才是返回生成的新對象。
function Example(type){this.type = type;console.log(this);return {};}var exp = new Example('mhq'); // { name: mhq }console.log(exp);// 如果返回函數(shù) f ,則 exp 是函數(shù) f,如果是對象 {} ,則 exp 是對象 {}原型鏈中的調(diào)用模式
function Example(type){this.type = type;console.log(this);}Example.prototype.func = function() {console.log(this.type);};var exp = new Example('mhq');exp.func();箭頭函數(shù)調(diào)用模式
var name = 'mhq';var student = {name: 'hiraku',func: function () {var arrowFunc = () => {console.log(this.name);}arrowFunc();},arrowFunc2: () => {console.log(this.name);}}student.func(); // 'hiraku'student.arrowFunc2(); // 'mhq'- 語法更加簡潔、清晰
- 箭頭函數(shù)不會創(chuàng)建自己的 this
- 箭頭函數(shù)的 this 指向定義時所在的外層第一個普通函數(shù),跟使用位置沒有關(guān)系
- 被繼承的普通函數(shù)的 this 指向改變,箭頭函數(shù)的 this 指向會跟著改變
- 箭頭函數(shù)外層沒有普通函數(shù),嚴(yán)格模式和非嚴(yán)格模式下它的 this 都會指向 window (全局對象)
- call()、apply()、bind() 無法改變箭頭函數(shù)中 this 的指向
- 箭頭函數(shù)不能作為構(gòu)造函數(shù)使用
- 箭頭函數(shù)沒有自己的 arguments
- 箭頭函數(shù)的 this 指向全局,使用 arguments 會報未聲明的錯誤
- 箭頭函數(shù)的 this 指向普通函數(shù)時,它的 arguments 繼承于該普通函數(shù)
- rest參數(shù)(...擴(kuò)展符)取參數(shù)
- 箭頭函數(shù)沒有原型 prototype
- 箭頭函數(shù)不能用作 Generator 函數(shù),不能使用 yeild 關(guān)鍵字
DOM 事件處理函數(shù)調(diào)用
- onclick 和 addEventerListener 是指向綁定事件的元素。
- 一些瀏覽器,比如 IE6~IE8 下使用 attachEvent,this 指向是 window。
高階函數(shù)(Higher-order function)
什么是高階函數(shù)?高階函數(shù)至少滿足兩個條件:接受一個或多個函數(shù)作為輸入,輸出一個函數(shù)。也可以這樣理解,一個函數(shù)的參數(shù)是一個函數(shù)(回調(diào)函數(shù));一個函數(shù)返回一個函數(shù)(函數(shù)柯里化)。
我們寫代碼時不希望破壞原有邏輯,所以將原有邏輯包上一個函數(shù),這個函數(shù)內(nèi)部實(shí)現(xiàn)自己的邏輯,即切片編程。
const originFunc = (...args) => {console.log('原函數(shù)', args);};// 希望在調(diào)用 originFunc 之前做一些事情,使用 Function.prototype 給每個函數(shù)擴(kuò)展一些功能Function.prototype.before = function(cb) {return (...args) => {cb();this(...args);};};let newFunc = originFunc.before(() => {console.log('before originFunc');});newFunc('a', 'b', 'c');一個異步并發(fā)問題
我同時發(fā)送多個請求,希望拿到最終的結(jié)果
function after(time, fn) {return () => {if (--time === 0) {fn();}};}const exaFun = after(3, () => {console.log('執(zhí)行');});exaFun();exaFun();exaFun();示例:
const fs = require('fs');const path = require('path');function run(times, fn) {const renderObj = {};return (key, value) => {renderObj[key] = value;console.log(key, value);if(--times === 0) fn(renderObj);};}let out = run(2, (result) => {console.log(result);});fs.readFile(path.resolve(__dirname, '1.txt'), 'utf8', (_, data) => {console.log(data, 'ddd')out('1', data);});fs.readFile(path.resolve(__dirname, '2.txt'), 'utf8', (_, data) => {out('2', data);});函數(shù)柯里化
柯里化,即 Currying,為了給多參數(shù)函數(shù)提供了一個遞歸降解的方式,把提供多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)的函數(shù),并且返回余下參數(shù)而且返回結(jié)果的新函數(shù)。
const curring = (fn, arr = []) => {// 長度值的是參數(shù)個數(shù)let len = fn.length;return (...arg) => {arr = [...arr, ...arg];if (arr.length < len) return curring(fn, arr);return fn(...arr);};};const add = (a, b, c, d, e, f, g, h) => {return a + b + c + d + e + f + g + h;}console.log(curring1(add, [1, 2])(3, 4)(5)(6)(7, 8)) // 15函數(shù)柯里化使用場景: 參數(shù)復(fù)用 延遲執(zhí)行 預(yù)加載 動態(tài)創(chuàng)建函數(shù)
發(fā)布訂閱、觀察者模式
觀察者模式 定義了對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都將得到通知,并自動更新 屬于行為型模式,行為型模式關(guān)注的是對象之間的通訊 * 觀察者和被觀察者之間的通訊
const fs = require('fs');const e = {arr: [],on(fn) {this.arr.push(fn);},emit() {this.arr.forEach(fn => fn());}};e.on(() => {console.log('讀取到了數(shù)據(jù)');})const renderObj = {};e.on(() => {if (Object.keys(renderObj).length === 2) {console.log('都讀取完畢了');}});fs.readFile('./name.txt', 'utf8', (_, data) => {renderObj['name'] = data;e.emit();});fs.readFile('./age.txt', 'utf8', (_, data) => {renderObj['age'] = data;e.emit();});發(fā)布訂閱 就是事先存儲好,稍后發(fā)布的時候讓事先訂閱的事執(zhí)行 發(fā)布者并不會直接通知訂閱者,換句話說,發(fā)布者和訂閱者,彼此互不相識 * 在發(fā)布者和訂閱者之間存在第三個組件,稱為調(diào)度中心或事件通道,它維持著發(fā)布者和訂閱者之間的聯(lián)系,過濾所有發(fā)布者傳入的消息并相應(yīng)地分發(fā)它們給訂閱者
class Subject { // 被觀察者constructor(name) {this.name = name;this.arr = [];this.state = '我很開心';}attach(observer) { // 注冊觀察者 基于發(fā)布訂閱this.arr.push(observer);}setState(newState) {this.state = newState;this.arr.forEach(o => o.update(this)); // 通知所有觀察者 我的狀態(tài)發(fā)生了變化}}class Observer { // 觀察者constructor(name) {this.name = name;}update(s) {console.log(s.name + '當(dāng)前狀態(tài)是' + s.state + '對:' + this.name);}}let s = new Subject('小寶寶');let o1 = new Observer('我');let o2 = new Observer('我媳婦');s.attach(o1);s.attach(o2);console.log(s.state);s.setState('不開心了');本篇文章涉及到的高階函數(shù)使用有函數(shù)柯里化、發(fā)布訂閱|觀察者模式等。下篇將分享 Promise 源碼,手寫 Promise。
總結(jié)
以上是生活随笔為你收集整理的箭头函数的this指向谁_高阶函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: string类的erase函数属于stl
- 下一篇: git上传分支的原理_几张图让你彻底弄懂