我们在写代码的时候, 时常会被 this 弄的傻傻分不清楚, 看源码的时候也经常被 call 啊 apply 啊弄的头皮发麻. this 到底是什么? 本文主要根据书上和实际应用做了一些归纳. 一般情况下 this 有 4 种绑定规则:
1, 默认绑定 - this 指向全局变量
举例:
- function baz(){
- console.log('a',this.a);
- };
- var a = 2;
- baz();
- // a 2
在非严格模式下, 我们调用了 baz, 它没有被任何对象包裹, 而是暴露在全局环境中, 因此此时它被调用的上下文就是全局环境, 所以获取到 this.a 是 2;
2, 隐式绑定
这种情况应该是最常见的, 我们调用的方法被包含在某个对象中, 此时应该找到调用这个方法的对象所处的上下文.
举例:
- function baz(){
- console.log('a',this.a);
- };
- var a = 2;
- var obj = {
- a: 3,
- baz: baz
- }
- obj.baz();
- // a, 3
这个时候, this 指向调用 baz 这个方法的对象 obj; 所以获取的 this.a 就是 obj.a 是 3;
思考一下下面的 this.a 的值又是什么:
- function baz(){
- console.log('a',this.a);
- };
- var a = 2;
- var obj = {
- a: 3,
- baz: baz
- };
- var obj2 = {
- a: 4,
- baz: obj.baz
- }
- obj2.baz();
tips: 对象属性的引用链只有最后一层在调用位置中起作用.
3, 显式绑定
这里就是函数可以使用的方法 call(obj,param1,param2,...) 和 apply(obj,[param1,param2,...]), 它们的作用是我们可以显式的改变函数的上下文 (this), 参数说明:
obj: 一个对象, 将方法的 this 指向该对象的 this;
paramX: 参数, 两者的表现形式不同
- var a = 2;
- function baz(){
- console.log('a',this.a);
- };
- var obj = {
- a: 3
- };
- baz.call(obj); // 改变了 this, 并执行了函数
- // a 3
如果直接 baz(), 则采用的是第一种规则, 这里使用 baz.call 强行将 baz 的 this 指向了 obj, 所以此时 baz 中的 this 是 obj;
应用场景: 带参数的
- var a = 2;
- function baz(p1, p2) {
- console.log(this.a, p1, p2);
- };
- var obj = {
- a: 3
- };
- var foo = function () {
- return baz.apply(obj, arguments)
- };
- foo("hi,", "heimayu");
- // 3 hi, heimayu
4,new 绑定
在 JS 中, 我们经常使用 new 来对函数进行构造调用, 如下:
- function Foo(a) {
- this.a = a;
- };
- var foo = new Foo(3);
- console.log("a", foo.a)
这里, 使用 new Foo() 的时候构造出一个新的对象 foo 并把它绑定到 Foo 调用中的 this 上.
5, 优先级
在大多数情况下我们找到函数的调用位置, 并判断应用哪种规则, 就可以快速找到 this; 结论:
new 绑定 > 显式绑定 (call,apply)> 隐式绑定 > 全局默认绑定
6, 一些例外
规则是死的, 程序是活的, 总有例外出现. 比如啊:
我不关心 this 是什么
- function foo(a, b) {
- console.log("a:" + a, "b:" + b);
- };
- foo.apply(null, [1, 2]);
- // a:1 b:2
ES6 箭头函数
箭头函数不使用上面的规则, 而是根据 由外层的作用域来决定它的 this
- function foo() {
- // return function () {
- // console.log('第一种情况', this.a)
- // }
- return (a)=>{
- console.log('第二种情况',this.a)
- }
- };
- var obj1 = {
- a: 2
- };
- var obj2 = {
- a: 3
- };
- var bar = foo.call(obj1);
- bar.call(obj2);
- // 第一种情况: 3
- // 第二种情况: 2
记住箭头函数的绑定无法被修改!!! 因此在执行 bar 的时调用了 foo, 而 foo 中的 this 是指向到 obj1 的.
最常见的场景:
- var a = 1;
- var obj = {
- a:2,
- foo: function(){
- setTimeout(function(){
- console.log(this.a)
- },100)
- }
- };
- obj.foo();
- // 1
在 ES5 中, 我们想打出的值是 2, 会这样做:
- var a = 1;
- var obj = {
- a:2,
- foo: function(){
- let self = this;
- setTimeout(function(){
- console.log(self.a)
- },100)
- }
- };
- obj.foo();
在 ES6 中, 则可以直接这样:
- var a = 1;
- var obj = {
- a:2,
- foo: function(){
- setTimeout(()=>{
- console.log(this.a)
- },100)
- }
- };
- obj.foo();
理解一下: 因为箭头函数中的 this, 指向了 foo,foo 的上下文是 obj. 具体的说: 箭头函数会继承外层函数调用的 this 绑定.
来源: https://www.cnblogs.com/webhmy/p/10212597.html