this问题

9 阅读5分钟

整体情况

  1. 全局环境下指向window。严格模式下需要严格写清函数的调用环境
  2. 隐式绑定,谁调用指向谁,链式调用指向最后一个调用的。引用类型都可以做调用
  3. 显示绑定,call/apply/bind可以改变this指向
  4. new绑定,this指向由构造函数改为新建的对象
  5. 箭头函数,没有this,向外层找,看外层的this

注意点

做完题再回来看,理解了就可以了

  1. 箭头函数没有this,去它的上一层找,不在乎是谁调用
  2. 箭头函数去上一层找this时,在执行前就已经确定,不会在调用后更改,比如通过call调用不会改变
  3. {}不构成独立作用域
  4. 引用类型都可以作为调用者
  5. 匿名函数独立调用,指向window
  6. let声明的变量不会成为window的属性
  7. fn.call(null) 传null指向window
  8. function声明的提升优先级高于 var声明的变量
  9. 如果同一作用域中存在同名的 function和 var,函数的声明会覆盖变量的声明
  10. var只提升声明,不提升赋值​
  11. 全局作用域this指向window
  12. setTimeout的回调函数是被 JavaScript 引擎直接调用的,独立调用的。匿名函数this指向window,箭头函数看外层。
  13. 立即执行函数(IIFE)是独立调用的​​,没有绑定到任何对象。​普通函数在独立调用时,this默认指向全局对象(window)​,在严格模式下,独立调用的函数 this是 undefined
  14. 赋值语句返回等号后面部分,调用为独立调用

做一做

  1. 数组用索引获取后直接调用(引用类型调用,指向这个引用类型)
 let length = 10
 function fn() {
   console.error(this.length)
 }
 const arr = [fn, 10, 20]
 arr[0]()
 const fn2 = arr[0]
 fn2()

let声明的变量不会成为window的属性 window.length = 0

  1. 箭头函数的this,看所在上下文中的this
 const obj = {
   name: 'obj',
   showThis: function() {
     console.log(this);
   }
 }
 obj.showThis()
 ​
 const obj1 = {
   name: 'obj1',
   showName: function() {
     console.log('111', this)
     let innerFunc = () => {
         console.log('222', this.name);
     };
     innerFunc();
   }
 };
 obj1.showName();
  1. 3.1
 function showTime() {
   console.log(new Date().getTime(), this);
 }
 var id = 1001;
 var name = "宁王";
 let jsonObj = {
   id: 9527,
   name: "华安",
   showAge: function () {
       console.log("年方18", this);
       console.log(this.id, this.name);
   }
 };
 showTime = jsonObj.showAge;
 showTime();

3.2

 function showTime() {
   console.log(new Date().getTime(), this);
 }
 var id = 1001;
 var name = "宁王";
 let jsonObj = {
   id: 9527,
   name: "华安",
   showAge: function () {
       console.log("年方18", this);
       console.log(this.id, this.name);
   }
 };
 ​
 jsonObj.showAge = showTime;
 jsonObj.showAge();

3.3 引用类型都可以作为调用者

 var id = 1001;
 var name = "宁王";
 let jsonObj = {
     id: 9527,
     name: "华安",
     tiger: {
         id: 8866,
         name: "江小白",
         showAge: function () {
             console.log(this);
             console.log(this.id, this.name);
         }
     }
 };
 jsonObj.tiger.showAge();
 function showTime1(X) {
     console.log("showTime1+" + X, this);
 }
 function showTime2() {
     this.showTime1(130);
 }
 showTime1(110);
 window.showTime1(120);
 showTime2();
 var var_id = 110;
 var var_fun = function (param) {
     console.log("var_fun", this.var_id, param);
 };
 let let_fun = function () {
     console.log("let_fun", this.var_id, this);
 };
 var_fun(1);
 window.var_fun(2);
 let_fun();

let 定义的函数作用域是 Script,并不会提升到顶层对象 window 下去,但是在定义位置后面都可以使用此函数,包括后面引入的script文件中也照样可以调用 let_fun不提升为window的属性,但是直接调用它,他内部的this还是window,可以这样理解,全局环境下this指向window

  1. new关键字将构造函数的this指向新建的对象
 function Tiger() {
   this.id = 9527;
   this.name = "华安";
   console.log("构造器执行", this.id);
 ​
   this.showAge = function () {
       console.log(this.name + " 今年 28 岁.");
   }
 }
 let tiger = new Tiger();
 console.log(tiger);
 console.log(tiger.id, tiger.name);
 tiger.showAge();
  1. call 7.1
