You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ES6新增一种特殊函数类型:箭头函数,箭头函数无法使用上述四条规则,在箭头函数自己的作用域中没有this,而是根据外层(函数或者全局)作用域(词法作用域)来决定this。 也就是说,箭头函数里的this不再有迷惑性,被永远封印到当前词法作用域之中,称作 Lexical this ,在代码运行前就可以确定。
this的作用
JavaScript 允许在函数体内部,引用当前环境的其他变量。
上面代码中,函数体里面使用了变量x。该变量由运行环境提供。
在不同的地方去调用,x都要从当前位置的作用域链查询。
现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
上面代码中,函数体里面的this.x就是指当前运行环境的x。
this 指向当前函数执行的上下文对象。
this说白了就是找拥有当前上下文(context)的对象(context object),也就是说this指向调用位置的执行上下文,跟调用位置,怎样调用有关。跟函数在哪里定义没有关系,而且会随着函数调用方式的变化而变化,这就给函数提供了非常大的灵活性。
在了解this调用方式之前,先来看一道题
通过查阅资料,JavaScript中函数的调用方式有3种
大部分人都只用到过前两种情况,而且认为前两者优于第三者。但其实第三种形式才是正常的调用形式。
其它两种都是语法糖,可以等价的变为call形式。
func(p1, p2)等价于 func.call(undefined, p1, p2);
obj.child.method(p1, p2) 等价于 obj.child.method.call(obj.child, p1, p2);
这么看我们的函数调用只有一种形式:
这时候是不是就知道this是什么了,就是上面的context。
回到刚才的那个题。
obj.foo() 转化为call的形式就是obj.foo.call(obj),所以this指向了obj
bar() 转化为call的形式就是bar.call(),由于没有传 context,所以 this 默认就是 window,本文的所有分析,都在浏览器环境,不考虑node.js环境。
下面内容来自《你不知道的JavsScript》,总结下常见的this调用情况。
5种this绑定
1、默认绑定
2、隐式绑定
3、显式绑定
4、new绑定
5、箭头函数绑定
默认绑定
独立函数调用,可以把默认绑定看作是无法应用其他规则时的默认规则,this指向全局对象。
严格模式下,不能将全局对象用于默认绑定,this会绑定到undefined。只有函数运行在非严格模式下,默认绑定才能绑定到全局对象。
bar()就是默认绑定,非下面要说明的的隐式绑定,函数调用的时候,前面没有任何修饰调用,也可以用之前的 call函数调用形式理解,bar.call(),所以输出结果是hello。
注意,有坑
setTimeOut和setInterval中的回调函数,是延时多少秒后,直接在执行队列中调用这个回调函数,这些函数中传递的函数中的this指向,在非严格模式指向的是全局对象,也是默认绑定
隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数中的this绑定到这个上下文对象。对象属性引用链中只有上一层或者说最后一层在调用中起作用。 隐式调用的意思是,函数调用时拥有一个上下文对象,就好像这个函数是属于该对象的一样。
隐式绑定也是坑最多的一种绑定,非常容易让人迷惑。
需要说明的一点是,最后一个调用该函数的对象是传到函数的上下文对象。
还有一种方式,在间接引用下,调用这个函数会应用默认绑定规则。间接引用最容易在赋值时发生。
显式绑定
通过call(..) 或者 apply(..)方法。第一个参数是一个对象,在调用函数时将这个对象绑定到this。因为直接指定this的绑定对象,称之为显示绑定。
new绑定
在JS中,构造函数只是使用new操作符时被调用的普通函数,他们不属于某个类,也不会实例化一个类。
包括内置对象函数(比如Number(..))在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用。
实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。 关于new,不再赘述,在《JavaScript原型原型链》中有了详细实现过程。
使用new来调用foo(..)时,会构造一个新对象并把它(bar)绑定到foo(..)调用中的this。
箭头函数绑定
ES6新增一种特殊函数类型:箭头函数,箭头函数无法使用上述四条规则,在箭头函数自己的作用域中没有this,而是根据外层(函数或者全局)作用域(词法作用域)来决定this。 也就是说,箭头函数里的this不再有迷惑性,被永远封印到当前词法作用域之中,称作 Lexical this ,在代码运行前就可以确定。
这样的好处就是方便让回调函数的this使用当前的作用域,不怕引起混淆。
所以对于箭头函数,只要看它在哪个环境(上下文)创建的就行。
上面案例变形一下
不管怎么变化,在全局环境创建的fn中的this永远指向window。
foo 内部创建的箭头函数会捕获调用时foo的this。由于foo的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改(new也不行),也可以理解为箭头函数无法显式使用call,apply修改this。
再来一个题巩固一下
如果这题没做对,建议再看一下上面的讲解
说完了这几种this绑定方式,再说说这几种方式的优先级
new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
new 的优先级最高
再来做题巩固
这里要拓展一个知识点,
函数中arguments.length表示实参的数量,arguments.callee.length表示形参的数量。
再来一个比较难的
在boss2.returnThis里,使用this的函数是boss1.returnThis,所以this绑定到boss1;
在boss3.returnThis里,使用this的函数是returnThis,returnThis直接调用,this属于方式1默认绑定。
那么,要想把this绑定到boss2怎么做呢?
没错,只要让使用this的函数是属于boss2就行。
最后, 回顾一下call,apply函数的作用和调用方式,
call,apply的第一个实参是要调用函数的母对象,他就是调用上下文,在函数体内通过this来获取对他的引用。
fn.call(window)
功能类似
在window上绑定一个属性,保留对fn函数的引用,调用这个属性,将临时方法再去掉。
思路有了,就可以实现call,apply了
因为《高程》,《犀牛书》,《你不知道的js》中给出的练习题太少了,所以这篇文章借鉴了很多大佬们的博客,这里给出原文链接
https://juejin.im/post/5d6e5f77f265da03e05b2fd9#heading-19
https://juejin.im/post/5ba24761e51d450e735e51f0
https://zhuanlan.zhihu.com/p/23804247
http://www.ruanyifeng.com/blog/2018/06/javascript-this.html
https://www.cnblogs.com/front-Thinking/p/4364337.html(摘自《你不知道的JavaScript》)
https://blog.crimx.com/2016/05/12/understanding-this/
this 练习题
The text was updated successfully, but these errors were encountered: