1. 关于this
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象。
包括我在内的前端学习者在学习过程中可能都被this的指向搞蒙圈过;而且this在很多中不同的编程方式里略有不同,this在面向对象中尤为重要,比如可以帮助我们获取操作的节点等等。
2.一般函数中的this
function fn() {
var str = "hello,world!";
console.log(this.str); //undefined
console.log(this); //Window
}
fn();
//window.fn();
我们在一般的函数中打印this,输出的window,根据定义我们可以理解成fn这个函数其实是被Window对象调用出来的,因此this便指向了window;
var obj = {
str:"hello,world!",
fn:function(){
console.log(this.str); //hello,world!
}
}
obj.fn();
在这里fn函数有obj对象调用,因此this就指向了obj对象,从而可以输出对象里面的str;
var obj = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //12
}
}
}
obj.b.fn();
由这个多层嵌套的例子,我们可以进一步详细的理解为:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象;
3. 构造函数里的this
function Fn(){
this.str = "hello,world!";
}
var fn = new Fn();
console.log(fn.str); //hello,world!
我们用new关键字和变量a创建了一个Fn()的实例化对象(相当于复制了一份Fn到对象fn里面),仅创建并没有执行,最终调用这个函数Fn的是对象fn,那么this指向的自然是对象fn,已经复制了一份Fn函数到对象fn中,因此对象fn中会有str,并可以打印;
4. 当this和return相遇
//例子1:
function Fn1()
{
this.str = "hello,world!";
return {};
}
var fn1 = new Fn1;
console.log(fn1.str); //undefined
//例子2:
function Fn2()
{
this.str = "hello,world!";
return function(){};
}
var fn2 = new Fn2;
console.log(fn2.str); //undefined
//例子3:
function Fn3()
{
this.str = "hello,world!";
return 1;
}
var fn3 = new Fn3;
console.log(fn3.str); //hello,world!
通过对比上述举例的结果,我们可以理解在有return的情况下,如果return返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象,那么this还是指向构造函数的示例化对象。
5. call和apply
/*apply和call调用*/
let obj1 = {
str: 'hello,world!',
name: 'Tony'
};
let obj2 = {
name: 'Frank',
fn: function () {
console.log(this.name);
}
}
obj2.fn.call(obj1); //Tony
obj2.fn.apply(obj1); //Tony
obj2.fn(); //Frank
此时虽然是 obj2 调用方法,但是使用了call或者apply ,动态地把 this 指向变到了 obj1 ;相当于这个 obj2.fn 这个执行环境是 obj1 ;从而输出的name是obj1中的Tony,而不是obj2中的Frank;
call 和 apply 两个主要用途:
改变 this 的指向(把this从obj2指向到obj1);
方法借用(obj1没有fn ,只是借用obj2方法);
call与apply区别
call和apply的作用完全一样,唯一的区别就是在参数上面;
call 接收的参数不固定,第一个参数是函数体内this的指向,第二个参数以下是依次传入的参数;
-
apply接收两个参数,第一个参数也是函数体内 this 的指向。第二个参数是一个集合对象(数组或者类数组);
/*apply和call区别*/ let fn = function (a, b, c) { console.log(a, b, c); } let arrArray = [1, 2, 3]; fn.call(window, arrArray); //[1, 2, 3] undefined undefined fn.apply(window, arrArray); //1 2 3
6. bind
let obj1 = {
str: 'hello,world!',
name: 'Tony'
};
let obj2 = {
name: 'Frank',
fn: function () {
console.log(this.name);
}
}
let a = obj2.fn.bind(obj1);
a();
对比call和apply的例子,我们不难看出,bind也是改变this指向,但有所区别:call和apply都是即时调用,绑定既是调用,而bind不是,bind会返回绑定后的新函数,改变新函数的this指向,而后需要的时候再调用;
7. 箭头函数中的this
但是在箭头函数里面,没有this,因此箭头函数里面的this指向它的宿主对象。
//例子1:
let obj1 = {
str: 'hello,world!',
fn: function () {
setTimeout(function () {
console.log(this.str);
})
}
}
obj1.fn(); //undefined
//例子2:
let obj2 = {
str: 'hello,world!',
fn: function () {
setTimeout(()=>{
console.log(this.str);
})
}
}
obj2.fn(); //hello,world!
对比两个举例不难看出,第一个用的一般函数,因此指向window,打印window.str结果为未定义;第二个使用箭头函数,箭头函数里面没有this,便向上层作用域查找,在这个例子2中, setTimeout的上层作用域是fn,而fn里面的this指向obj2,所以setTimeout里面的箭头函数的this,指向obj2,因此能打印obj2中的str;
结尾:仅根据个人学习后的理解整理如上知识点,如有错误,请不吝赐教~