1. 前言
1.1 浏览器中js执行机制:
词法分析、语法分析 ===> AST 含预解析的过程:变量和函数声明提升为全局对象的属性
全局执行上下文(scope chain、vo(go)、this binding(执行时才确定))入执行栈,执行
遇到函数执行语句,函数内部的变量、函数提升为该函数的ao的属性
该函数的执行上下文(scope chain、vo(go)、this binding(执行时才确定))入栈,执行,出栈,继续执行全局执行上下文的代码
…
2. this
2.1 在全局作用域中
go有个window属性使得全局对象指向自己,this指向window全局对象
<script>
// this是关键字,值是当前执行上下文(global、function 或 eval)的一个属性,在非严格模式下,总是指向一个对象
console.log(this)
</script>
2.2 在普通函数中
谁调用这个函数,函数中的this就指向谁,和在何处定义无关;
function foo() {
console.log('this', this);
}
foo(); // window
2.3 箭头函数中的this
箭头函数的this和它所在作用域的this有关,即上级执行上下文的this指向谁,它内部的this就指向谁;且指向不会改变。
箭头函数不能用作构造函数,因为js构造函数实例对象时,需要改变this的指向(指向空对象),使得属性和方法在实例对象上;而箭头函数的this指向不能改变。
2.4 构造函数中的this
指向实例化对象,new关键字的作用
new四步:
- 申请一块内存,存放空对象
- 利用call方法改变构造函数内部this指向,使this指向实例化的这个空对象(属性)
- 把实例化对象的隐式原型指向构造函数的显示原型(方法)
- 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象。
new的实现:
手写new
1
3. 示例
3.1 立即执行函数
this指向window
(function foo() {
console.log('IIFE', this);
})();
3.2 定时器回调函数中的this
定时器的回调函数本身是普通函数时: 无论定时器是否定义在事件绑定的普通函数内,都指向window
setTimeout(function() {
console.log(this); // window
,500)
<ul class="father">
<li class="btn">保存</li>
<li></li>
</ul>
<script>
btn.addEventListener('click', function (e) {
console.log('this:', this, 'e.target', e.target); // this: <ul class="father">…</ul> e.target <li class="btn">…</li>
setTimeout(function () {
console.log('timerFunc', this); // 定时器的回调是普通函数: window
}, 500);
});
</script>
定时器的回调函数本身是箭头函数函数时: 如果定时器是在某个函数内部注册的,this指向和它相同,如果定时器在全局作用域中,则指向window
<ul class="father">
<li class="btn">保存</li>
<li></li>
</ul>
<script>
btn.addEventListener('click', function (e) {
console.log('this:', this, 'e.target', e.target); // this: <ul class="father">…</ul> e.target <li class="btn">…</li>
setTimeout(()=> {
console.log('timerFunc', this); // 定时器的箭头函数: <ul class="father">…</ul>
}, 500);
});
</script>
怎么感觉这么诡异?这里只是确定了是谁调用定时器的回调?
3.3 DOM事件绑定中的this
回调函数是普通函数时:
this指向绑定的事件源(节点),和到底谁触发了无关(事件委托:给父元素绑定点击事件,点击子元素,因为事件冒泡,仍会触发父元素的点击事件执行回调;e.target是点击的子元素、this是绑定了事件的父元素)
<body>
<ul class="father">
<li class="btn">保存</li>
<li></li>
</ul>
<script>
const btn = document.querySelector('.father');
btn.addEventListener('click', function (e) {
console.log('this:', this, 'e.target', e.target);
});
</script>
回调函数是箭头函数时:
<body>
<ul class="father">
<li class="btn">保存</li>
<li></li>
</ul>
<script>
const btn = document.querySelector('.father');
btn.addEventListener('click', (e) => {
console.log('this:', this, 'e.target', e.target); //this: window , e.target: li
});
</script>
这里箭头函数内的this指向全局,是因为它的上一层作用域其实就是全局作用域,虽然这个回调的调用者是btn对象,但箭头函数的this在定义时确定,而不是调用时。
3.4 原型链中的this
对象的原型链上的方法内的this,指向实例对象本身