this指针详解

148 阅读5分钟

this指针详解

JavaScript 中的 this 指向非常灵活,它可以根据函数的调用方式和所处的环境不同而发生变化。

在 JavaScript 中,this 关键字表示当前函数执行的环境。当一个函数被调用时,JavaScript 引擎会为该函数创建一个执行环境,其中包括了函数内部的变量和对象等。而 this 关键字则用来指代当前执行环境的上下文。

例如,当我们在一个函数内部使用 this 关键字时,它会指向当前函数执行的环境,比如一个 DOM 元素、一个全局对象或者是一个函数的实例等。如果我们想要在函数内部访问外部的变量或对象,就需要通过 this 关键字来引用它们。

此外,还有一些其他的与 this 相关的关键字,如 call()、apply()、bind() 等。它们可以用来改变函数执行环境、实现函数的高阶调用、绑定函数的上下文等。 context(上下文环境,webpack 钩子,compiler 对象,compilation 对象始终贯穿于整个钩子的)。

this 永远指向一个对象;

this 的指向取决于函数调用的位置,或通过 call、apply、bind 修改;**

this 指向跟调用有关,跟定义无关



下面我们具体介绍了 this 在不同场景下的指向:

全局作用域下的this

例如以下问题:

//demo1
function fun(){
    console.log(this.s);
}

var obj = {
    s:'1',
    f:fun
}

var s = '2';

obj.f(); //1
fun(); //2
//demo2
var btn = document.getElementById('btn');
btn.onclick = function(){
    this ;  // this指向本节点对象
}
//demo3
var obj = {
    fun:function(){
        this ;
    }
}

setInterval(obj.fun,1000);      // this指向window对象
setInterval(obj.fun(),1000);  // this指向obj对象

全局作用域

全局作用域指的是没有包含在任何函数中的代码块。如果在全局作用域中使用 this,它的指向就是浏览器的 Window 对象(在 Node.js 中则是 Global 对象)。

例如,下面的代码段中,this 指向全局对象 Window:

console.log(this); // Window

对象方法中的 this

在 JavaScript 中,一个对象可以包含多个方法。如果在对象的方法中使用 this,它的指向就是该对象本身。 例如,下面的代码段中,this 指向 obj 对象:

let obj = {
    name: 'John',
    sayName() { console.log(this.name); }
}

obj.sayName(); // John

构造函数中的 this

function Person(name, age) {
    this.name = name;
    this.age = age;
}

let p1 = new Person('Tom', 25);
console.log(p1); // Person {name: "Tom", age: 25}

call 和 apply 方法中的 this

JavaScript 中的函数对象具有 call 和 apply 两种方法。这两种方法允许开发者在调用函数时自定义 this 的值。

let foo = {
    value: 1
}

function bar() {
    console.log(this.value)
}

bar.call(foo)//1

手写call 最终完整版

Function.prototype.myCall = function(context, ...args){
    if(typeof context==='undefind' || context==null){
        context= window
    } //等同于 var context = context||window;

    let fnSymbol = Symbol();//每个从 Symbol() 返回的 symbol 值都是唯一的
    context[fnSymbol] = this;

    let result = context[fnSymbol](...args);//入参...args === [...arguments].slice(1);

    delete context[fnSymbol];//删除目的是不污染原来数据

    return result;
}

手写apply 最终完整版(与call不同的是:args参数是一个数组)

Function.prototype.myApply = function(context, args) {
    if(typeof context ==='undefind' || context === null) {
        context = window
    }

    let fnSymbol = Symbol();
    let fn = context[fnSymbol](...args);

    delete context[fnSymbol];

    return fn;
}

手写bind 最终完整版(返回一个函数,不会执行)

1.改变this的指向为context

2.返回一个函数

Function.prototype.myBind = function(context, ...args1) {
    var _this = thisvar fnTemp = new Function(){};

    const fBound = return function(...args2) {
        return _this.apply( this typeof fnTemp ? this :context, [...args, ...args2])
    }

    fnTemp.prototype = this.prototype;

    fBound.prototype = new fnTemp();

    return fBound;
}

模拟new 最终完整版

1.返回对象

2.访问构造函数上的属性

3.访问原型(prototype)上的属性

function objectFactory() {
    var obj = new Object(),
    var Constructor = [].shift.call(arguments);
    obj.__proto__ = Constructor.prototype;
    var result = Constructor.apply(obj, arguments);

    return typeof ret === 'object' ? result : obj;

};

箭头函数中的 this

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

例如,在下面的代码段中,this 指向 obj:


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

obj.func(); // jinjin

函数作为参数和返回值时的 this

JavaScript 中的函数可以被传递给其他函数作为参数或返回值。如果一个函数作为参数被传递给另一个函数,那么其中的 this 指向可能会发生变化。这在很多框架中处理链式调用时常用。

例如,在下面的代码段中,this 指向全局对象 Window 而不是 obj:

let obj = { name: 'jinjin' };

function func2() {
    console.log(this.name);
}

function func1() {
    func2();
}

func1.call(obj); // undefined

同样地,在下面的代码段中,this 也不会指向 obj:

let obj = { name: 'Lily' };

function func2() {
    console.log(this.name);
}

function func1() {
    return func2;
}

let f = func1(); 
f(); // undefined

事件绑定中的 this

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

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

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

this 可能出现的坑点

由于 JavaScript 中的 this 指向比较灵活,所以在实际开发中,可能会出现一些常见的坑点。

例如,在下面的代码段中,this 指向全局对象 Window 而不是 obj:

let obj = { name: 'jinjin' };

document.querySelector('#btn').addEventListener('click', function() {
    console.log(this.name);
});

事件绑定时的 this 实际上是指向触发事件的元素,而不是 obj。

同样地,在下面的代码段中,this 也不会指向 obj:

let obj = { name: 'jinjin' };

setTimeout(function() {
    console.log(this.name);
}, 1000);

由于 setTimeout 不属于任何对象,所以 this 的指向默认为全局对象 Window。

按绑定形式分类

this 绑定/指向方式总结为三点:

  1. 默认绑定:全局作用域下的 this,正常书写代码,this 指向处理

  2. 隐式绑定:this 取决于当前执行上下文环境

  3. 显式绑定:通过 call、apply、bind 改变 this 指向

this 与作用域相关问题面试常问的

  1. 谈谈你对 this 指向问题的理解

  2. 箭头函数的一些特性

  3. 手写实现 call、apply、bind 函数

  4. new 创建一个对象时,做了哪些事情