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

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

相关推荐
zhougl9962 小时前
html处理Base文件流
linux·前端·html
花花鱼2 小时前
node-modules-inspector 可视化node_modules
前端·javascript·vue.js
HBR666_2 小时前
marked库(高效将 Markdown 转换为 HTML 的利器)
前端·markdown
careybobo3 小时前
海康摄像头通过Web插件进行预览播放和控制
前端
TDengine (老段)4 小时前
TDengine 中的关联查询
大数据·javascript·网络·物联网·时序数据库·tdengine·iotdb
杉之5 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
喝拿铁写前端5 小时前
字段聚类,到底有什么用?——从系统混乱到结构认知的第一步
前端
再学一点就睡5 小时前
大文件上传之切片上传以及开发全流程之前端篇
前端·javascript
木木黄木木6 小时前
html5炫酷图片悬停效果实现详解
前端·html·html5
请来次降维打击!!!7 小时前
优选算法系列(5.位运算)
java·前端·c++·算法