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