this的指向
普通函数
- 函数在调用时,js会默认给this一个值
- this的绑定和定义的位置没有关系
- this的绑定和调用方式以及调用位置有关系
- this是在运行时被绑定的
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
function test() {
console.log(this);
}
// 1. 直接调用
test(); // window
// 2 通过对象调用
const obj = {
name: 'obj',
test: test
}
obj.test(); // obj对象
// 3 通过call/bind/apply指定绑定对象调用
// 下面绑定方式不加String()包裹也可以,js会将其自动转为对象
test.apply(String('apply')); // String {'apply'} 对象
test.call(String('call')); // String {'call'} 对象
test.bind(String('bind'))(); // String {'bind'} 对象
</script>
内置函数的this指向
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>点击</bu>
<script>
setTimeout(function () {
console.log('定时器函数:', this); // window
}, 0);
const btn = document.querySelector('button');
btn.onclick = function () {
console.log('btn的点击:', this); // <button>…</button>
}
var names = ['tom'];
names.forEach(function () {
console.log('forEach: ', this); // window
})
names.forEach(function () {
console.log('指定this,forEach: ', this); // ['tom']
}, names)
</script>
</body>
</html>
绑定优先级
- 默认绑定的优先级最低
- 显示绑定优先级高于隐式绑定
function test () {
console.log(this);
}
const obj = {
name: 'boj',
test: test
}
obj.test.apply('apply'); // String {"apply"}
- new绑定优先级高于隐式绑定
const obj = {
name: 'boj',
test: function () {
console.log(this);
console.log(this === obj)
}
}
new obj.test()
4. new绑定优先级高于bind
注意:new绑定和call、apply是不允许同时使用的
function test () {
console.log(this);
}
var demo = test.bind('bind');
new demo();
箭头函数
- 箭头函数不会绑定this、arguments属性
- 箭头函数不能作为构造函数来使用(不能new)
// 箭头函数没有this
const test = () => {
console.log(this);
}
test(); // window
test.apply('apply'); // window
this的查找规则
案例一
因为箭头函数demo是没有this的,所以js引擎往上一层作用域找,找到普通函数test(存在this),而函数test的this指向obj对象,因此最终结果就是obj对象。
// 箭头函数调用this的查找规则
const obj = {
name: 'obj',
test: function () {
var demo = () => {
console.log('this:', this);
}
return demo;
}
}
var fn = obj.test();
fn();
fn.apply('apply');
案例二
把上述的普通函数test改为箭头函数:
因为箭头函数demo是没有this的,所以js引擎往上一层作用域找,找到箭头函数test(也不存在this),最终找到全局作用域,因此最终结果就是window对象。
注意:obj的{}不是一个作用域
var name = 'window';
// 箭头函数调用this的查找规则
const obj = {
name: 'obj',
test: () => {
var demo = () => {
console.log('name:', name)
console.log('this.name:', this.name)
console.log('this:', this);
}
return demo;
}
}
var fn = obj.test();
fn();
fn.apply('apply');
this的练习题
练习一
var name = 'window';
const obj = {
name: 'obj',
sayName: function () {
console.log(this.name);
}
}
const demo = obj.sayName; // 默认绑定 window
demo();
obj.sayName(); // 隐式绑定 obj
(obj.sayName)(); // 隐式绑定 obj
(test = obj.sayName)(); // 间接函数引用 window
练习二
var name = 'window';
const obj1 = {
name: 'obj1',
sayName1: function () {
console.log(this.name);
},
sayName2: () => {
console.log(this.name);
},
sayName3: function () {
return function () {
console.log(this.name);
}
},
sayName4: function () {
return () => {
console.log(this.name);
}
}
}
const obj2 = {name: 'obj2'};
console.log('----sayName1---');
obj1.sayName1(); // 隐式绑定 obj1
obj1.sayName1.call(obj2); // 显示绑定 obj2
console.log('----sayName2---');
obj1.sayName2(); // 上层作用域:window
obj1.sayName2.call(obj2); // 上层作用域:window
console.log('----sayName3---');
obj1.sayName3()();
/*
默认绑定(独立函数调用) window
相当于
let fn = obj1.sayName3()
==>
fn = function () { console.log(this.name) } 这个函数相当于是全局函数
fn();
*/
obj1.sayName3.call(obj2)(); // 默认绑定 window
/*
默认绑定(独立函数调用) window
相当于
let fn = obj1.sayName3()
==>
fn = obj2.sayName3()
==>
fn = function () { console.log(this.name) } 这个函数相当于是全局函数
fn();
*/
obj1.sayName3().call(obj2); // 显示绑定 obj2
/*
let fn = obj1.sayName3()
==>
fn = function () { console.log(this.name) } this改为window
==>
fn.call(obj2) // this改为obj2
*/
console.log('----sayName4---');
obj1.sayName4()(); // 上一层作用域 obj1
obj1.sayName4.call(obj2)(); // 改变上一层作用域 obj2
obj1.sayName4().call(obj2); // 上一层作用域(箭头函数没有this) obj1
练习三
var name = 'window';
function Obj (name) {
this.name = name,
this.sayName1 = function () {
console.log(this.name);
}
this.sayName2 = () => {
console.log(this.name);
},
this.sayName3 = function () {
return function () {
console.log(this.name);
}
},
this.sayName4 = function () {
return () => {
console.log(this.name);
}
}
}
const obj1 = new Obj('obj1');
const obj2 = new Obj('obj2');
console.log('----sayName1---');
obj1.sayName1(); // 隐式绑定 obj1
obj1.sayName1.call(obj2); // 显示绑定 obj2
console.log('----sayName2---');
obj1.sayName2(); // 上层作用域:obj1
obj1.sayName2.call(obj2); // 上层作用域:obj1
console.log('----sayName3---');
obj1.sayName3()(); // 默认绑定(独立函数调用) window
obj1.sayName3.call(obj2)(); // 默认绑定(独立函数调用) window
obj1.sayName3().call(obj2); // 显示绑定 obj2
console.log('----sayName4---');
obj1.sayName4()(); // 上一层作用域 obj1
obj1.sayName4.call(obj2)(); // 改变上一层作用域 obj2
obj1.sayName4().call(obj2); // 上一层作用域(箭头函数没有this) obj1
练习四
var name = 'window';
function Obj (name) {
this.name = name,
this.demo = {
name: 'demo',
sayName1: function () {
return function () {
console.log(this.name);
}
},
sayName2: function () {
return () => {
console.log(this.name);
}
}
}
}
const obj1 = new Obj('obj1');
const obj2 = new Obj('obj2');
console.log('----sayName1---');
obj1.demo.sayName1()(); // 默认绑定(独立函数调用) window
obj1.demo.sayName1.call(obj2)(); // 默认绑定(独立函数调用)window
obj1.demo.sayName1().call(obj2); // 显示绑定 obj2
console.log('----sayName2---');
obj1.demo.sayName2()(); // 上一次作用域 demo
obj1.demo.sayName2.call(obj2)(); // 改变上一次作用域 obj2
obj1.demo.sayName2().call(obj2); // 上一次作用域 demo