
call、apply
和bind
是挂在Function
对象上的三个方法,调用这三个方法的必须是一个函数。
func.call(thisArg, param1, param2, ...)
func.apply(thisArg, [param1,param2,...])
func.bind(thisArg, param1, param2, ...)
-
在浏览器里,在全局范围内this 指向window对象; -
在函数中,this永远指向最后调用他的那个对象; -
构造函数中,this指向new出来的那个新的对象; -
call、apply、bind
中的this被强绑定在指定的那个对象上; -
箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来; -
apply、call、bind
都是js给函数内置的一些API,调用他们可以为函数指定this的执行,同时也可以传参。

let a = {
value: 1
}
function getValue(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
getValue.call(a, 'poe', '24')
getValue.apply(a, ['poe', '24'])
bind
和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过bind
实现柯里化
方法的应用场景
下面几种应用场景,你多加体会就可以发现它们的理念都是“借用”方法的思路。我们来看看都有哪些。
-
判断数据类型
用
Object.prototype.toString
来判断类型是最合适的,借用它我们几乎可以判断所有类型的数据
function getType(obj){
let type = typeof obj;
if (type !== "object") {
return type;
}
return Object.prototype.toString.call(obj).replace(/^$/, '$1');
}
-
类数组借用方法
类数组因为不是真正的数组,所有没有数组类型上自带的种种方法,所以我们就可以利用一些方法去借用数组的方法,比如借用数组的
push
方法,看下面的一段代码。
var arrayLike = {
0: 'java',
1: 'script',
length: 2
}
Array.prototype.push.call(arrayLike, 'jack', 'lily');
console.log(typeof arrayLike); // 'object'
console.log(arrayLike);
// {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}
用
call
的方法来借用Array 原型链上的 push
方法,可以实现一个类数组的 push
方法,给arrayLike
添加新的元素
-
获取数组的最大 / 最小值
我们可以用 apply 来实现数组中判断最大 / 最小值,
apply
直接传递数组作为调用方法的参数,也可以减少一步展开数组,可以直接使用Math.max、Math.min
来获取数组的最大值 / 最小值,请看下面这段代码。
let arr = [13, 6, 10, 11, 16];
const max = Math.max.apply(Math, arr);
const min = Math.min.apply(Math, arr);
console.log(max); // 16
console.log(min); // 6
实现一个 bind 函数
对于实现以下几个函数,可以从几个方面思考
-
不传入第一个参数,那么默认为 window
-
改变了 this
指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
var args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
实现一个 call 函数
Function.prototype.myCall = function (context) {
var context = context || window
// 给 context 添加一个属性
// getValue.call(a, 'pp', '24') => a.fn = getValue
context.fn = this
// 将 context 后面的参数取出来
var args = [...arguments].slice(1)
// getValue.call(a, 'pp', '24') => a.fn('pp', '24')
var result = context.fn(...args)
// 删除 fn
delete context.fn
return result
}
实现一个 apply 函数
Function.prototype.myApply = function(context = window, ...args) {
// this-->func context--> obj args--> 传递过来的参数
// 在context上加一个唯一值不影响context上的属性
let key = Symbol('key')
context[key] = this; // context为调用的上下文,this此处为函数,将这个函数作为context的方法
// let args = [...arguments].slice(1) //第一个参数为obj所以删除,伪数组转为数组
let result = context[key](args); // 这里和call传参不一样
delete context[key]; // 不删除会导致context属性越来越多
return result;
}
// 使用
function f(a,b){
console.log(a,b)
console.log(this.name)
}
let obj={
name:'张三'
}
f.myApply(obj,[1,2]) //arguments[1]
原文始发于微信公众号(消失的程序员):apply/call/bind 原理
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/250974.html