🔥🔥🔥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 的值是在函数实际执行时确定的,而不是定义时。它可能会因为函数的调用方式和执行上下文的变化而发生改变。

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

相关推荐
程序员海军3 分钟前
2024 Nuxt3 年度生态总结
前端·nuxt.js
m0_7482567813 分钟前
SpringBoot 依赖之Spring Web
前端·spring boot·spring
web1350858863541 分钟前
前端node.js
前端·node.js·vim
m0_5127446442 分钟前
极客大挑战2024-web-wp(详细)
android·前端
若川1 小时前
Taro 源码揭秘:10. Taro 到底是怎样转换成小程序文件的?
前端·javascript·react.js
潜意识起点1 小时前
精通 CSS 阴影效果:从基础到高级应用
前端·css
奋斗吧程序媛1 小时前
删除VSCode上 origin/分支名,但GitLab上实际上不存在的分支
前端·vscode
IT女孩儿1 小时前
JavaScript--WebAPI查缺补漏(二)
开发语言·前端·javascript·html·ecmascript
m0_748256564 小时前
如何解决前端发送数据到后端为空的问题
前端
请叫我飞哥@4 小时前
HTML5适配手机
前端·html·html5