## 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
- 一些开放问题、智力题