首先得明白一点:this是一个对象类型,既不指向函数自身也不指向函数的词法作用域。
this是在执行上下文创建时确定的,而执行上下文又是函数被调用时创建的,所以this是在函数被调用时确定的,也就是this与函数的创建的位置无关,this指向与函数调用的方式有关 ,一般情况this指向调用者,若直接执行则this指向window,当然,javascript也提供了一些方法让this强制绑定某一对象。
看个例子:
this.a = 2;
const obj = {
a: 1,
test: function () {
console.log(this.a);
}
}
obj.test(); // 1
这个很简单,test的this指向obj,那改下
this.a = 2;
const obj = {
a: 1,
test: function () {
console.log(this.a);
}
}
var newTest = obj.test;
newTest(); // 2
那再改下
this.a = 2;
function inner() {
console.log('a',this.a);
}
const obj = {
a: 1,
inner: function() {
console.log(this.a);
},
test: function () {
console.log(this.a);
this.inner();
}
}
var newTest = obj.test;
newTest();
// == > 2
// a 2
newTest执行,this指向window,打印2,接着执行this.inner()
,此时的this指向的window,所以调用的inner方法是全局定义的inner函数,打印的a当然也是全局a变量的值。
就这几个例子不难看出,this与函数定义的位置无关,在不被绑定时,this指向调用者,若当前的函数直接调用(没有调用者),不管在哪执行this指向window
call、apply、bind都能改变this的指向
区别:
bind返回一个被绑定this的一个函数,若当前返回的函数重复bind绑定,this只指向首次绑定的对象
call、apply绑定完this就将函数执行掉了,返回值是函数执行的结果,所以call、apply没有重复绑定的一说,他俩的区别就是传参的方式不同,第一个参数都是需要this绑定的对象,至于后面的其它参数,apply只接受参数的第二个数组,并且第二个参数必须是数组,不然会报错,后续参数传了不报错,但会被忽略;call接受的是一个列表式的参数,传多少接受多少
function fun() {
console.log(this, ...arguments);
};
var obj1 = {a: 1};
var obj2 = {b: 3};
fun.call(obj1, 1, 2); // {a: 1} 1 2
fun.call(obj1, [1, 2], 3); // {a: 1} [1, 2] 3
fun.apply(obj2, [1, 2, 3], 3); // {b: 3} 1 2 3
var fun1 = fun.bind(obj1, 2);
fun1(); // {a: 1} 2
var fun2 = fun1.bind(obj2, 3);
fun2(); // {a: 1} 2 3
new操作会返回构造函数里的this对象实例,new时首先会在函数顶部隐式创建一个this对象,然后执行代码,向this对象里添加属性,最后返回this对象。
function Test(name, age) {
// var this = {};
this.name = name;
this.age = age;
// return this;
}
new Test('xiaoliu', 23); // Test {name: "xiaoliu", age: 23}
一定返回this对象吗?看例子
function Test(name, age) {
// var this = {};
this.name = name;
this.age = age;
return {a: 1};
}
new Test('xiaoliu', 23); // {a: 1}
???new时只要构造函数有返回的引用类型的数据,那最终new返回的就不是this对象了
function Test(name, age) {
// var this = {};
this.name = name;
this.age = age;
return '1111';
}
new Test('xiaoliu', 23); // Test {name: "xiaoliu", age: 23}
const obj = {
a: 1,
test: function() {
setTimeout(function() {
console.log(this);
}, 0);
}
}
obj.test(); // Window {.......}
???当函数在执行栈执行遇到setTimeout这类异步事件是,js引擎会将这类函数的触发条件和回调函数扔给浏览器其它线程去处理,等到满足条件时就会将回调函数人到事件队列等待执行,等到执行栈空闲时在去取事件队列的函数,然后执行。为什么上述代码答应的this时window呢,因为执行栈执行的只是setTimeout的回调函数,没有调用者,是直接执行的,所以this自然指向window。这样的异步事件很多,就是宏任务和微任务
那怎么将this指向为当前定义的obj对象呢?
const obj = {
a: 1,
test: function() {
var _this = this; // <==
setTimeout(function() {
console.log(_this);
}, 0);
}
}
obj.test(); // {a: 1, test: ƒ}
2、箭头函数
const obj = {
a: 1,
test: function() {
setTimeout(() => {
console.log(_this);
}, 0);
}
}
obj.test(); // {a: 1, test: ƒ}
箭头函数没有this,它的this是通过继承父作用域里的this,说到这了,再聊聊箭头函数
箭头函数时ES6的语法,在开发中大量被使用,因为他比较轻量,没有this,没有arguments,没有原型,没有super
1、没有arguments
const obj = {
a: 1,
test: (a) => {
console.log(arguments);
}
}
obj.test(1); // Uncaught ReferenceError: arguments is not defined
2、没有原型,但箭头函数仍是Function对象的一个实例
const func = () => {}
func.prototype // undefined
Function.prototype === func.__proto__ // true
3、不能new,报错:箭头函数不是一个构造器
const func = () => {}
new func();// Uncaught TypeError: func is not a constructor
4、没有this
const obj = {
a: 1,
test(){
return () => {
console.log(this);
}
},
}
obj.test()(); // {a: 1, test: ƒ}
那试试this的硬绑定
const obj = {
a: 1,
test(){
return () => {
console.log(this);
}
},
}
const obj1 = {
d: 3
}
obj.test.call(obj1)(); // {a: 1, test: ƒ}
obj.test().apply(obj1); // {a: 1, test: ƒ}
const bindFn = obj.test().bind(obj1);
bindFn(); // {a: 1, test: ƒ}
显然都不行,所以箭头函数的this等于父级作用域里的this,只有改变父级的this才能改变箭头函数的this
1.默认绑定,没有被明确隶属对象执行的函数的this,window
2.隐式绑定,被明确隶属对象执行的函数的this,执行对象
2.1.隐式丢失:如果作为参数,传到另一个函数中,那么丢失原本的对象,变成window
3.强制绑定,利用函数的方法(call,apply,bind),改成谁,this就是谁
3.1.修复隐式丢失
4.new绑定,函数被new执行后,函数内部的this会指向,new出来的实例
this永远跟着当前函数走,this永远是一个对象,永远在执行时才能确定指向
因篇幅问题不能全部显示,请点此查看更多更全内容