this

73 阅读4分钟

this

this就是函数运行时所在的环境对象。

this的指向非常的灵活,会根据所处位置和调用的方式不同而发生变化。

this 永远指向一个对象(非严格模式);this 的指向取决于函数调用的位置,或通过 call、apply、bind 修改; this 指向跟调用有关,跟定义无关

全局作用域下的this

全局作用域下的this指向顶层globalThis,浏览器环境中实现为window

console.log(this === window && this === globalThis) //true

node环境中实现为global

console.log(this === global && this === globalThis) //true, node环境

严格模式this不同的地方

// 非严格模式
var name = 'window';
var doSth = function(){
    console.log(this.name);
}
doSth(); // 'window'

这样的情况下this指向window,因为es5中全局变量是挂载到顶层对象中的(window),如果将上述代码中的var换成let

// 非严格模式
let name2 = 'window2';
let doSth2 = function(){
    console.log(this === window);
    console.log(this.name2);
}
doSth2() // true, undefined

这样的情况下,window.name2与window.doSth2都为undefined,this指向window

在严格模式中,表现有一些不同

// 严格模式
'use strict'
var name = 'window';
var doSth = function(){
    console.log(typeof this === 'undefined');
    console.log(this.name);
}
doSth(); // true,// 报错,因为this是undefined

默认绑定,类似于

doSth.call(undefined);
doSth.apply(undefined);

下面我们分几种情况讨论this的调用

函数中this

    function fn(){
        console.log(this)  //window
    }

对象中this

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

    var obj = {};
    obj.m = test;

    obj.m(); // obj

构造函数中this

    function Test() {
      this.x = 1
      console.log(this);
    }

    var obj = new Test();
    obj.x = 1

箭头函数中的this

在 JavaScript 中,箭头函数是 ES6 新增的语法。与传统的函数不同,箭头函数中的 this 指向调用该函数的上下文环境(即当前作用域所属的对象)。 当前this指向obj

let obj = {
    name: 'Jack',
    func1: function() {
        setTimeout(() => console.log(this.name), 1000);
    }
}

obj.func1(); // Jack

事件绑定中的this

在 Web 开发中,经常需要将某个函数绑定到 DOM 元素的事件上。此时,函数中的 this 指向通常是绑定事件的元素。

例如,在下面的代码段中,this 指向 button 元素:

<button id="btn" onclick="console.log(this);">Click me</button>

this指向题目

接下来看几道题

function a(){
    function b(){
        console.log(this)
    }
    b()
}
a() //window
//this指向一个对象



const object = {
  message: 'Hello, World!',

  getMessage() {
    const message = 'Hello, Earth!';
    return this.message;
  }
};

console.log(object.getMessage()); // 输出Hello World



const object = {
  message: 'Hello, World!',

  logMessage() {
    console.log(this.message); // => undefined
  }
};

setTimeout(object.logMessage, 1000);
// setTimeout使用object.logMessage作为回调,但仍把 object.logMessage 作为常规函数而非方法调用。常规函数中this指向window

棘手的length

var length = 4;
function callback() {
  console.log(this.length); // => 4
}

const object = {
  length: 5,
  method(callback) {
    callback();
  }
};

object.method(callback, 1, 2);

使用 method() 内部的常规函数调用来调用 callback() 。因为在常规函数调用期间的 this 值等于全局对象,所以在 callback()函数中 this.length 为 window.length

调用参数

var length = 4;
function callback() {
  console.log(this.length); // 输出什么
}

const object = {
  length: 5,
  method() {
    arguments[0]();
  }
};

object.method(callback, 1, 2);

因为 arguments[0]() 是对 arguments 对象上 callback 的方法调用,所以 callback 内部的 this 等于 arguments。结果在 callback() 内部的 this.length 与 arguments.length 是相同的,都是3

call,apply,bind

用法

function fn(food, drink) {
    console.log(this.name)
    console.log(`今天吃${food},喝${drink}`)
}
const obj = {
    name: "obj"
}
fn.call(obj, "米饭", "可乐")   
fn.apply(obj, ["米饭", "可乐"])   

const fn2 = fn.bind(obj, "米饭", "可乐")
fn2()

image.png

手写实现call

Function.prototype.selfCall = function( context, ...params ) {
    
    context == null ? context = window : null
    !/^*(object|function)*$/.test( context.toString() ) ? context = Object(context) : null
    let _this = this, result = null, UNIQUE_KEY = Symbol('UNIQUE_KEY')
    context[UNIQUE_KEY] = _this
    result = context[UNIQUE_KEY](...params)
    delete context[UNIQUE_KEY]
    return result
}



// 简化版,便于理解
Function.prototype.selfCall = function (context, ...params) {
    const _this = this
    context["KEY"] = _this
    const res = context["KEY"](...params)
    delete context["KEY"]
    return res
}
  • 判断context是不是没传,这里偷了个懒,undefined == null是true
  • 判断context是不是引用数据类型的,不是的话转成引用数据类型
  • 保留this指向,创建一个 Symbol key,不乱写一个是怕自己定义的与context上的冲突
  • 把当前函数放到context中,即改变等会函数执行的this指向
  • 执行函数,注意📢,这里函数执行是通过context.UNIQUE_KEY()执行的,也就是函数的this指向context,拿到结果
  • 返回结果
// bind:
Function.prototype.myBind = function (context, ...params) {
    let _this = this
    return function(...arg){
        _this.selfCall(context, ...params, ...arg)
    }
}

this绑定

默认绑定

就是我们正常书写代码,this 的指向处理(默认绑定作用于函数直接调用的情况下,此时this指向全局对象,但严格模式下this指向undefined。)

隐式绑定

在函数调用时机,this 的指向取决于当前上下文环境

显示绑定

通过使用 apply、call、bind 函数进行处理

this绑定当然也有说四种,把new绑定单独列出来,也有说五种的,单独把箭头函数列出来。分类本身不绝对,重要的是清楚this怎么绑定的。