翼度科技»论坛 编程开发 JavaScript 查看内容

JavaScript this 绑定详解

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
函数内 this 绑定

函数内this的绑定和函数定义的位置没有关系,和调用的方式和调用位置有关系,函数内的this是在被调用执行时被绑定的。
this的具体绑定规则

this 绑定基本包含下面4种绑定规则,以及一些其它的特殊绑定规则:

  • 默认绑定
  • 隐式绑定
  • 显式绑定
  • new绑定
默认绑定

独立的函数被调用,那么里面的this绑定的是window,也就是调用时没有被绑定到具体的对象。
比如普通的函数调用:
  1. function foo() {
  2.   console.log(this); // window
  3. }
  4. foo();
复制代码
第二种情况是函数调用链:
  1. function test1() {
  2.   console.log(this); // window
  3.   test2();
  4. }
  5. function test2() {
  6.   console.log(this); // window
  7.   test3();
  8. }
  9. function test3() {
  10.   console.log(this); // window
  11. }
  12. test1();
复制代码
在调用每一个方法的时候,方法的前面没有绑定对象,并不是像obj.test1()这种形式前面绑定了对象。
第三种情况是函数作为参数:
  1. function foo(func) {
  2.   func();
  3. }
  4. function bar() {
  5.   console.log(this);    // window
  6. }
  7. foo(bar);
复制代码
同上,虽然看起来bar到了foo的上下文中了,但是调用bar函数时,前面没有绑定对象,那么还是默认绑定,是由window调用的。
可以再看一下上例中的变例:
  1. function foo(func) {
  2.   func();
  3. }
  4. var obj = {
  5.   name: "easylee",
  6.   bar: function () {
  7.     console.log(this);  // window
  8.   },
  9. };
  10. foo(obj.bar);
复制代码
上面的变例,表面上看,调用的是obj对象的bar方法,但是具体执行的时候,是将这个方法整体作为参数传入foo,foo里面执行func()的时候,执行func()并没有绑定任何对象,调用obj.bar传入参数的时候,只是调用并没有执行,所以还是window。
一定要注意,只有在调用执行的时候绑定了对象才算绑定,定义的时候就算它绑定了,但是调用并执行的时候未绑定就不算绑定。
提示:
在严格模式下,默认绑定模式,直接调用this(也就是window)会打印undefined,在webpack等打包工具中,通常会使用严格模式,来避免一些低级的语法错误。
所以在直接打印this的地方,直接使用window来代替 this
隐式绑定

通过一个对象,调用对象里面的函数,也就是通过某个对象发起的函数调用,此时函数的this就会绑定为这个对象。
  1. function foo() {
  2.   console.log(this);
  3. }
  4. let obj1 = {
  5.   name: "obj1",
  6.   foo: foo
  7. };
  8. let obj2 = {
  9.   name: 'obj2',
  10.   obj1: obj1
  11. }
  12. obj1.foo();  // obj1
  13. obj2.obj1.foo();  // obj1
  14. let bar = obj1.foo;
  15. bar();  // window,还是默认绑定,最终还是window在调用这个函数
复制代码
隐式绑定有一个前提是调用的对象内部一定有一个对函数的引用,如上例中的foo:foo,对象一定有一个引用函数的属性,通过这个引用,间接的将函数里面的this绑定到这个对象中。
显式绑定

如果不希望在对象内部包含函数引用的属性,同时又希望this绑定到这个对象上,那么可以使用显示绑定,就不需要在这个对象设置函数引用属性了。
通常使用 call 、apply和 bind 来给this显式的绑定一个对象。
使用call、apply均是显示的指定函数执行时,函数中this的指向,通过第一个参数传递需要绑定的对象。
三者的区别,可以看:https://www.cnblogs.com/easy1996/p/17954254
  1. let name = "window";
  2. function foo() {
  3.   console.log(this.name);
  4. }
  5. let obj1 = {
  6.   name: "obj1",
  7. };
  8. foo();  // window
  9. foo.call(obj1); // obj1
  10. foo.apply(obj1);  // obj1
复制代码
可以看到,后面两个函数显式指定了对象,其中的this打印的是 obj1 的 name 属性。
bind函数也是一样的,只是还需要调用一遍:
  1. function foo() {
  2.   console.log(this.name);
  3. }
  4. let obj1 = {
  5.   name: "obj1",
  6. };
  7. let bar = foo.bind(obj1);
  8. bar(); // obj1
复制代码
临时绑定对象一般使用call和apply即可,像是很多框架中,使用bind来绑定并创建新的函数,之后均使用新函数即可,这样做的好处是,可以固定的绑定一个对象,因为新函数是绑定后创建的,那么里面的this则不会再发生改变。
new绑定

new绑定则非常简单,类里面的 this 就是 new Class() 创建对象时,创建后的对象。
  1. function Person() {
  2.   console.log(this);  // {name: "person1"} 和 {name: "person2"}
  3. }
  4. let p = new Person();
  5. p.name = 'person1'
  6. let p2 = new Person();
  7. p2.name = 'person2'
复制代码
其它绑定规则

