函数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 |
箭头函数 | 继承定义时外层作用域的 this | const foo = () => { console.log(this); } // 继承外层 this |