问:改变this指向的方法有哪些?
三种(我知道的)
call 、apply 、bind(这里不含es6语法讲解)
我在复习这几个方法的时候,看了很多博客和讨论,前面两种都很好理解,但是bind(我笨)的自定义,我花了一点时间去适应!
话不多说,开始探索:
1.call
对于call,以前只知道是改变this的一种方法以及怎么用,但是没考虑过当前环境是严格模式还是非严格模式,在一番学习之后,我发现了一点有意思的知识。
第一点:(怎么用)
call的使用很简单,直接看代码!
let obj = {a:1};
function sayHi(a){
console.log("summer 真乖!");
console.log("this是",this);
console.log(a);
}
sayHi();
sayHi.call(obj,9999);//第一个参数是this要指向的对象,后面的参数是传递给函数的参数(可以传多个)
输出:
window
undefined
obj:{a:1}
9999
使用真的easy了。
接下来,看看严格模式和非严格模式下的区别吧!
其实区别就是对于第一个参数的处理不同(我所知道的,有其他不同,评论指出,感谢!)
非严格模式下:
function test (val){
console.log("this----",this);
console.log("val-----",val);
}
test.call(1,999);
输出
this---- Number {1}
val----- 999
严格模式下:
'use strict'
function test1 (val){
console.log("this----",this);
console.log("val-----",val);
}
test1.call(1,888);
输出
this---- 1
val----- 888
实际告诉我们:
🎈 在非严格模式下:如果传入一个原始类型的数据( Number 、String 、 Boolean 、Symbol)等,它的内部会先进行对象包装,将传入的原始类型的数据转化成一个包装对象,再将函数的this指向这个包装对象,而不是原始数据本身
🎈 在严格模式下:call的第一个参数传入什么,调用的函数就指向什么!不进行任何操作!
不理解,可以看上面的代码和图示,表达的很清楚!
2.apply
apply和call真的差不多,只不过传递参数的方式不太一样,所以这里就不再详细讲,介绍一下用法:
function test (...args){
console.log("this----",this);
console.log("val-----",arguments);
}
test.apply(true,[1,2,3]);//后面传递参数使用的是数组
输出:
this---- Boolean {true}
val----- Arguments(3) [1, 2, 3, callee: (...), Symbol(Symbol.iterator): ƒ]
包括对于第一个传入参数的处理,和call也是一致的
apply___ VS ___ call
call传递参数(第一个以后的),可以单个传,也可以传数组 ! 😲
apply传递参数(第一个以后的),可以传数组,不能穿单个值 ! 😲
3.bind
与call和apply的区别:
call和apply改变函数的this指向后,直接执行函数💡
bind改变函数的this指向后,返回一个函数,而不是立马执行 💡
看实际效果:
let obj = {a:1}
function test(){
console.log("bind----",this);
}
let res = test.bind(obj);//调用bind后返回一个函数,原函数并没有立马执行
console.log(res);
res();
输出 :
ƒ test(){
console.log("bind----",this);
}
bind---- {a: 1}
有趣的是,bind和call、apply一样,在非严格模式下,对于第一个参数的处理是一样的👇👇
"use strict"
function test1(){
console.log("bind-----",this);
}
test1.bind(1)();
输出 :
bind----- 1
你以为到这里结束了?当然不是!!!
当函数调用bind改变this指向后,会返回一个函数,那可不可以让返回的这个函数的this也改变呢?🤔
let obj = {a:1};
let jbo = {b:1};
function t(){
console.log(this);
}
let first = t.bind(obj);
first();
let second = first.bind(jbo);
second();
{a: 1}
{a: 1}
答案是不行,💥bind改变this指向后,就不能二次改变了,除非函数二次调用bind💥
4.自定义
相信看完,或多或少都有提升吧!
接下来,我们一起看看如何自己实现call、apply、bind方法吧!
myCall💡
Function.prototype.myCall = function(context = window, ...args) {
let type = typeof context;
context = context == null || context == undefined ? window : context;
context.fn = null;
if (type == "number" || type == "string" || type == "boolean") {
switch (type) {
case "number":
context = new Number(context);
break;
case "string":
context = new String(context);
break;
case "boolean":
context = new Boolean(context);
break;
}
context.fn = this;
} else {
context.fn = this;
}
const res = context.fn(...args);
delete context.fn;
return res;
}
myApply💡
Function.prototype.myApply = function(context = window, ...args) {
let type = typeof context;
context = context == null || context == undefined ? window : context;
context.fn = this;
const res = context.fn(...args);
delete context.fn;
return res;
}
myBind💡
Function.prototype.myBind = function(context) {
if (typeof this != "function") {
throw new Error("this is not a function");
}
let _this = this;
let oargs = Array.prototype.slice.call(arguments, 1);
let Func = function() {
let iargs = Array.prototype.slice.call(arguments);
return _this.apply(this instanceof Func ? this : context, oargs.concat(iargs));
// 因为调用bind之后返回的是一个函数,所以有可能作为构造函数被new了,所以这里要加一个判断,保证原型上的东西不丢
}
let Fnull = function() {}
Fnull.prototype = this.prototype;
Func.prototype = new Fnull();
return Func;
}
对于自定义方法,都是我个人学习和自我总结之后写的,如果有错误,还请各位大佬指正!
好了,对于改变this(不含es6)的方法,我介绍完了,欢迎大家评论补充、纠错!
觉得有用,记得给个赞,哈哈😁