彻底搞清楚 this 的指向!!!

this 是什么

this是 js 中的一个关键字

它是函数运行时,在函数体内部自动生成的一个对象,它只有在函数内部时才可以使用

总结一下,this就是函数运行时所处的环境对象(即执行期上下文)

执行期上下文:所有的函数在被调用时们都会创建一个执行期上下文,执行期上下文中记录着函数的调用栈、函数的调用方式等,而 this就是其中的一个属性

this 是什么时候绑定的

既然 this绑定的是函数运行时所处的环境对象,那么 this是什么时候绑定的呢

this是在函数被调用的时候绑定的

举个栗子:当函数 test执行的时候,就会将this的值绑定为 window

function test() {
  console.log(this);
}
test();	//window

再举个栗子

function test() {
  console.log(this);
}
var obj = {
  name: '张三',
  foo: test
};
obj.foo(); // obj


function test1() {
  console.log(this);
}
var obj1 = {
  name: '张三',
  foo: function test1() {
    console.log(this);
  }
};
var foo = obj1.foo;
foo(); // window

这个栗子中,为什么执行的都是同一个函数test,但this的值一个是 obj,另一个是却是window

从这里貌似可以得出这样的结论:

  • this值的绑定和函数定义时的位置没有关系
  • this值的绑定和函数调用的方式有关,谁调用函数,this就绑定为谁(或者说是指向谁)
  • this是在运行时,才会别绑定的,光定义、不运行相当于不起作用

this 是如何绑定的

绑定的方法大概有以下这几种

  • 默认绑定,即函数独立调用,this指向window
  • 隐式绑定,即函数通过某个对象进行调用,那么 this就指向这个调用的对象
  • 显示绑定,即 call、apply、bind
  • new绑定

默认绑定

函数单独的被调用,并没有绑定到那个对象上

// 1、函数直接被调用
function test() {
 console.log(this)
}
test(); // window



// 2、一个函数调用另一个函数,函数只是在另一个函数里面被单独的直接调用,并没有绑定到那个对象上
function test1() {
 console.log(this);
 test2();  // window
}

function test2() {
 console.log(this);
 test3(); // window
}

function test3() {
 console.log(this);
}
test1(); // window

//3、将函数作为参数传递到另一个函数中
// 这里的参数 fun 只是一个函数的引用,然后在 foo 函数里面 直接被调用, 类似于上面的2
function foo(fun) {
  fun();
}

function bar() {
  console.log(this);
}
foo(bar); // window

隐式绑定

函数的执行是通过某个对象调用的,则 this被绑定到调用它的对象上

函数的引用和函数的调用要区分开

var obj1 = {
  name: 'obj1',
  foo() {
    console.log(this);
  }
};

// obj1对象直接调用这个 foo方法
// 对象里面的函数一般称为方法 

// 函数被一个对象直接调用(执行),this 绑定的是这个对象
obj1.foo(); // obj1


var obj2 = {
  name: 'obj2',
  obj1: obj1
};
// 一个对象(obj2)引用另一个对象(obj1),执行obj1里的方法(foo),实际调用(执行)foo 的函数obj1,所以 this 指向的还是 obj1
obj2.obj1.foo(); // obj1


var obj3 = {
  name: 'obj3',
  obj1: obj1.foo,
  fun() {
    console.log(this);
  }
};
// 这里保存的是这个函数的引用,调用函数的话就相当于 obj3 直接调用
// obj1: obj.foo 就相当于下面的 fun() {}
obj3.obj1(); // obj3 

// 将 obj1 里的方法 foo 给了 bar ,再由 bar 调用,函数单独直接被调用,指向 window
var bar = obj1.foo;
bar(); // window

显示绑定

也就是平常所说的通过 call、apply、bind直接改变 this的指向

function foo() {
  console.log(this);
}
var obj1 = {
  name: 'obj1'
};
foo.call(window); // window
foo.apply(obj1); // obj1
var bar = foo.bind(123);
bar(); // 数字123的包装类,会在内部自动执行 Number(123)

使用这三个显示绑定 this的注意点

  • 使用过 bind绑定this之后,再通过call、apply改变 this的绑定是无效的(例1)
  • 原函数 test,对其使用bind进行绑定后的返回的函数,再进行bind绑定是无效的(例2)
  • 进行绑定时,如果this传的是nullundefined,那么此时绑定的 this是无效的(例3)
function foo() {
  console.log(this);
}
var obj1 = {
  name: 'obj1'
};
var obj2 = {
  name: 'obj2'
}

// 例1
var bar = foo.bind(obj1);
bar(); // this 已经绑定为 obj1,再进行 call、或 apply进行绑定是无效的
bar.call(obj2); // obj1
bar.apply(obj2); // obj1


// 例2
var bar = foo.bind(obj1);
bar();
var bar1 = bar.bind(obj2);
bar1();

// 例3
foo.call(null); //window
foo.apply(undefined); // window
var fun = foo.bind(null); 
fun(); // window

new 绑定

指向实例对象

function Person(name, age) {
  this.name = name;
  this.test = function test() {
    console.log(this.name);
  }
}
var person1 = new Person('李四', 18);
person1.test(); // 李四
var person1 = new Person('张三', 18);
person2.test(); // 张三

绑定的优先级

  • 默认绑定的优先级最低
  • 显示绑定的优先级高于隐式绑定的优先级
  • new绑定的优先级高于显示绑定中 bind的优先级(new 、call 、apply 三者同时使用会报错)
function foo() {
  console.log(this);
}

var obj1 = {
  name: 'obj1',
  foo: foo
};
var obj2 = {
  name: 'obj2'
};

foo(); // widnow
obj1.foo(); // obj1
obj1.foo.call(obj2); // obj2

var bar = foo.bind(obj2);
bar(); // obj2
var bar1 = new bar(); // foo

箭头函数的 this

箭头函数的 this是不进行绑定的,即就是 通过call、apply、bind是不能改变其 this指向的

箭头函数的 this是由定义时外围最接近一层的非箭头函数的 this来决定的,也就是平常所说的箭头函数的this是从上层作用域中去找的,一直找不到的话即为window

var name = 'window';
var obj1 = {
  name: 'obj1',
  foo() {
    return () => {
      console.log(this.name);
    }
  }
};

var obj2 = {
  name: 'obj2'
};

obj1.foo()(); // obj1

// 箭头函数的 this 是不能进行绑定的,它的 this 只会从上层作用域中去查找,所以给它进行绑定 this 是无效的
obj1.foo().call(obj2); //obj1

// 这里改变的是箭头函数上层作用域中的 this,也就是 foo 的 this,所以此时的 this指向的才是 obj2
obj1.foo.call(obj2)(); // obj2

关于 this 的几道题

自己做完,在控制台输出对答案就行了~~~

第一道

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};
function sayName() {
  var sss = person.sayName;
  sss(); 
  person.sayName(); 
  (person.sayName)(); 
  (b = person.sayName)(); 
}
sayName();

第二道

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}

var person2 = { name: 'person2' }

person1.foo1(); 
person1.foo1.call(person2); 

person1.foo2();
person1.foo2.call(person2);

person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);

person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);

第三道

var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1()
person1.foo1.call(person2)

person1.foo2()
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

第四道

var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()()
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)

person1.obj.foo2()()
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)

补充与总结

调用方式this 指向
普通函数调用window
构造函数调用实例对象 原型对象里面的方法也指向实例对象
对象方法调用该方法所属的对象
事件绑定方法绑定事件的对象
定时器函数window
立即执行函数window
箭头函数上层作用域中去查找,直至到 window
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值