this 是 JavaScript 语言中的一个关键字,它引用的是函数被调用时的上下文对象。this 的具体指向取决于函数如何被调用,也就是说,它的值是在运行时动态绑定的(谁调用this就指向谁),而非在编写时确定(箭头函数除外)。
- 作为普通函数去调用 (返回window)
- 使用call apply bind 被调用(传入什么,就返回什么)
- 作为对象方法被调用 (返回对象的本身)
- 在class方法中被调用 (当前实例的本身)
- 在箭头函数中被调用 (永远找它上级作用域的this取值)
1.this的场景
(1)全局上下文:
在全局作用域中,this 指向的是全局对象。在浏览器环境中,全局对象通常是 window。
console.log(this); // window
(2)函数作为对象方法调用:
当函数作为某个对象的方法被调用时,this 指向调用该方法的对象。
var obj = {
name: 'Alice',
sayHello: function() {
console.log('Hello, ' + this.name);
}
};
obj.sayHello(); // 输出 "Hello, Alice"
(3)构造函数:
当函数作为构造函数使用(即使用 new 关键字调用)时,this 指向新创建的对象实例。
function Person(name) {
this.name = name; //相当于let obj=new Object();obj.name=name; return obj;
}
var alice = new Person('Alice');
console.log(alice.name); // 输出 "Alice"
(4)函数作为普通函数调用:
如果函数并非作为对象的方法或构造函数调用,而是直接调用,那么 this 通常会指向全局对象(在浏览器中是 window)。
(5)在类(class)中:
在类(class)中,this 关键字引用的是类的当前实例。在类的方法中,你可以使用 this 来访问类的属性和方 法。
class Person {
constructor(name, age) {
// 在构造函数中,this 指向新创建的 Person 实例
this.name = name;
this.age = age;
}
introduce() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
grow() {
this.age++;
this.introduce(); // 调用 introduce 方法
}
}
// 创建一个 Person 实例
const alice = new Person('Alice', 25);
// 调用实例方法
alice.introduce(); // 输出 "Hello, my name is Alice and I am 25 years old."
// 调用另一个实例方法
alice.grow(); // 输出 "Hello, my name is Alice and I am 26 years old."
(6)箭头函数中的 this
箭头函数不绑定自己的 this,它会捕获其所在上下文代码块的 this 值,作为自己的 this 值。这意味着在箭头函数中,this 的值将始终保持不变,无论该函数如何被调用.
function outerFunction() {
this.value = 42; //this是window
var innerFunction = () => {
console.log(this.value); //捕获其所在上下文的 this 值,作为自己的 this 值,所以this是window
};
innerFunction();
}
outerFunction(); // 输出 42
var x=11
var obj={
x:22,
say:()=>{
console.log(this.x) //捕获obj对象的this,obj对象的this是window
}
}
window.obj.say() //输出11
var x=11
var obj={
x:22,
say:function(){
const fn=()=>{
console.log(this.x) //捕获function函数的this作为自己的this,function函数this是obj
}
fn()
}
}
window.obj.say() //输出22
2.改变this(call 、 apply 和 bind 方法)
call、apply和bind都是JavaScript中用于改变函数执行上下文(也就是this的值)的方法,但它们在使用方式和行为上有一些重要的区别。
call使用场景常见的有:改变this指向,复杂数据类型的判断Object.prototype.toString.call({a:1}),js继承,构造函数继承,es5把伪数组转成数组Array.prototype.slice.call(arguments)
(1)参数传递方式:
-
- call方法接受一个对象作为this的值,然后是一个参数列表。例如:func.call(obj, arg1, arg2, ...)。
- apply方法也接受一个对象作为this的值,但它的第二个参数是一个参数数组或类数组对象。例如:func.apply(obj, [arg1, arg2, ...])。
- bind方法接受一个对象作为this的值,以及一个参数列表,但它返回一个新的函数,而不是立即执行。这个新函数在被调用时,会使用bind方法指定的this值和参数。例如:func.bind(obj, arg1, arg2, ...)。
(2)执行时机:
-
- call和apply都是立即执行函数,调用它们会立即执行对应的函数。
- bind方法不会立即执行函数,而是返回一个新的函数。这个新的函数在被调用时,才会执行原函数,并且this的值会被设置为bind方法指定的值。
(3)返回值:
-
- call和apply调用原函数后,会返回原函数的返回值。
- bind方法返回一个新的函数,这个新函数在被调用时,会使用bind方法指定的this值和参数来调用原函数,并返回原函数的返回值。
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
var obj = {name: 'Alice'};
// 使用 call 方法
greet.call(obj, 'Hello', '!'); // 输出 "Hello, Alice!"
// 使用 apply 方法
greet.apply(obj, ['Hello', '!']); // 输出 "Hello, Alice!"
// 使用 bind 方法
var greetAlice = greet.bind(obj, 'Hello', '!');
greetAlice(); // 输出 "Hello, Alice!"
3.手写实现call 、 apply 和 bind 方法
(1)手写实现call:
var person={
getName(a,b){
console.log('getName:::this',this)
return this.name
}
}
const man={
name:'奶茶不加冰七分甜'
}
Function.prototype.myCall=function(content){
if(typeof this!=='function'){
throw new Error('this is not a function')
}
const arr=[...arguments].slice(1)
content.fn= this //该this是getName函数
const res=content.fn(...arr) //该this是getName函数
delete content.fn
return res
}
const res=person.getName.myCall(man,'奶茶只喝常温','666')
(2)手写实现apply:
var person={
getName(a,b){
console.log('getName:::this',this)
return this.name
}
}
const man={
name:'奶茶不加冰七分甜'
}
Function.prototype.myApply=function(content){
if(typeof this!=='function'){
throw new Error('this is not a function')
}
content.fn= this //该this是getName函数
let res=null
if(arguments[1]){
res=content.fn(...arguments[1])
}else{
res=content.fn()
}
delete content.fn
return res
}
const res=person.getName.myApply(man,['奶茶只喝常温','666'])
(3)手写实现bind:
var person={
getName(a,b){
console.log('getName:::this',this)
return this.name
}
}
const man={
name:'奶茶不加冰七分甜'
}
Function.prototype.myBind=function(content){
if(typeof this!=='function'){
throw new Error('this is not a function')
}
const arr=[...arguments].slice(1)
const fn= this //该this是getName函数
return function(){
return fn.call(content,...arr)
}
}
let Fn=person.getName.myBind(man,'奶茶只喝常温','666')
const res=Fn()
console.log(res) //'奶茶不加冰七分甜'
4.this 大厂实战题:
var name=222
var a={
name:111,
say:function(){
console.log(this.name)
}
}
var fun=a.say
fun() //222 this指向window
a.say() //111 this指向a
var b={
name:333,
say:function(fun) {
fun()
}
}
b.say(a.say) //222 this指向window
b.say=a.say
b.say() //333 this指向b