搞定面试——this 指向

167 阅读3分钟

不同环境下的不同指向

  • 透彻认识 functionthis 在不同的调用环境下的指向
    • 事件调用环境
    • 全局环境
    • 函数内部
  • 箭头函数中的 this 指向

事件调用环境

定义一个块元素 点击触发时向右移动100px

  <body>
    <div id="box"></div>
  </body>
  <script>
    box.onclick = function() {
      this.style.left = "100px";
    };
  </script>

函数中的 this 为触发事件的元素

全局环境(浏览器)

全局环境(浏览器) this 指向 window

函数内部

"use strict";
function add() {
  console.log(this);
}
// 非严格模式下 调用定义的函数  函数内部的 this 指向 window
// 严格模式下 调用定义的函数 函数内部的 this 指向 undefined
add(); // undefined
// 严格模式下 调用 window.fn() 可以使函数内部的 this 指向 window 
window.add(); // window
"use strict";
let obj = {
  a: 10,
  fn() {
    console.log(this.a);
  },
  b: {
    fn() {
      console.log(this.a);
    }
  }
};
// 最终指向的是调用它的对象
obj.fn(); // 10
// 多层调用 指向的也只是上一层级的对象
obj.b.fn(); // undefined

// 此时调用fn方法的是 newObj 。严格模式下 this 指向的是 undefined
let newObj = obj.fn;
newObj(); // 报错

function Add(x) {
  this.x = x;
  console.log(this);
}
// 调用 new 大概进行的操作
// 1:调用函数 2:创建对象 3:将对象与this绑定 4:如果没有定义返回值 隐式返回this对象
let add = new Add(5); // {x: 5}
"use strict";
function Add(x) {
  this.x = x;
  console.log(this);
  // 基础类型的都不会有影响 
  // null 特殊对象也不会有影响
  return null;
}
let add = new Add(5); // {x: 5}
console.log(add); // {x: 5}
"use strict";
function Add(x) {
  this.x = x;
  console.log(this);
  // 返回的是对象时 返回值为定义的对象值
  return [];
}
let add = new Add(5); // {x: 5}
console.log(add); // []

结论:

  • this 最终指向的是调用它的对象

  • 函数如果被多成对象包含,如果被函数最外层调用,this 指向的也只是上一层级的对象

  • 构造函数中的 this 指向的是实例对象,如果构造函数返回的不是对象,this 保持不变 否则会被更改。 null 也属于对象 但不是有影响

箭头函数中的this

回到最初的例子 点击块元素 延时一秒执行函数

"use strict";
box.onclick = function() {
  // 此时的写法是不对的 因为此时函数内的 this 指向为 window
  setTimeout(function() {
    console.log(this); // window
    this.style.left = "100px";
  }, 1000);
  // 相当于 setTimeout(window.fn, 1000);
};

箭头函数绑定的是定义时上一层作用域的 this

box.onclick = function() {
  // 箭头函数绑定的是定义时的上一层作用域
  setTimeout(() => {
    console.log(this);
    this.style.left = "100px";
  }, 1000);
};
"use strict";
let obj = {
  fn: () => {
    console.log(this);
  },
  fn2() {
    console.log(this);
  }
};
// 绑定的是上一层作用域 但对象不能形成独立的作用域
obj.fn(); // window
obj.fn2(); // obj 对象
  • 箭头函数本身是没有 this 和 arguments 的,在箭头函数中引用的 this 实际上调用的是定义时的上一层的作用域的 this
  • 这里强调一下,对象是不能形成独立的作用域的

修改 this 指向

applycallbind

"use strict";
let person = {
  age: 18
};
let obj = {
  fn: (x, y) => {
    console.log(this.age, x, y);
  },
  fn2(x, y) {
    console.log(this.age, x, y);
  }
};
// 使用箭头函数 this 绑定的是定义时的作用域 所以无法修改
obj.fn.call(person, 1, 2); // undefined 1 2
// 调用 call 方法修改
obj.fn2.call(person, 1, 2); // 18 1 2
// 调用 apply 方法修改 参数为数组
obj.fn2.apply(person, [1, 2]); // 18 1 2
// 调用 bind 方法修改  bind 返回的是一个函数
obj.fn2.bind(person)(1, 2); // 18 1 2