函数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 = namethis.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指向windowvar 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 = namethis.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) // person2person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
- this 面试题四
var name = 'window'
function Person (name) {this.name = namethis.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) // person2person1.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 |