🔥🔥🔥JavaScript彻底搞懂this指向

在 JavaScript 中,关于 this 的指向是一个常见的问题。this 关键字表示当前执行代码的上下文对象,它的值取决于函数的调用方式。以下是一些常见情况和 this 的指向:

默认绑定:如果函数没有使用任何上下文对象进行调用,即独立函数调用,this 默认绑定到全局对象(在浏览器环境下是 window 对象,Node.js 环境下是 global 对象)。在严格模式下,this 将绑定为 undefined

js 复制代码
console.log(this); // 在全局作用域中打印 this,指向全局对象
//案例一
function myFunction() {
console.log(this)
}
myFunction(); // 指向全局对象
//案例二
function foo1() {
  console.log(this)
}
function foo2() {
  console.log(this)
  foo1()
}
function foo3() {
  console.log(this)
  foo2()
};;;
foo3() //打印3次全局对象
// 案例三
let obj = {
  name: 'why',
  foo: function () {
    console.log(this)
  }
}

let bar = obj.foo
bar() // 全局对象
//案例四
function foo() {
  console.log(this)
}
let obj = {
  name: 'why',
  foo: foo
}

let bar = obj.foo
bar() // 全局对象
//案例五
function foo() {
  function bar() {
    console.log(this)
  }
  return bar
}
let fn = foo()
fn() //全局对象

一些补充

javascript 复制代码
//定时器
setTimeout(function () {
  console.log(this) //游览器环境下window,
       }, 1000)       
//node环境下是
//`setTimeout`函数的回调函数中的 `this` 默认是指向一个空对象的,
//而不是指向全局对象`global`,
//这个空对象称为"定时器对象"(Timer object)。

//监听点击
const boxDiv = document.querySelector('.box')
boxDiv.onclick = function () {
  console.log(this)
}
boxDiv.addEventListener('click', function () {
  console.log(this)
})
//当事件被触发时,`this`都会指向触发事件的元素,也就是`.box`元素。

// 3.数组.forEach/map/filter/find
let names = ['zhaimou', 'zhaione', 'zhaitwo']
names.forEach(function (item) {
  console.log(item, this)
}, 'abc')
names.map(function (item) {
  console.log(item, this)
}, 'cba')
//zhaimou [String: 'abc']
//zhaione [String: 'abc']
//zhaitwo [String: 'abc']
//zhaimou [String: 'cba']
//zhaione [String: 'cba']
//zhaitwo [String: 'cba']

隐式绑定:当函数作为对象的方法调用时,通过隐式绑定,this 绑定到调用该方法的对象。

javascript 复制代码
// 案例一
function foo() {
  console.log(this)
}
const obj = {
  name: 'why',
  foo: foo
}
obj.foo() // obj对象
// 案例二:
const obj = {
  name: 'why',
  eating: function () {
    console.log(this.name + '在吃东西')
  },
  running: function () {
    console.log(obj.name + '在跑步')
  }
}
obj.eating() //why在吃东西
obj.running() //why在跑步
// 案例三:
const obj1 = {
  name: 'obj1',
  foo: function () {
    console.log(this)
  }
}
let obj2 = {
  name: 'obj2',
  bar: obj1.foo
}
obj2.bar() //{ name: 'obj2', bar: [Function: foo]}
// 案例4
function fn() {
  console.log(this)
}
const obj = {
  foo: fn
}
const cloneObj = {
  foo1: obj
}
cloneObj.foo1.foo() // obj 

一些面试题

js 复制代码
// 立即执行函数
const obj = {
  foo: function () {
    (function () {
      console.log(this)
    })()
  }
}
obj.foo()
// 另一种情况
const obj = {
  foo: function () {
    function fn() {
      console.log(this)
    }
    return fn()
  }
}

obj.foo()
// 当调用obj.foo()方法时,this指向obj对象。
// 但是当obj.foo()方法执行时,创建一个新函数执行,又用到fn(),所以最后又绑定到全局对象上

显式绑定:通过 callapplybind 方法,可以显式地绑定函数的 this 指向一个特定的对象。

js 复制代码
function foo() {
  console.log(this)
}
foo.call('123') //[String: '123']
foo.apply('456') // [String: '456']
let newFoo = foo.bind('aaa')
newFoo() //// [String: 'aaa'] 
当传入null/undefined时, 自动将this绑定成全局对象
// foo.apply(null) window
// foo.apply(undefined) window

