JavaScript中的this

本文深入解析JavaScript中的this关键字,涵盖其在不同环境下的行为特点,包括全局对象、构造函数、对象方法、DOM事件等场景,并介绍了如何使用call、apply、bind来改变this的指向。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行环境

JavaScript代码主要有两种运行环境,一种是常用的浏览器环境,还有一种就是node环境,本文中如无特殊说明,均指浏览器环境。

this的含义

首先,this关键字总是返回一个对象。
其次,this的指向是动态的,即this的指向是可以发生变化的,this对象是在运行时基于函数的执行环境绑定的,即只有在运行的时候才能知道this的值(bind函数除外)。

this的使用场景

this的使用场景主要有以下这五个使用场景。

  • 全局对象
  • 构造函数
  • 对象的方法
  • DOM事件
  • call/apply/bind

下面,我将详细阐述这五个使用场景。
其中,call/apply/bind单独作为一个单元来讲。

1. 全局对象中的this

在全局环境中使用this,无论是直接运行还是在函数内部运行,它指向的就是顶层对象window。

this === window; // true
(function(){console.log(this === window)})(); // true
function f() {console.log(this === window)}; f(); // true

通过上例,我们可以看到在浏览器上,以上三段代码均返回true。但在node中并不完全一致。

// 以下代码请在node环境中执行
console.log(this === global); // false
(function(){console.log(this === global)})(); // true
function f() {console.log(this === global)}; f(); // true

在node环境下,只有在函数中,this 才会指向顶层对象 global。

2. 构造函数中的this

1.构造函数中的 this 指向实例对象。

function Person(name) {
    this.name = name;
}
Person.prototype.sayName = function() {
    return this.name;
}
var p = new Person('kylin');
console.log(p.name); // kylin
p.sayName(); // kylin

2.原型继承中的this

var Parent = function() {
    this.get = function() {
        return this;
    }
}
var Son = function() {
}
Son.prototype = new Parent();
var son = new Son();
son.get(); // son{}

这个例子也说明了,this 对象和调用方法的对象有关,即使在继承中也不例外。

3. 对象方法中的this

1.直接使用对象调用对象中的方法,其方法中的 this 指向该对象。

var person = {
    name: 'kylin',
    getName: function() {
        return this.name;
    }
}
person.getName(); // kylin

2.需要注意的是,当A对象的方法被赋值给了B对象时,方法中的 this 指向就从A对象变成了B对象。

var personA = {
    name: 'kylin',
    getName: function() {
        return this.name;
    }
}
var personB = {
    name: 'lover'
}
personB.getName = personA.getName;
personB.getName(); // lover

3.如果某个方法位于多层对象的内层,这时 this 也只会指向当前这一层的对象,而不会继承更上面的层的对象。

var outerObj = {
    a: 'hello',
    b: {
        // a: 'world',
        c: function() {
            return this.a;
        }
    }
};
outerObj.b.c(); // undefined

如果将注释掉的内容恢复,就会返回 world。

4.如果将对象的方法赋值给一个变量,那么 this 指向会变成 window 对象。

var name = 'the window';
var obj = {
    name: 'kylin',
    getName: function() {
        return this.name;
    }
}
var f = obj.getName;
f(); // the window

5.方法内部定义的函数,一般而言其执行环境具有全局性,即 this 指向顶层对象window。

var name = 'the window';
var obj = {
    name: 'my ojb',
    getNameFunc: function() {
        return function() {
            console.log(this.name); // the window
        }
    }
};
obj.getNameFunc()();

var obj2 = {
    name: 'my ojb',
    getNameFunc: function() {
        function f() {
            console.log(this.name); // the window
            console.log(this === window); // true
        }
        f();
    }
};
obj2.getNameFunc();

实际开发中,我们确实有在方法中定义新的函数的需求,一般而言,我们会使用闭包将 this 传入内部函数。

var name = 'the window';
var obj = {
    name: 'my ojb',
    getNameFunc: function() {
        return function f() {
            console.log(this.name); // the window
            console.log(this === window); // true
        }
    }
};
obj.getNameFunc()();

var obj2 = {
    name: 'my ojb',
    getNameFunc: function() {
        var _this = this;
        return function f() {
            console.log(_this.name); // my obj
            console.log(_this === window); // false
        }
    }
};
obj2.getNameFunc()();

6.比较特殊的,在方法中处理数组

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
  }
};

o.f();
// undefined a1
// undefined a2

与上面的示例一样,数组处理方法中的 this 也是指向顶层对象window。

7.对象属性中的this

var obj = {
    name: 'outer',
    self: this,
    getName: function() {
        console.log(this.name); // outer
        console.log(this.self === window); // true
    },
    inner: {
        name: 'inner',
        self: this,
        getName: function() {
            console.log(this.name); // inner
            console.log(this.self === window); // true
        },
    }
}
obj.getName();
obj.inner.getName();

具体JavaScript为什么这么设计,我也不太明白,有知道的同学麻烦告诉我。

8.函数数组中的this

function f1() {return this};
function f2() {return this};
var arr = [f1, f2];
arr[0](); // arr[]

