javascript 中 函数this的指向

函数this的指向

在全局环境中,this指向

  • 浏览器中window
  • node中global
function foo(){
    console.log(this) // // 浏览器中:Window 对象 ,NODE中:global对象
}
foo()
"use strict";
 .....
// 严格模式下 this 为 undefined:
this的指向,跟函数所处的位置无关,跟函数的调用方式有关

function foo() {
  console.log(this)
}

// 1.直接调用这个函数
foo() // this指向全局对象(在浏览器中是 Window 对象)

// 2.创建一个对象, 对象中的函数指向foo
var obj = {
  name: 'SUMU',
  foo: foo
}

obj.foo() // this指向obj对象

// 3.apply调用
foo.apply("abc") // this指向"abc"
默认绑定
//案例1:
function foo() {
  console.log(this) // this 指向window对象
}

foo()

// 2.案例二:
function foo1() {
  console.log(this) // this 指向window对象
}

function foo2() {
  console.log(this) // this 指向window对象
  foo1()
}

function foo3() {
  console.log(this) // this 指向window对象
  foo2()
}

foo3()

// 3.案例三:
var obj = {
  foo: function() {
    console.log(this) // this 指向window对象
  }
}

var bar = obj.foo
bar() 

// 4.案例四:
function foo() {
  console.log(this) // this 指向window对象
}
var obj = {
  foo: foo
}

var bar = obj.foo
bar() 

// 5.案例五:
function foo() {
  function bar() {
    console.log(this)
  }
  return bar
}

var fn = foo()
fn() // this 指向window对象

var obj = {
  eating: fn
}

obj.eating() // 隐式绑定 {name: 'sumu', eating: ƒ}
隐式绑定
function foo() {
  console.log(this) // this 指向obj对象 

}
var obj = {
  foo: foo 
}
obj.foo() // this 指向obj对象

// 2.案例二:
var obj = {
  name: "alive",
  eating: function() {
    console.log(this,'eating')
    console.log(this.name + "在吃东西")
  },
  running: function() {
    console.log(obj.name + "在跑步")
  }
}

 obj.eating() // this 指向obj对象 // 输出结果为: alive在吃东西
 obj.running() // this 指向obj对象 输出结果为: alive在跑步

var fn = obj.eating
fn() // this 指向window对象 输出结果为: 在吃东西

// 3.案例三:
var obj1 = {
  name: "obj1",
  foo: function() {
    console.log(this)
  }
}

var obj2 = {
  name: "obj2",
  bar: obj1.foo
}

obj2.bar() // this 指向obj2对象 输出结果为: obj2
显式绑定 (call/apply/bind)
function foo() {
  console.log("函数被调用了", this)
}
var obj = {
  name: "obj"
}

// call/apply是可以指定this的绑定对象
foo.call(obj) // this 指向obj对象
foo.apply(obj) // this 指向obj对象
foo.apply("aaaa") // this 指向"aaaa"对象

// 2.call和apply有什么区别?
// 传递的参数不一样
// call传递的是参数列表
// apply传递的是数组

function sum(num1, num2, num3) {
  console.log(num1 + num2 + num3, this)
}

sum.call("call", 20, 30, 40)
sum.apply("apply", [20, 30, 40])

// 3.bind
// bind是返回一个新的函数 ,永久绑定 this 并返回新函数
var fn = sum.bind("bind", 10, 20, 30)
fn() // this 指向"bind"对象 输出结果为: 60 bind
fn() // this 指向"bind"对象 输出结果为: 60 bind

new绑定

使用 new 调用构造函数时,this 指向新创建的对象:

function Person(name, age) {
 console.log(this) //Person {}
  this.name = name
  this.age = age
}

var p1 = new Person("sumu", 18)
console.log(p1.name, p1.age)

var p2 = new Person("kobe", 30)
console.log(p2.name, p2.age)

DOM 事件处理函数中的 this

在 DOM 事件处理函数中,this 指向触发事件的元素:

const boxDiv = document.querySelector('.box')
boxDiv.onclick = function() {
  console.log(this) //this 指向boxDiv对象 <button> 元素
}

boxDiv.addEventListener('click', function() {
  console.log(this) // this 指向boxDiv对象 <button> 元素
})
箭头函数中的 this( 看定义时的外层作用域 )

箭头函数 没有自己的 this,其 this 继承自外层作用域

  • 箭头函数 this 的核心规则​
  • 继承外层作用域的 this
    箭头函数没有自身的 this,它会​​捕获定义时所在作用域的 this 值​​,且​​无法被修改​​(即使使用 call、apply、bind)
