this指向问题
this机制:this绑定只取决于函数调用的方式,大的来说有三种:
- 函数调用:window/undefined
- 方法调用:调用对象
- 构造函数:返回的实例对象
此外,注意两点:
- 利用call、apply、bind可以改变this指向
- 箭头函数的this继承外层函数
1. this机制
- 全局作用域中,window
- 函数调用(普通调用、IIFE),window
- 方法调用,绑定到调用对象
- 隐式丢失(被传递给变量或者参数)
var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a: 1,
foo: foo
};
obj.foo(); // 1
var bar = obj.foo; // 隐式丢失
bar(); // 0
(obj.foo)(); // 1
(obj.foo = obj.foo)(); // 0
- 显式绑定 : bind、apply、call (作业帮面试)
- 硬绑定:绑定后无法修改this
var a = 0;
var obj = {a: 1};
function foo(){
console.log(this.a);
};
function bar(){
foo.call(obj);
};
bar(); // 1
var baz = foo.bind(obj);
baz(); // 1
- 数组内置函数显示绑定:
map forEach filter some every
- new绑定:构造函数
- 构造函数一般不使用return语句,通常初始化新对象,当构造函数的函数体执行完毕时,会显示返回。this指向新返回的实例对象
- 构造函数使用了return语句,但没指定返回值,或者返回一个原始值,那么这时将忽略返回值。返回构造出来的实例对象。
- 构造函数显示地使用return语句返回一个对象,this指向这个对象
// 1
function Car(){
this.counts = 1200;
}
var c = new Car(); // {counts: 1200}
// 2
function Car(){
this.counts = 1200;
return;
}
var c = new Car(); // {counts: 1200}
// 3
function Car(){
this.counts = 1200;
return {noCounts: 0};
}
var c = new Car(); // {noCounts: 0}
- 严格模式下,独立调用的函数的this指向undefined
- 非严格模式下,call、apply中的null、undefined会被转化成window
function Foo(v){
this.a = v;
};
var obj = {};
var bar = Foo.bind(obj);
bar(0);
console.log(obj.a); // 0
var baz = new bar(1);
console.log(obj.a); // 0
console.log(baz.a); // 1
2. this指向优先级
优先级:new > call/apply/bind > 方法调用 > 函数调用
// 1.
var a = 0;
function foo(){
function test(){
console.log(this.a);
};
return test;
};
var obj = {
a: 1,
foo: foo
}
obj.foo()(); // 0
// 2.
var a = 0;
function foo(){
var that = this;
function test(){
console.log(that.a);
};
return test;
};
var obj = {
a: 1,
foo: foo
}
obj.foo()(); // 1
3. 箭头函数
- 箭头函数:改变this机制,不再只是根据函数调用方式决定;箭头函数会继承外层this指向 —> 这直接解决了
var that = this;
的写法 - call/apply/bind 无法改变箭头函数的this指向(流利说面试)
var a = 0;
function foo(){
var test = () => this.a;
return test;
};
var obj1 = {
a: 1,
foo: foo
};
var obj2 = {
a: 2,
foo: foo
};
obj1.foo().call(obj2); // 1
4. 手写bind
(度小满面试)
一、bind
和柯里化是分不开的。手写bind
就是利用柯里化绑定部分参数实现的。
// 注意内外两层参数
Function.prototype._bind = function(thisArg, ...args){
let fun = this;
return function(){
fun.call(thisArg, ...args, ...arguments);
}
}
二、手动实现一个apply
// 利用方法调用,绑定this指向
Function.prototype._apply = function(thisArg, ...args){
// 注意这里只考虑了thisArg为对象的情况,才能添加方法
thisArg['_fun'] = this; // 添加方法
let result = thisArg._fun(...args);
delete thisArg._fun; // 删除方法
return result;
}