一、this关键词
this在函数调用时,自动生成的对象,函数的执行方式决定,指向最后调用他的对象。
由此我们可以想到以下几种调用方式:
- 普通函数调用,正常情况下指向window,严格模式下为undefined
- 为什么会指向window呢 全局变量环境中的变量等同于在window顶级变量定义的
全局中的,在变量环境中的变量等同于在顶级对象(Windows)当中进行了一个挂载。
fn()函数调用等同于window.fn(),this一定会指向最后调用它的对象。但是这种调用方式是不对的,他会污染window,因此我们就会有严格模式。(use strict),让我们来看下下面的代码:
函数运行时通过对象函数的方法进行调用method,当我们给它一个赋值时,这个函数仍然可以运行。在JS当中,我们的变量是没有类型的,fn2其实是一个指针,在栈内存中分配了一个空间,指向堆内存函数的地址。
二、构造还是执行函数
函数是如何区分构造还是执行的呢?因为函数是一个对象有着属性 [[constructor]] [[call]]。函数本身有两个方法,当作为普通函数来调用的话就会走call分支来执行函数。
当使用 new 调用构造函数时,JavaScript引擎会执行以下步骤:
- 创建一个新的空对象 {}
- 将新对象的原型链指向构造函数的 prototype 属性
- 将构造函数的 this 绑定到新创建的对象
- 执行构造函数内部代码(为新对象添加属性和方法)
- 如果构造函数没有显式返回对象,则返回这个新对象
construct 过程本质上完成了对象的初始化和原型关联,使新对象能够继承构造函数原型上的属性和方法。
普通函数作为method,还有引用函数,构造函数来运行,让我们来看下最后的一种方法!!!
三、事件的回调函数
this指向事件发生的对象,最后谁调用谁负责,还是由它的调用对象来决定的。指向事件发生的对象,this由函数的调用方式决定的。
<button id="btn">点击</button>
<script>
const btn = document.getElementById('btn');
btn.addEventListener('click',function(){
console.log(this); // <button id="btn">点击</button>
})
这里的运行是会报错的,this指向的是window,当我们修改 var _this=this时就可以解决该问题。
这里叫做this的丢失,作用域链解决这个问题;
四、 call apply bind (霸道一些)
看下面代码,为什么函数最后的运行方式是undefined呢?
setTimeout函数运行时是异步的,执行的函数是匿名函数,调用方式是普通函数,我们可以发现调用this.func1()的是匿名函数。这时候我们会发现会报错,在严格模式下则为undefined,因为这时候this指向的是window,而全局没有func1()这个方法。
如何将setTimeout函数运行时内部的this指向a呢?来看下面的代码,当this丢失的时候,我们可以通过作用域链来使用外层的this.
能够指定
call的方法,接受一个参数,这个参数将是能够指定当这个函数调用的时候它内部的this的指向被强制指向为a,也完成了将内部的this指向a。函数对象的原型链上有一个call, apply等方法 指定 this 指向 为第一个参数.
我们可以看下b.bind如何执行,b.apply(a,[1,2]),bind生成的函数跟call和apply有个共同点,当它在运行时,它的内部的this是被指定的。
区别与用法
call/apply:调用即执行,参数格式不同,用完即焚bind:绑定不执行,返回新函数,this被永久锁定- 优先级 :bind > call/apply,绑定后无法被再次修改this 记住 :想立即执行用call/apply,想延迟执行用bind!
五、 封装函数
查找元素,设定点击事件,这样写的话下次继续使用的话就会很麻烦,我们可以将函数封装一下,一个方法一个函数,方便复用。
当我们点击变色按钮时出现下列报错情况,为什么会出现这种情况呢?
this丢失问题,下面的this指向this.element,当我们把function()函数换成箭头函数就可以实现,那有没有其它方法呢。
我们可以看下下述代码,不用call和apply的原因是因为call和apply函数会立刻运行。只要一个函数调用了call和apply之后就会立马运行。
Button.prototype.bindEvent = function(){
// this 丢失问题 // this Button
this.element.addEventListener('click',function(){
// this => this.element
this.element.style.backgroundColor = 'red';
}.bind(this))
}
我们可以看到这里的this指向this.element,因为作为事件的处理函数被调用,处理函数是对下述函数的引用,不再是对象上的一个方法,而是函数。这个函数我们要让它内部的this要指向它的实例。
Button.prototype.bindEvent = function(){
// this 丢失问题 // this Button
this.element.addEventListener('click',this.setBgColor.bind(this))
}