this📑
本质上是个对象,谁调用就指谁,所以使用 call/bind/apply 时通过 this 获取调用的对象
- 全局对象中的 this 指向全局,严格模式下是 undefined
- 函数的 this 在函数调用时已经确定,fun(),其中 fun 是调用者,如果 fun 被其他对象拥有,比如 window.fun(),那么 this 指向拥有者 window(最后一个调用的),否则按第一种情况判断
- 对象的 this 调用由于{}不会形成单独的作用域所以指的是全局对象(注意:区分作用域【定义时确立】和 this 指向【执行时且可以被改变】)
- apply/bind/call 等用于灵活改变 this 指向,第一个参数为 this 的真正指向对象
- new 构造函数 this 指向实例出来的对象
- 箭头函数 this 指向同外层作用域,且指向函数定义时的 this 而非执行时(关联知识点:箭头函数跟普通函数的区别)
有两种情况容易发生隐式丢失问题
:
- 使用另一个变量来给函数取别名
- 将函数作为参数传递时会被隐式赋值,回调函数丢失 this 绑定,普通传参及 setTimeout 均为此种情况
情况一:变量别名导致隐式丢失
function foo() {
console.log(a); // 这里的a表示的是作用域下的a
console.log(this.a); // 这里的a是this对象中的a属性
}
// 注意这里的foo函数不能使用箭头函数,不然直接指向定义时外层的this即window
var obj = { a: 1, foo };
var a = 2;
obj.foo(); // obj调用的foo,this指向obj,打印1
var foo2 = obj.foo;
foo2(); // 隐式丢失,this指向的是window
关于为什么
JavaScript 中的
this
关键字用于指向当前正在执行的函数的上下文对象。它的存在是为了提供对当前上下文的访问权限和操作。
【建议 👍】再来 40 道 this 面试题酸爽继续(1.2w 字用手整理)
关于 call/bind/apply📑
- 使用
.call()
或者.apply()
的函数是会直接执行的,即 obj1.fun.call(obj2)等价于 obj1.fun(),this 为 obj2 bind()
是创建一个新的函数,需要手动调用才会执行,返回一个 bound 函数.call()
和.apply()
用法基本类似,不过call
接收若干个参数,而apply
接收的是一个数组- 如果
call、apply、bind
接收到的第一个参数是空或者null、undefined
的话,则会忽略这个参数。 - ⭐
forEach、map、filter
函数的第二个参数也是能显式绑定this
的
call
call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
- 将函数设为对象的属性
- 执行该函数
- 删除该函数
Function.prototype.myCall = function (context, ...args) {
// 获取调用call的函数
var fn = this;
var result;
// 如果没有传递上下文,则默认为全局对象(非严格模式下)
context = context || window;
// 为上下文对象创建一个唯一的属性,用于保存要调用的函数
var uniqueProp = Symbol("call");
context[uniqueProp] = fn;
// 执行函数
result = context[uniqueProp](...args);
// 删除临时创建的函数属性
delete context[uniqueProp];
return result;
};
apply
Function.prototype.myApply = function (context, argsArray) {
// 保存原始函数
var fn = this;
var result;
// 如果没有传递上下文对象,则默认为全局对象(非严格模式下)
context = context || window;
// 为上下文对象创建一个唯一的属性,用于保存要调用的函数
var uniqueProp = Symbol("apply");
context[uniqueProp] = fn;
// 执行函数
if (argsArray) {
// 使用展开操作符 `...` 将参数数组传递给函数
result = context[uniqueProp](...argsArray);
} else {
result = context[uniqueProp]();
}
// 删除临时创建的函数属性
delete context[uniqueProp];
return result;
};
bind
// 第一版 修改this指向,合并参数
Function.prototype.bindFn = function bind(thisArg) {
if (typeof this !== "function") {
throw new TypeError(this + "must be a function");
}
// 存储函数本身
var self = this;
// 去除thisArg的其他参数 转成数组
var args = [].slice.call(arguments, 1);
var bound = function () {
// bind返回的函数 的参数转成数组
var boundArgs = [].slice.call(arguments);
// apply修改this指向,把两个函数的参数合并传给self函数,并执行self函数,返回执行结果
// 内外arguments不同
return self.apply(thisArg, args.concat(boundArgs));
};
return bound;
};
// 测试
var obj = {
name: "若川",
};
function original(a, b) {
console.log(this.name);
console.log([a, b]);
}
var bound = original.bindFn(obj, 1);
bound(2); // '若川', [1, 2]
ES3
var args = ["1", "dierge"];
var fnStr = "context.fn(";
for (var i = 0; i < args.length; i++) {
fnStr += i == args.length - 1 ? args[i] : args[i] + ",";
}
fnStr += ")";
console.log(fnStr); // context.fn(1,dierge);
感谢您的阅读,如果对文章内容有任何疑问或者建议,欢迎在掘金社区私信我