⭐⭐⭐

每次调用bind()方法都会创建一个新的函数,并将原函数的this绑定到指定的对象。而后续的bind()方法调用只会改变新函数的this绑定目标,而不会影响之前已经绑定过的对象,因此,最终的this由第一次bind()决定

js 复制代码
 function fn(){
 console.log(this)
 }
 fn().bind('123').bind('1234')()  //123

new绑定:通过使用 new 关键字调用构造函数创建一个新的对象实例时this绑定到新创建的对象。

javascript 复制代码
//案例1
function Person(name) {
    this.name = name;
  }
  const person = new Person('Bob');
  console.log(person.name); // 使用 new 创建对象实例,this 指向新创建的对象
//案例2
//注意构造函数的返回值  如果是返回的是基础变量就this就指向实例
function Foo() {
  this.name = 'Foo'
  const obj = {
    name: 'obj'
  }
  return obj
}
const foo = new Foo()
console.log(foo.name) // obj 

箭头函数:箭头函数的 this 不会被绑定到任何特定的对象上,而是继承自外部作用域。

javascript 复制代码
const obj = {
  name: 'Alice',
  sayHello() {
    const greet = () => {
      console.log(`Hello, ${this.name}!`);
    };
    greet();
  }
};

obj.sayHello(); // 在箭头函数内部,this 继承自外部作用域,指向 obj,打印 "Hello, Alice!"
//箭头函数不能通过`bind()`、`call()`和`apply()`来改变其上下文,
//因为它们没有自己的上下文。

优先级

new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)

一些综合面试题

js 复制代码
//面试题一

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};

function sayName() {
  var sss = person.sayName;
  sss(); // window: 独立函数调用
  person.sayName(); // person: 隐式调用
  (person.sayName)(); // person: 隐式调用
  (b = person.sayName)(); // window: 赋值表达式(独立函数调用)
}
sayName();

//面试题二

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person2 = { name: 'person2' }
person1.foo1(); // person1(隐式绑定)
person1.foo1.call(person2); // person2(显示绑定优先级大于隐式绑定)

person1.foo2(); // window(不绑定作用域,上层作用域是全局)
person1.foo2.call(person2); // window

person1.foo3()(); // window(独立函数调用)
person1.foo3.call(person2)(); // window(独立函数调用)
person1.foo3().call(person2); // person2(最终调用返回函数式, 使用的是显示绑定)

person1.foo4()(); // person1(箭头函数不绑定this, 上层作用域this是person1)
person1.foo4.call(person2)(); // person2(上层作用域被显示的绑定了一个person2)
person1.foo4().call(person2); // person1(上层找到person1)

//面试题三

var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}

var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1() // person1
person1.foo1.call(person2) // person2(显示高于隐式绑定)

person1.foo2() // person1 (上层作用域中的this是person1)
person1.foo2.call(person2) // person1 (上层作用域中的this是person1)

person1.foo3()() // window(独立函数调用)
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2

person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1

//面试题四

var name = 'window'

function Person(name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}

var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2

person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj

总结 this 的值是在函数实际执行时确定的,而不是定义时。它可能会因为函数的调用方式和执行上下文的变化而发生改变。

到这里就结束了,更多作为自己学习,希望对你有所帮助,有错欢迎指出

相关推荐
拉不动的猪6 分钟前
前端常见数组分析
前端·javascript·面试
小吕学编程23 分钟前
ES练习册
java·前端·elasticsearch
Asthenia041230 分钟前
Netty编解码器详解与实战
前端
袁煦丞35 分钟前
每天省2小时!这个网盘神器让我告别云存储混乱(附内网穿透神操作)
前端·程序员·远程工作
一个专注写代码的程序媛2 小时前
vue组件间通信
前端·javascript·vue.js
一笑code2 小时前
美团社招一面
前端·javascript·vue.js
懒懒是个程序员2 小时前
layui时间范围
前端·javascript·layui
NoneCoder2 小时前
HTML响应式网页设计与跨平台适配
前端·html
凯哥19702 小时前
在 Uni-app 做的后台中使用 Howler.js 实现强大的音频播放功能
前端
烛阴2 小时前
面试必考!一招教你区分JavaScript静态函数和普通函数,快收藏!
前端·javascript