this
This指向
- 函数在调用时,JavaScript会默认给this绑定一个值;
- this 的指向不是在创建时就绑定了,而是在调用时被绑定的;
- this的绑定和定义的位置(编写的位置)没有关系;
- this的绑定和调用方式以及调用的位置有关系;
四种绑定规则
- 默认绑定;
- 隐式绑定;
- 显示绑定;
- new绑定;
默认绑定(独立调用)
独立的调用,this 就代表 window 对象。
独立调用:xxx()为调用,没有小括号的不叫调用;函数前面没有别的东西调用叫独立;
- 普通函数被独立的调用
1 | // 定义函数 |
- 函数定义在对象中,但是独立调用
1 | const obj = { |
- 高阶函数,独立调用
1 | const obj = { |
- 严格模式中,独立调用的函数中的this指向的是 undefined
严格模式中,独立调用的函数中的this指向的是 undefined ; webpack打包啥的可能自动设置为严格模式,所以 独立调用的函数建议不要写this,直接写window;
1 | // 严格模式中,独立调用的函数中的this指向的是 undefined ; webpack打包啥的可能自动设置为严格模式,所以 独立调用的函数建议不要写this,直接写window; |
隐式绑定(对象环境(属性调用))
也就是它的调用位置中,是通过某个对象发起的函数调用。
当函数被保存为一个对象的属性时,它就可称为这个对象的方法。当一个方法被调用时,this被绑定到这个对象上。如果调用表达式包含一个提取属性的动作(. 或 []),那么它被称为方法调用。下面两种在对象环境中都可以解释的通。
- 对象环境指向对象
- 谁调用,this指向谁。
函数作为对象的属性去调用 例一:
1 | var name = "全局变量"; |
函数作为对象的属性去调用 例二:
1 | var obj1 = { |
函数作为对象的属性去调用 例三:
1 | var a = { |
函数作为对象的属性去调用 例四:
1 | var obj = { |
函数作为对象的属性去调用 例五:
1 | var bj=10; |
- 为啥是obj.say()():因为obj.say()执行之后返回了一个函数,并不会执行return函数里面的代码;
- 为啥是window:因为obj.say()返回了一个函数,然后在();相当于直接**函数名()**来调用的;所以指向window;
函数作为对象的属性去调用 例六:
1 | var oldObj = { |
显式绑定
通过call、apply、bind明确的绑定了this指向的对象,所以称之为显式绑定;
函数的调用为 xxx();其实这个只是在调用前加上了绑定:xxx.绑定(“绑定对象”);也可以不写绑定对象:xxx.绑定(),效果和xxx()一样;
场景:需求:执行如下函数,并且函数中的this指向obj;
1 | const obj = { |
- 隐式绑定
1 | obj.foo=foo; |
如果我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?
- 显式绑定
1 | // 执行函数,并且强制this就是obj对象; |
那让我们系统的了解一下他们的用法吧~
call、apply
- JavaScript所有的函数都可以使用call和apply方法;
- 第一个参数是相同的,要求传入一个对象;这个对象的作用是什么呢?就是给this准备的,在调用这个函数时,会将this绑定到这个传入的对象上。
- 后面的参数,apply为数组,call为参数列表;
func.apply(thisArg,[argsArray])
func.call(thisArg,arg1,arg2,...)
1 | function foo(name, age, height) { |
bind
需求:调用foo时,总是绑定到obj对象身上,并不希望foo对象身上有添加的函数;
- 可以通过call或apply实现
1 | function foo(name) { |
那有没有什么简单的写法呢,即:
foo.call(obj)
简写一下子
1 | function foo(name) { |
可能有人会想:独立函数调用this不指向
window
了么这不就,其实有个优先级(下文有提到),独立函数的优先级比较低,低于bind绑定;
new绑定 :
new做了什么事情
- 创建一个新的空对象 (空实例)(obj)
- 将构造函数的显式原型赋值给这个新对象,作为新对象的隐式原型
- 构造函数内部的 this,会指向创建出来的新对象 (this 指向第一步的空对象)
- 执行函数体的代码块
- 返回对象
所以说:构造函数中的 this 会指向创建出来的实例对象
1 | var TestClass = function () { |
1 | function Foo(name,age){ |
this优先级
如果new、bind、apply、call、隐式绑定和默认绑定同时出发多个的话,this会指向谁呢?
这里先说一下结论,下面做验证,优先级排名为:
- new
- bind
- apply/call
- 隐式绑定
- 默认绑定
接下来可通过代码进行如上验证:
- 显示优先级(call/apply)高于隐式优先级
1 | // 1.显示绑定>隐式绑定 |
- 显示优先级(bind)高于隐式优先级
1 | function foo() { |
- new优先级高于隐式优先级
1 | let obj = { |
- new不能和call/apply一起使用
- new优先级高于bind优先级
1 | function foo(){ |
- bind比apply/call优先级高
1 | function foo(){ |
事件对象:
在 DOM 事件中使用 this,this 指向了触发事件的 DOM 元素本身
1 | li.onclick = function(){ |
箭头函数:
详见:箭头函数
setTimeout与setInterval:
setInterval和setTimeout中传入函数时,函数中的this会指向window对象
可以在定(延)时器外面var that = this;来解决这个问题
1 | setTimeout(function () { |
- 这是最最常用的常见的定时器用法,回调函数里的this指向的是window。
- 我的理解是:由于setTimeout属于宏任务,它的回调在延时之后才进入到主线程执行,而函数执行的时候才明确 this 的指向。执行的时候,由于没有设置内部this的指向。相当于是普通函数调用。所以会默认指向window
1 | var obj = { |
- 第一个setTimeout,执行obj.getage 之后,相当于setTimeout的回调是一个匿名函数,执行的时候,函数内部未设置this的指向。相当于是普通函数调用。所以this默认指向window,所以结果是undefined。
- 第二个setTimeout,传给setTimeout的也是一个匿名回调函数,执行匿名函数,执行到 obj.getage() 的时候,getage函数里的this,指向的就是obj了,所以能打印出10。还是遵循 谁调用产生 this指针的函数,this就指向谁的规则