第一种:事件绑定

给元素的某个事件行为绑定方法,事件触发,方法执行,此时方法中的 this 一般都是当前元素本身;

DOM0 级事件

// DOM0
btn.onclick = function anonymous() {
  console.log(this); // 元素
};

DOM2 级事件

// => DOM2:不兼容 IE6 7 8
btn.addEventListener('click', function anonymous() {
  console.log(this);  // 元素
}, false);

btn.attachEvent('onclick', function anonymous() {
  // IE8 浏览器中的 DOM2 事件绑定
  console.log(this); // window
});

第二种:上下文对象调用

普通函数执行,它里面的 this 取决于方法执行前面是否有 “点”,有 “点” 前面是谁 this 就是谁,没有则指向 window (严格模式下是 undefinde

function fn() {
    console.log(this);
}

let obj = { name: 'OBJ', fn: fn };

fn(); // window
obj.fn(); // { name: 'OBJ', fn: [Function: fn] }

第三种:构造函数执行

构造函数执行 new xxx,函数中的 this 是当前类的实例

function Fn() {
  console.log(this);
  // this.xxx = xxx 是给当前实例设置私有属性
}
let f = new Fn;

第四种:箭头函数

  1. 箭头函数中没有自身的 this ,箭头函数的 this 指向始终为外层的作用域;

  2. 箭头函数没有的东西很多:

    1. 没有 prototype (也就是没有构造器),所以不能被 new 执行;
    2. 没有 arguments 实参集合 (可以基于 …args 剩余运算符获取)
JavaScript
JavaScript
JavaScript
let obj = {
  name: 'OBJ',
  fn: function () {
    // console.log(this); //=>obj
    let _this = this;
    return function () {
      // console.log(this); //=>window
      _this.name = "旺旺";
    };
  }
};

let ff = obj.fn();
ff();
console.log(obj.name); // 旺旺
let obj = {
  name: 'OBJ',
  fn: function () {
    // console.log(this); //=>obj
    return () => {
      // console.log(this); //=>obj
      this.name = "旺旺";
    };
  }
};

let ff = obj.fn();
ff();
console.log(obj.name); // 旺旺
let obj = {
  name: 'OBJ',
  fn: function () {
    setTimeout(_ => {
      // console.log(this); //=>obj
      this.name = "旺旺";
    }, 10);
  }
};
obj.fn();
console.log(obj.name); // OBJ

第五种:API 改变 this 指向

  1. 基于 call/apply/bind 可以改变函数中 this 的指向(强行改变),callapplybindFunction.prototype 上的方法;

  2. func.call(context, 10, 20)context 为改变的 this 指向(非严格模式下,传递 null/undefined 指向的也是 window);

call

Function.prototype.call = function call(context = window, ...args) {
    context === null ? context = window : null;

    //=> 此处 this 为 apply 前的函数,也就是调用 apply 的函数
    //=> 把函数赋值给需要指定 this 的对象 context
    context.$fn = this;
    let result = context.$fn(...args); // 这时函数执行 this 就变成了 context

    delete context.$fn;
    return result;
}

apply

Function.prototype.apply = function apply(context = window, args) {
    context === null ? context = window : null;

    //=> 此处 this 为 apply 前的函数,也就是调用 apply 的函数
    //=> 把函数赋值给需要指定 this 的对象 context
    context.$fn = this;
    let result = context.$fn(...args); // 这时函数执行 this 就变成了 context

    delete context.$fn;
    return result;
}

bind

bind 不是立即执行函数,属于预先改变 this 和传递一些内容

// es5 版本
Function.prototype.bind = function bind(context) {
  context = context || window;
  // 获取传递的实参集合
  var args = [].slice.call(arguments, 1);
  var _this = this;

  // 需要最终执行的函数
  return function anonymous() {
    // 此函数的参数
    var amArg = [].slice.call(arguments, 0);
    _this.apply(context, args.concat(amArg));
  };
}

// es6 版本
Function.prototype.bind = function bind(context = window, ...args) {
  // 经过测试:apply 的性能不如 call(3 个参数以上)
  return (...amArg) => this.call(context, ...args.concat(...amArg));
}

面试题

阿里原题

function fn1() {
  console.log(1);
}
function fn2() {
  console.log(2);
}

// fn2.$fn = fn1;
// fn2.$fn() => fn2.fn1();this 指向 fn2,执行 fn1,输出 1
fn1.call(fn2); // 1

// 把 fn1.call 看成 AF0,AF0.call(fn2)
// fn2.$fn() => fn2.AF0(); fn1.call 和 fn1.call.call 都是同一个 call 函数
// 则 AF0 可以看成 call 函数,即 fn2.call 执行,this 为 window,输出 2
fn1.call.call(fn2); // 2

// 同上
Function.prototype.call.call(fn2); // 2
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 第一种:事件绑定
    1. 1.1. DOM0 级事件
    2. 1.2. DOM2 级事件
  2. 2. 第二种:上下文对象调用
  3. 3. 第三种:构造函数执行
  4. 4. 第四种:箭头函数
  5. 5. 第五种:API 改变 this 指向
    1. 5.1. call
    2. 5.2. apply
    3. 5.3. bind
  6. 6. 面试题
    1. 6.1. 阿里原题