var id = 120;
var name = "华太师";
let json = {
    id: 110,
    name: "华安"
};
function showTime() {
    console.log(this);
    console.log(this.id, this.name);
}
showTime();
showTime.call(json);

7.2

function fun() {
  console.log(this.age);
}
var person = {
  age: 20,
  fun
}
var age = 28;
var fun = person.fun;
fun.call(person);
fun.apply(person);
fun.bind(person)();
fun.call(null);

传null指向window function声明的提升优先级高于 var声明的变量 var只提升声明,不提升赋值​ 如果同一作用域中存在同名的 function和 var,函数的声明会覆盖变量的声明

const showInfo2 = () => {
  console.log(this);
}
var Tiger = function () {
  this.id = 110;
  this.name = "宁王";
  /**Dog 是一个 Json对象*/
  this.Dog = {
    id: 120,
    name: "华太师",
    /**非箭头函数写法*/
    showInfo1: function () {
      console.log(this.id, this.name);
    },
    /**箭头函数写法*/
    showInfo2: () => {
      console.log(this);
    }
  }
};
/**这种写法已经说过了,调用 showInfo1 函数的是 Dog 对象,里面的 this 就表示 Dog 对象*/
new Tiger().Dog.showInfo1();
/**虽然调用 showInfo2 函数的是 Dog 对象,但是箭头函数会往外抛一层,里面的 this 表示的是 Tiger*/
new Tiger().Dog.showInfo2();
  1. 9.1
const obj ={name:'xiaoming'};
function fn(){
    console.log(this);
    return ()=>{
        console.log(this);
    }
}
fn.call(obj)()
fn()()

9.2 {}​​不构成单独的作用域

const obj = {
  name: 'obj',
  obj3: {
    name: 'obj3',
    foo: () => {
      console.log(this)
    }
  }
}
obj.obj3.foo()

箭头函数没有自己的 this,它继承的是​​定义时所在作用域的 this​​(即“词法作用域”),不在乎谁调用它 foo定义在 obj3对象中,但对象字面量 {}​​不构成单独的作用域​​,因此 foo的外层作用域实际上是​​全局作用域​​(即 window或 global)。 9.3 同上,{}并不是一个作用域

const obj =  {
  name: 'obj',
  obj3: {
    name: 'obj3',
    foo: function () {
      console.error(this)
      return {
        name: 'obj4',
        foo: () => {
          console.log(this)
        }
      }
    }
  }
}
obj.obj3.foo().foo()
var btn = document.querySelector('button');
btn.onclick = function () {
  console.log(this);  // btn <button>点击</button>
}
setTimeout(function(){
  console.log(this);
}, 2000); //this指向window

setTimeout(() => {
  console.log(this); // 取决于外层 this(可能是 window 或某个对象)
}, 2000);

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

setTimeout的回调函数是被 JavaScript 引擎直接调用的,独立调用的 立即执行函数(IIFE)是独立调用的​​,没有绑定到任何对象。​普通函数在独立调用时,this默认指向全局对象(window)​,在严格模式下,独立调用的函数 this是 undefined

  1. 赋值语句
function foo() {
  console.error(this)
}
const obj1 = {
  name: 'obj1',
  foo
}
const obj2 = {
  name: 'obj2'
}
console.log((obj2.foo = obj1.foo));
(obj2.foo = obj1.foo)()

赋值语句会把右边的结果返回,调用就是独立调用,所以是window

  1. 13.1
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
person1.foo1.call(person2) // person2

person1.foo2() // window
person1.foo2.call(person2) // window

person1.foo3()() // window

person1.foo3.call(person2)() // window

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

person1.foo4()() // person1

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

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

13.2

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
person1.foo1.call(person2) // person2

person1.foo2() // person1   person1隐式绑定
person1.foo2.call(person2) // person1

person1.foo3()() // window

person1.foo3.call(person2)() // window

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

person1.foo4()() // person1

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

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

13.3

 var name = 'window'
 function Person(name) {
   this.name = name
   this.obj = {
     name: 'obj1',
     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()() // window
 ​
 person1.obj.foo1.call(person2)() // window
 ​
 person1.obj.foo1().call(person2) // person2
 ​
 person1.obj.foo2()() // obj1
 ​
 person1.obj.foo2.call(person2)() // person2
 ​
 person1.obj.foo2().call(person2) // obj1