其实这个并不难理解,arr数组其实就是arr对象,[]运算符可以理解为对象中的 . 运算符,只不过在对象中凡是能用 . 运算符的时候都能用 [] 运算符,但是在某些特殊情况下只能用 [] 运算符。调用可以这样理解

// 仅仅作为辅助理解作用,语法上并不合适
arr.0()

很明显,会输出 arr 对象嘛。

9.下面来看几个比较奇怪的例子

var name = 'the window';
var obj = {
    name: 'kylin',
    getName: function() {
        console.log(this.name);
    }
};
obj.getName(); // kylin
(obj.getName)(); // kylin
(obj.getName = obj.getName)(); // the window
(true && obj.getName)(); // the window
(false || obj.getName)(); // the window
(1, obj.getName)(); // the window

相信实际开发中不会有人这么写,仅作为开拓视野的部分,不再详细展开。
其基本原理是一致的,最后四个代码片段,第一个小括号最后都会返回一个函数并且在全局作用域下执行。

4. DOM事件中的this

DOM事件处理程序中的 this ,一般都是指向绑定事件的Element节点。
但里面有两个特殊的事件处理程序的 this 对象指向 window 对象。

// HTML级事件处理程序
<div id="root" onclick="showMSG()"/>
function showMSG() {
    console.log(this === window); // true
}

// IE事件处理程序
<div id="root"/>

var rootEle = document.getElementById('root');
rootEle.attachEvent('onclick', function () {
    console.log(this === window); // true
});

有对DOM事件还不太了解的同学可以看我的这篇文章DOM事件;

绑定this

1. function.prototype.call()

1.call 方法接受多个参数,其中第一个参数为要绑定的对象,其他的参数为函数调用需要的参数。

var name = 'the window';
var obj = {
    name: 'kylin',
};
var f = function() {
    console.log(this.name);
}
f(); // the window
f.call(obj); // kylin

2.call 方法中的参数,如果参数为空、null、undefined,则默认传入 window 对象。

var name = 'the window';
var obj = {
    name: 'kylin',
    getName: function() {
        console.log(this.name);
    }
};
var obj2 = {
    name: 'obj'
};
obj.getName.call(obj2); // obj
obj.getName.call(); // the window
obj.getName.call(null); // the window
obj.getName.call(undefined); // the window

3.如果 call 方法中的参数是原始值,那么将传入其包装类型。

var name = 'the window';
var obj = {
    name: 'kylin',
    getName: function() {
        console.log(this);
    }
};
var obj2 = {
    name: 'obj'
};
obj.getName.call(obj2); // obj{}
obj.getName.call(1); // Number{}
obj.getName.call('string'); // String{}
obj.getName.call(false); // Boolean{}

4.call 方法的一个经典实用场景是调用对象的原生方法。

var str = 'my str';
console.log(str.toString()); // my str
Object.prototype.toString.call(str); // [object String]

2. function.prototype.apply()

apply方法和call方法类似,唯一的不同是其接受数组作为函数执行时的参数。

var name = 'the window';
var obj = {
    name: 'kylin',
};
var f = function() {
    console.log(this.name);
}
f(); // the window
f.apply(obj); // kylin

3. function.prototype.bind()

1.bind 函数用于将函数体内的 this 绑定到某个对象上,然后返回一个新的函数。

var obj = {
    count: 0,
    inc: function() {
        console.log(++this.count);
    }
};
var f1 = obj.inc;
f1(); // NaN
var f2 = obj.inc.bind(obj);
f2(); // 1

2.bind 函数在绑定 this 的同时,还可以绑定原函数的参数。

var add = function(x, y) {
    return x * this.m + y * this.n;
}
var obj = {
    m: 2,
    n: 3
}
var newAdd = add.bind(obj, 5);
newAdd(10); // 40

3.如果 bind 函数的第一个参数为null、undefined,则默认绑定顶层对象。

var m = 5, 
    n = 10;
var add = function(x, y) {
    console.log(x * this.m + y * this.n);
}
var newAdd1 = add.bind();
newAdd1(5, 10); // 125
var newAdd2 = add.bind(null, 5);
newAdd2(10); // 125
var newAdd3 = add.bind(undefined, 5);
newAdd3(10); // 125

4.请注意,bind函数每次都返回一个新函数。

var obj = {name: 'kylin'};
var f = function() {return this}
var f1 = f.bind(obj);
var f2 = f.bind(obj);
f1 === f2; // false
f1() === f2(); // true

5. 箭头函数

一般而言,this 指向和执行时的对象有关,而和定义时的对象无关,但是对于箭头函数而言并不是这样。

var name = 'the window';
var obj = {
    name: 'kylin',
    getNameFunc: function(){
        return () => {
            return this.name;
        }
    }
};
obj.getNameFunc()(); // kylin
var f = obj.getNameFunc();
f(); // kylin

var obj2 = {
    name: 'kylin',
    getNameFunc: function(){
        return function() {
            return this.name;
        }
    }
};
obj2.getNameFunc()(); // the window
var f2 = obj2.getNameFunc();
f2(); // the window

致谢

本文主要参考了阮一峰老师的文章this 关键字和《JavaScript高级程序设计》,谨在此向阮一峰老师和JavaScript的作者译者表示感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值