## apply和call講解
為什么這兩個放在一起,使用過這兩個方法的都知道,他們在使用上,傳參方式不一樣,其他都一樣的;但是bind就要復雜些了。
### 通俗話**原理**
* 就是把A對象作用域(this)傳遞到B(函數)對象作用域中執行,B函數作用域中執行取決于在哪個執行環境(this)中。
* 埋個伏筆,先來看一下下面繼承,來對應一下上面這個描述,FB中this即是objA(注意:當FB原來this即是window對象),說明FB在經過objA.FB = FB時,FB環境已經改變。這個等閱讀完回過頭看,就會發現和call實現原理好像挺相似的。
``` js
function FB(age){
console.log('FB:this=',this) // {name: 'vvmily', FB: ?}
this.age = age
console.log(this.name,this.age) // vvmily 18
}
var objA = {
name: 'vvmily'
}
objA.FB = FB // objA.prototype.FB = FB
objA.FB(18)
```
* 下面通過call方式,在表面可以看出objA.FB = FB與FB.call(this)作用一樣,把FB的執行作用域環境改變為FA的作用域(this)環境。
``` js
function FB(age){
this.age = age
console.log(this.name, this.age) // vvmily 19
}
function FA(){
this.name = 'vvmily'
FB.call(this,19)
}
const a = new FA()
```
### 總結
有了總結,知道call做了什么事情,才更好的實現,不是嗎。
1. 將當前執行作用域賦予另一個函數作用域 FB(this) => FA(this)
2. 另一個函數作用域執行結果返回(掛在)當前作用域
### 手寫實現
``` js
function MyCall(context, ...args){
context = context || window // MyCall所在執行作用域的this,并非MyCall自己的this
args = args || []
context.B_Fn = this // 保存當前MyCall執行作用域
const result = context.B_Fn(...args) // 執行當前作用域,并拿到結果,并且結果直接掛在執行的作用域中,而非MyCall作用域哦
delete context.B_Fn // 防止污染
return result
}
```
檢驗一下是否正確,當然是成功了
``` js
Function.prototype.MyCall = MyCall
function FB(age){
this.age = age
console.log(this.name, this.age) // vvmily 19
}
function FA(){
this.name = 'vvmily'
FB.MyCall(this,19)
}
const a = new FA()
```
### apply手動實現
請仔細對比,基本args的區別,就是參數,這里FB.MyCall(this,[19])第二個參數為數組即可
``` js
function MyApply(context, args){
context = context || window // MyCall所在執行作用域的this,并非MyCall自己的this
args = args || []
context.B_Fn = this // 保存當前MyCall執行作用域
const result = context.B_Fn(...args) // 執行當前作用域,并拿到結果,并且結果直接掛在執行的作用域中,而非MyCall作用域哦
delete context.B_Fn // 防止污染
return result
}
```
## bind講解
通俗講:將某個(對象,如objB)作用域作為(將要返回的)新函數體作為執行環境。
先來看看下面的代碼,感受一下上面這句話。
``` js
const objB = {
name: 'vvmily',
getName: function(){
// console.log("getName is name:",this.name)
return this.name
}
}
const FA = objB.getName
console.log(FA()) // undefined,為什么呢,FA執行作用域是window,即getName函數作用域this===window,而在window.name可是沒有值的。
console.log(FA.bind(objB)()) // vvmily ,將objB作用域作為返回新函數體的執行環境
console.log(objB.getName()) // vvmily ,此時執行作用域是objB對象內,getName執行環境是objB作用域內,即this===objB。
FA.bind(obj)
```
## 手動實現
目的:最終肯定返回一個函數體(如:myBind)
### 用FA.bind(objB)()分析:
1. 當FA.bind(objB)時,需要干點什么呢?肯定要保存objB作用域的數據
2. 該函數體需要包含哪些數據呢?包含FA.bind(objB)執行時的**執行作用域**和**入參**;
3. 而最終返回的函數體,執行時,還能訪問到上次FA.bind(objB)函數中的變量,則需要用到**閉包**了;
4. 接下來通過函數體如何訪問objB信息?這里需要借助一個中間構造函數Fn了,通過Fn原型繼承FA.bind(objB)的原型,即Fn.prototype = self.prototype;
5. 返回函數體原型繼承Fn實例,即myBind.prototype = new Fn()。
注意:FA.bind(objB)就是objB的作用域,訪問的就是objB數據。
``` js
function MyBind(context,...args){
let self = this; // 2
args = args || []; // 2
let Fn = function(){}; // 4
let myBind = function(){
// ...
}
Fn.prototype = self.prototype // 4
myBind.prototype = new Fn() // 5
return myBind
}
```
### 實現myBind函數體
* 這里需要借助前面實現的call或者apply方法
``` js
// ...
let myBind = function(...params){
const allArgs = [...args, ...params] // 參數合并
return self.call(context, ...allArgs)
}
// ...
```
* 完整代碼
``` js
function MyBind(context,...args){
let self = this;
args = args || [];
let Fn = function(){};
let myBind = function(...params){
const allArgs = [...args, ...params] // 參數合并
return self.call((this instanceof Fn ? this : context), ...allArgs)
}
Fn.prototype = self.prototype
myBind.prototype = new Fn()
return myBind
}
```
## 最終總結
call、apply 和 bind 的功能相似,什么時候使用 bind 呢?這個確實也沒有明確的規定,只要知道其原理,怎么選擇都不重要,主要的區別無非就是 call、apply 綁定后是立即執行,而 bind 綁定后是返回函數體,需要調用即可。
- 首頁
- 2021年
- 基礎知識
- 同源策略
- 跨域
- css
- less
- scss
- reset
- 超出文本顯示省略號
- 默認滾動條
- 清除浮動
- line-height與vertical-align
- box-sizing
- 動畫
- 布局
- JavaScript
- 設計模式
- 深淺拷貝
- 排序
- canvas
- 防抖節流
- 獲取屏幕/可視區域寬高
- 正則
- 重繪重排
- rem換算
- 手寫算法
- apply、call和bind原理與實現
- this的理解-普通函數、箭頭函數
- node
- nodejs
- express
- koa
- egg
- 基于nodeJS的全棧項目
- 小程序
- 常見問題
- ec-canvas之橫豎屏切換重繪
- 公眾號后臺基本配置
- 小程序發布協議更新
- 小程序引入iconfont字體
- Uni-app
- 環境搭建
- 項目搭建
- 數據庫
- MySQL數據庫安裝
- 數據庫圖形化界面常用命令行
- cmd命令行操作數據庫
- Redis安裝
- APP
- 控制縮放meta
- GIT
- 常用命令
- vsCode
- 常用插件
- Ajax
- axios-services
- 文章
- 如何讓代碼更加優雅
- 虛擬滾動
- 網站收藏
- 防抖節流之定時器清除問題
- 號稱破解全網會員的腳本
- 資料筆記
- 資料筆記2
- 公司面試題
- 服務器相關
- 前端自動化部署-jenkins
- nginx.conf配置
- https添加證書
- shell基本命令
- 微型ssh-deploy前端部署插件
- webpack
- 深入理解loader
- 深入理解plugin
- webpack注意事項
- vite和webpack區別
- React
- react+antd搭建
- Vue
- vue-cli
- vue.config.js
- 面板分割左右拖動
- vvmily-admin-template
- v-if與v-for那個優先級高?
- 下載excel
- 導入excel
- Echart-China-Map
- vue-xlsx(解析excel)
- 給elementUI的el-table添加骨架
- cdn引入配置
- Vue2.x之defineProperty應用
- 徹底弄懂diff算法的key作用
- 復制模板內容
- 表格操作按鈕太多
- element常用組件二次封裝
- Vue3.x
- Vue3快速上手(第一天)
- Vue3.x快速上手(第二天)
- Vue3.x快速上手(第三天)
- vue3+element-plus搭建項目
- vue3
- 腳手架
- vvmily-cli
- TS
- ts筆記
- common
- Date
- utils
- axios封裝
- 2022年
- HTML
- CSS基礎
- JavaScript 基礎
- 前端框架Vue
- 計算機網絡
- 瀏覽器相關
- 性能優化
- js手寫代碼
- 前端安全
- 前端算法
- 前端構建與編譯
- 操作系統
- Node.js
- 一些開放問題、智力題