有一些绑定规则不遵循上面的四条绑定规则,同时也是非常常用的规则:
1.显式绑定忽略
如果call、apply、bind绑定的对象是null或者undefined那么显示绑定会被忽略,转变为默认绑定,this 指向 window。
2.自执行间接函数引用
如果使用自执行的方式去执行,引用其它对象的函数的这种形式,那么会指向window。具体效果查看代码,这个记住就行,不要去套上面的规则,肯定套不上:
  1. let obj1 = {
  2.   name: 'obj1'
  3. };
  4. let obj2 = {
  5.   name: 'obj2',
  6.   foo: function () {
  7.     console.log(this.name);
  8.   },
  9. };
  10. obj2.foo(); // obj2
  11. obj1.bar = obj2.foo;
  12. obj1.bar();  // obj1
  13. (obj1.foo = obj2.foo)();  // window不是obj1,是window调用的
复制代码
3.内置函数的绑定
setTimeout内绑定的是window:
  1. setTimeout(function () {
  2.   console.log(this); // window
  3. });
复制代码
forEach等遍历函数的绑定:
  1. ["foo", "bar"].forEach(function () {
  2.   console.log(this); // window 默认是window
  3. });
  4. let obj1 = {
  5.   name: "obj1",
  6. };
  7. ["foo", "bar"].forEach(function () {
  8.   console.log(this); // obj1,可以传递绑定的对象参数
  9. }, obj1);
复制代码
元素的事件默认绑定的是元素对象,除非使用箭头函数:
  1. const box = document.querySelector(".box");
  2. box.onclick = () => {
  3.   console.log(this);  // window
  4. };
  5. box.onclick = function() {
  6.   console.log(this);  // box对象
  7. }
复制代码
4.箭头函数的绑定
箭头函数的this不绑定任何对象,而是从上层作用域中找到对应的this的绑定对象。
  1. let obj1 = {
  2.   name: "obj1",
  3.   foo: () => {
  4.     console.log(this);
  5.   },
  6.   bar: function () {
  7.     console.log(this);
  8.   },
  9. };
  10. obj1.foo(); // window 当前默认是obj1对象中,使用箭头函数所以上一层是window
  11. obj1.bar(); // obj1
复制代码
要注意箭头函数不仅默认this不绑定对象,而且是无法绑定,就算通过显式绑定,也无法生效。
常见题目

常见题目1
  1. let name = "window";
  2. let person = {
  3.   name: "person",
  4.   sayName: function () {
  5.     console.log(this.name);
  6.   },
  7. };
  8. function sayName() {
  9.   let sss = person.sayName;
  10.   sss();  // window
  11.   person.sayName(); // person
  12.   (person.sayName)(); // person 这里就是单纯的执行一下,和上面没区别
  13.   (b = person.sayName)(); // window
  14. }
  15. sayName();
复制代码
常见题目2
  1. var name = "window";
  2. var person1 = {
  3.   name: "person1",
  4.   foo1: function () {
  5.     console.log(this.name);
  6.   },
  7.   foo2: () => console.log(this.name),
  8.   foo3: function () {
  9.     return function () {
  10.       console.log(this.name);
  11.     };
  12.   },
  13.   foo4: function () {
  14.     return () => {
  15.       console.log(this.name);
  16.     };
  17.   },
  18. };
  19. var person2 = { name: "person2" };
  20. person1.foo1(); // person1
  21. person1.foo1.call(person2); // person2
  22. person1.foo2(); // window
  23. person1.foo2.call(person2); // window
  24. person1.foo3()(); // window
  25. person1.foo3.call(person2)(); // window
  26. person1.foo3().call(person2); // person2
  27. person1.foo4()(); // person1
  28. person1.foo4.call(person2)(); // person2
  29. person1.foo4().call(person2); // person1
复制代码
常见题目3
  1. var name = "window";
  2. function Person(name) {
  3.   this.name = name;
  4.   this.foo1 = function () {
  5.     console.log(this.name);
  6.   };
  7.   this.foo2 = () => console.log(this.name);
  8.   this.foo3 = function () {
  9.     return function () {
  10.       console.log(this.name);
  11.     };
  12.   };
  13.   this.foo4 = function () {
  14.     return () => {
  15.       console.log(this.name);
  16.     };
  17.   };
  18. }
  19. var person1 = new Person("person1");
  20. var person2 = new Person("person2");
  21. person1.foo1(); // person1
  22. person1.foo1.call(person2);   // person2
  23. person1.foo2(); // person1
  24. person1.foo2.call(person2); // person1
  25. person1.foo3()(); // window
  26. person1.foo3.call(person2)(); // window
  27. person1.foo3().call(person2); // person2
  28. person1.foo4()(); // person1
  29. person1.foo4.call(person2)(); // person2
  30. person1.foo4().call(person2); // person1
复制代码
常见题目4
  1. var name = "window";
  2. function Person(name) {
  3.   this.name = name;
  4.   this.obj = {
  5.     name: "obj",
  6.     foo1: function () {
  7.       return function () {
  8.         console.log(this.name);
  9.       };
  10.     },
  11.     foo2: function () {
  12.       return () => {
  13.         console.log(this.name);
  14.       };
  15.     },
  16.   };
  17. }
  18. var person1 = new Person("person1");
  19. var person2 = new Person("person2");
  20. person1.obj.foo1()(); // window 最终调用函数时,没有任何隐式绑定和显示绑定,就是普通调用
  21. person1.obj.foo1.call(person2)(); // window
  22. person1.obj.foo1().call(person2); // person2
  23. person1.obj.foo2()(); // obj
  24. person1.obj.foo2.call(person2)(); // person2
  25. person1.obj.foo2().call(person2); // obj
复制代码
来源:https://www.cnblogs.com/easy1996/p/17954257
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具