网站首页 > 技术文章 正文
前言:
call、apply、bind这3个方法的用处都是更改this指向,在学习call、apply、bind之前,需要先了解this,所以本文会先对this进行讲解。
通过本文可以了解:
- this是什么
- call、apply、bind是如何实现的
- call、apply、bind的用处
什么是this
定义:
当前执行上下文(global、function 或 eval)的一个属性,在非严格模式下,总是指向一个对象,在严格模式下可以是任意值。
说白了,this就是一个变量,他指向一个对象或者一个参数,我们通常需要使用this来设置或获取变量、对象、方法,最重要的就是能够分辨出当前使用的this的指向。接下来我们来看看不同情况下this的指向。
重点:this的指向是==调用时==决定的。
1. 全局上下文
- 在全局上下文中,this指向的是window
console.log(this==window)
//true
2.函数调用
- 直接调用this指向window
function test(){
console.log(this)
}
test();
//window
function test(){
test1()
function test1(){
console.log(this)
}
}
test();
//window
3.通过对象调用
- this指向调用方法的对象
const obj = {
a:1,
test:function(){
console.log(this)
}
}
console.log(obj.test())
//obj
4.箭头函数
- this与所在上下文this指向相同
const fun1 = ()=>{
console.log(this)
}
console.log(fun1())
//window
function fn1(){
console.log(this)
return ()=>{
console.log(this)
}
}
console.log(fn1()())
//window
//window
5.构造函数
- this指向构造出的对象
function Test(){
this.name = 'test';
}
let t = new Test();
console.log(t.name)
//test
call、apply、bind原生实现解析
1.call
function.call(thisArg, arg1, arg2, ...)。 第一个参数为要更改的对象,arg1,arg2...为传入参数
原生实现
//call函数原生模拟实现
//给context一个默认值,如果没有传入,默认指向window,用...来将传入参数转为数组
Function.prototype.call = function(context = window,...args){
//将调用call的函数挂在传入的对象上,起到更改this的目的
context.fun = this;
//执行方法,并传入参数
const result = context.fun(...args);
//执行完毕删除刚创建的自定义方法,防止污染
delete context.fun;
//返回结果
return result
}
let obj = {
a:1
}
function test(){
console.log(this.a)
}
test.call(obj);
//输出
//1
2.apply
func.apply(thisArg, [argsArray])。 第一个参数为要更改的对象,第二个参数为func执行时传入的数组参数
//apply函数原生模拟实现
//给context一个默认值,如果没有传入,默认指向window,用...来将传入参数转为数组
Function.prototype.apply = function(content=window,args){
//将调用call的函数挂在传入的对象上,起到更改this的目的
content.fn = this;
//执行方法,并传入参数
let result = content.fn(...args);
//执行完毕删除刚创建的自定义方法,防止污染
delete content.fn;
//返回结果
return result;
}
let obj = {
a:1
}
function test(){
console.log(this.a)
}
test.apply(obj);
call/apply方法基本一样,唯一的区别在于call传入的是多个参数,apply传入的为数组
3.bind
function.bind(thisArg[, arg1[, arg2[, ...]]])。第一个参数为要更改的对象,arg1, arg2, ...为传入的参数
- 与call、apply的不同在于,call、apply调用后会立即执行,返回值依赖于调用他们的函数。而bind,调用后会返回原函数,拥有指定的this以及初始参数。
//bind函数原生模拟实现
//给context一个默认值,如果没有传入,默认指向window,用...来将传入参数转为数组
Function.prototype.bind = function(oThis) {
//校验调用者是否为方法
if (typeof this !== "function") {
throw new TypeError("error");
}
//截取入参内的参数,并转为数组
const aArgs = Array.prototype.slice.call(arguments, 1);
//临时存储调用bind的对象
const fToBind = this;
//中间方法
const fNOP = function() {};
//返回的新函数
const fBound = function() {
//这里判断this指向
//如果this指向中间方法,则说明返回的函数fBound通过new调用了,则这里的this不更改指向,否则指向bind时传入的对象
//执行方法并将bind时传入的参数与新函数调用时传入的参数合并,传入执行方法。
//例:如果通过let t = new test1()
//此时 这里的this指向t,通过t的原型链可以找到fNOP的原型对象,这时候不能更改this为其他值
return fToBind.apply(this instanceof fNOP && oThis ?
this :
oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
//中间方法的原型指向调用bind的原型对象
fNOP.prototype = this.prototype;
//将返回函数的原型对象的隐式原型指向中间方法的原型对象
fBound.prototype = new fNOP();
return fBound;
};
let obj = {
a: 1
}
function test() {
console.log(this.a)
}
let test1 = test.bind(obj);
test1()
//输出
//1
到这里,call、apply、bind的实现方式已经介绍完毕。
为什么要更改this指向
接下来用几个示例说明为什么要使用call、apply、bind更改this指向
例1:
var obj = {
fn1: function() {
},
fn2: function() {
},
fn3: function(cb) {
//这里调用的this.fn3传入的function,由于是直接调用,所以this指向的是window
cb();
},
list: function() {
this.fn3(function() {
console.log(this)
this.fn1();
this.fn2();
});
}
};
obj.list();
执行这段代码,控制台会报个错误,this.fn1 is not a function,我们查看this.fun3中的this,可以知道,这里的this指向的是window,去window中查询fn1方法,的确是查不到。
这时候我们就需要更改方法中的this指向了。
1、使用call
var obj = {
fn1: function() {
console.log('fn1')
},
fn2: function() {
console.log('fn2')
},
fn3: function(cb) {
cb.call(this);
console.log('fn3')
},
list: function() {
this.fn3(function() {
this.fn1();
this.fn2();
});
}
};
obj.list();
//输出
//fn1
//fn2
//fn3
2、使用bind
var obj = {
fn1: function() {
console.log('fn1')
},
fn2: function() {
console.log('fn2')
},
fn3: function(cb) {
cb();
console.log('fn3')
},
list: function() {
this.fn3(function() {
this.fn1();
this.fn2();
}.bind(this));
}
};
obj.list();
//输出
//fn1
//fn2
//fn3
例2:
function test(){
console.log(typeof arguments)
//通过输出可以看到,arguments是类数组对象
//如果我们想要把传入的参数转为数组,就需要借用call,将slice方法挂到arguments上,从而实现我们要的功能
let args = Array.prototype.slice.call(arguments,0)
console.log(args);
}
test(1,2,3);
//输出
//object
//[1,2,3]
通过call方法可以将不存在当前对象的方法挂在到自己对象上,从而实现我们需要的功能。
例3:
const arr = [1,2,3,4];
//通过apply方法,将min方法挂到window上,然后将arr转为字符串组传入min方法
const min = Math.min.apply(null,arr);
console.log(min)
//输出
//1
通过这3个示例,可以看到call、apply、bind的使用场景。
结尾:
call、apply都是立即执行,call传入的是多个参数,apply传入的是数组。
bind是返回一个新函数,拥有this以及初始参数。
在此,对call、apply、bind的介绍就结束了,希望本篇文章对大家有所启示,谢谢观看~
- 上一篇: 碎片时间学编程「202]:分组数组元素
- 下一篇: 一道二进制子串算法,让面试官都解不出来?
猜你喜欢
- 2024-11-21 浅析GIF 格式图片的存储与解析
- 2024-11-21 如何用2 KB代码实现3D赛车游戏?2kPlus Jam大赛了解一下
- 2024-11-21 快速了解ES6的代理与反射
- 2024-11-21 「实战」蘑菇街 PC 端首页,瀑布流布局的实现原理与细节技巧
- 2024-11-21 Knative 驾驭篇:带你 '纵横驰骋' Knative 自动扩缩容实现
- 2024-11-21 ECMAScript 6使用教程总结
- 2024-11-21 一道二进制子串算法,让面试官都解不出来?
- 2024-11-21 碎片时间学编程「202]:分组数组元素
- 2024-11-21 从入门到入土:Lambda完整学习指南,包教包会(上)
- 2024-11-21 碎片时间学编程「58]:取消组合数组元素
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-