const obj = {
  name: "Alice",
  show: () => console.log(this.name)  // this 指向全局对象(非严格模式)
};
obj.show(); // 输出 undefined(全局 name 未定义)
  • ​​与调用方式无关
    无论箭头函数如何被调用(作为方法、回调等),其 this 始终由​​定义时的词法作用域​​决定
const obj = {
  method() {
    setTimeout(() => console.log(this), 100); // this 指向 obj
  }
};
obj.method(); // 输出 obj 对象
  • 全局作用域中的表现​
    在全局作用域定义的箭头函数,this 指向全局对象(浏览器中为 window,Node.js 中为 global)
// 案例1:
// 1.测试箭头函数中this指向
var name = "sumu"

var foo = () => {
  console.log(this)
}

foo() // this 指向window对象
 var obj = {foo: foo}
 obj.foo() // this 指向window对象
 foo.call("abc") // this 指向window对象

 // 案例2:
const obj = {
  name: 'sumu',
  foo: function() {
    console.log(this) // this 指向obj对象 {name: 'sumu', foo: ƒ}
    const bar = () => {
      console.log(this) // this 指向obj对象 {name: 'sumu', foo: ƒ}
    }
    bar()
  }
}
obj.foo()

// 案例3:
var obj = {
  data: [],
  getData: function() {
    // 发送网络请求, 将结果放到上面data属性中
    // 在箭头函数之前的解决方案
    // var _this = this
    // setTimeout(function() {
    //   var result = ["abc", "cba", "nba"]
    //   _this.data = result
    // }, 2000);
    // 箭头函数之后
    setTimeout(() => {
      console.log(this,'obj') // this 指向obj对象 {data: Array(0), getData: ƒ}
      var result = ["abc", "cba", "nba"]
      this.data = result
    }, 2000);
  }
}

obj.getData()


特性箭头函数普通函数
this 绑定静态绑定(定义时确定)动态绑定(调用时确定)
能否修改 this❌ 不可用 call/apply/bind 修改✅ 可通过 call/apply/bind 修改
构造函数❌ 不可用 new 调用(报错)✅ 可用 new 创建实例
arguments 对象❌ 不存在✅ 存在
其他
// 3.数组.forEach/map/filter/find
var names = ["abc", "cba", "nba"]
names.forEach(function(item) {
  console.log(item, this) // this 指向String->abc
}, "abc")
names.map(function(item) {
  console.log(item, this) // this 指向String->cba
}, "cba")

setTimeout(function() {
  console.log(this) // window
}, 2000)

优先级
// 一.显示绑定 > 隐式绑定 
var obj = {
  name: "obj",
  foo: function() {
    console.log(this)
  }
}

obj.foo() // this 指向obj对象 {name:'obj', foo: ƒ}

// 1.call/apply的显示绑定高于隐式绑定
 obj.foo.apply('abc') // this 指向"abc"
 obj.foo.call('abc') // this 指向"abc"

// 2.bind的优先级高于隐式绑定
 var bar = obj.foo.bind("cba")
 bar() // this 指向"cba"


// 3.更明显的比较
function foo() {
  console.log(this)
}

var obj = {
  name: "obj",
  foo: foo.bind("aaa")
}

obj.foo() // this 指向"aaa"

//二. new的优先级高于隐式绑定
var f = new obj.foo() // this指向foo

// 结论: new关键字不能和apply/call一起来使用

// new的优先级高于bind
function foo() {
  console.log(this,'xxx') 
}

 var bar = foo.bind("aaa")

 var obj = new bar() // 

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


//三.特殊绑定-忽略显示绑定
function foo() {
  console.log(this)
}

foo.apply("abc")
foo.apply({})

// apply/call/bind: 当传入null/undefined时, 自动将this绑定成全局对象
foo.apply(null) // this指向window
foo.apply(undefined) // this指向window

var bar = foo.bind(null)
bar() // this指向window
  • this面试题一
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();


  • this面试题二
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)
  • this面试题三
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
  • this 面试题四

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指向示例
全局/普通函数调用非严格模式:全局对象(window,严格模式:undefined)function fn() { console.log(this); };fn(); // window(非严格模式)
对象方法调用调用该方法的对象obj.fn(); 中 this 指向 obj
构造函数调用new 创建的新对象实例const p = new Person(); 中构造函数内 this 指向 p
显式绑定call/apply/bind 的第一个参数fn.call(obj); 中 this 指向 obj
箭头函数继承定义时外层作用域的 thisconst foo = () => { console.log(this); } // 继承外层 this
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的鱼先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值