您对JavaScript的了解可以通过您对此的理解来很好地判断。 人们发现很难理解这一事实,是因为它在多种多样的场景中使用。
让我们一劳永逸地使它们正确。
如果我要从字面意义上谈论这一点,它只是意味着-这件事就在我眼前。 “这件夹克很酷”,我姐姐手里拿着那件夹克。 我回答说:“不,这更好!” 我指的是我附近的夹克,她指的是她附近的夹克,因此是在各自的陈述中使用它 。
此值取决于
1.执行上下文
2.调用
执行上下文
执行上下文是代码在其中运行的环境。 例如,一个函数的局部变量只能在该函数中访问,而不能在该函数外部访问。 这称为功能级别作用域或本地执行上下文。 在全局范围内声明的变量(在任何函数之外)都可以在所有基础函数中访问。 这称为全局执行上下文 。
让我们考虑这个示例,以了解它们之间的区别-
这在默认情况下,全局执行环境的价值是窗口对象。
在任何本地上下文中, 此值取决于函数的调用方式。
一,要求在全球范围内的功能将有它的这个值作为 window
二。 调用运行JavaScript语句在严格模式下的函数必须以此为未定义
三, 调用对象内部的函数
功能 add 是对象的属性之一 obj 。 当我们调用引用obj add时 作为obj.add() ,它在obj的上下文中被调用。 属性a和b在obj的上下文中具有确定的值,因此定义了obj.add()结果。
cacheAdd变量存储add函数的引用。 如果我们稍后执行此函数,它将在调用它的上下文中运行。 在我们的示例中,我们在全局上下文中调用cacheAdd ,因此它将有一个全局对象作为this引用。 由于a和b未在全局上下文中定义,因此我们将a+b的值设为NaN 。
为了证明这一点,让我们在全局上下文中定义a和b 。
obj.add()值为3,因为obj中定义的a和b的值分别为1和2。 当我们将add缓存在变量中然后在全局上下文中执行后运行add ,由于a和b在全局上下文中的值分别为3和4,因此它的值为7。 因此,使用this函数的函数可能会根据执行上下文的不同而给出不同的结果。
让我们尝试另一个有趣的例子-
在这种情况下,add函数返回另一个函数,并且在此内部函数中执行a+b的值。 obj.add()将返回一个具有一个console语句的函数。 因此,我们必须执行此返回的函数才能获得a&b的总和。 但是此返回的函数(在执行obj.add()返回)将在全局上下文中运行,因此a + b的值求值为7(全局上下文中a和b的值分别为3和4)。
在这里,添加功能会在500ms超时后打印a和b的值。 setTimeout在全局上下文中运行。
为什么setTimeout在全局上下文中运行? (我将在下一篇文章中对此进行详细说明。)
这需要调用该函数的直接对象的引用
在这里, parentObj具有childObj作为属性之一,并且在childObj上定义了add函数。 当我们将add称为parentObj.childObj.add()时 ,add函数采用childObj的此引用,并且无法从childObj的本地上下文访问变量a和b。 因此a和b的值是不确定的 。
链接到原型链
如果我们实例化parentObj来创建childObj ,则可以在childObj上下文中访问其属性。
使用Object.create
当在childObj的上下文中调用add函数时,它首先尝试在childObj的上下文中查找a,b和c的值 ,如果未定义这些值中的任何一个,它将查找其父上下文,在这种情况下是parentObj 。
使用新的运算符
在这种情况下,父级的属性在子级的上下文中可用。 正如我们在尾部控制台语句中看到的那样,子级的构造函数定义为Parent。 因此,它首先在被调用的上下文中检查值,然后查找其原型链。
这不是火箭科学!
默认情况下,代码在全局上下文中运行,即,它可以访问在window对象的上下文中定义的变量。 在上述每种情况下,我们仅将上下文限制为obj对象。 与之相关的所有操作都将以与上下文为window完全相同的方式执行。 即使在全局上下文中,如果未定义任何值,它也会查找原型链。
如我们所见, toString函数是在obj __proto__属性中定义的,这意味着它是在Object的原型上定义的(因为obj是通过将new运算符应用于Object来创建的)。 作为toString 未在obj的上下文中定义,而是查找其原型链。
让我们在这里解决一个有趣的问题-
她姐姐顽固地拒绝与我分享糖果,并支持她的主张,她说,她是该糖果的所有者。
这是她的糖果-
于是,不知何故,我已经修改this参考,她的作用whosCandyIsThis使用。 小孩子不知道JavaScript是一种非常规语言。 您可以随时按照自己的方式进行修改。
打电话,申请并绑定-我来了!
使用这3个漂亮的功能,您可以修改此参考的值。 在以上示例中, candy.whosCandyIsThis.call(myCandy) this修改 whosCandyIsThis参考whosCandyIsThis功能 myCandy 。
所谓的全部是什么? call this引用显式传递给函数。 在这种情况下,它会告诉whosCandyisThis功能使用this作为myCandy 。
call的第一个参数是this引用,如果我们必须传递任何其他参数,则可以执行以下操作
candy.whosCandyisThis.call(myCandy, true)`
让我们再考虑一个示例:
在这里,我们this引用显式传递给在hotel对象中定义的displayMenu函数,以相应地显示菜单。 (这很酷吗?)
但是,如果我想根据用户的位置显示菜单,该怎么办? 为此,我们将传递一个附加参数 displayMenu函数的location 。 让我们修改上面的代码以适合我们的要求。
call的第一个参数是this引用,然后您可以传入任意数量的所需参数。 现在, displayMenu函数很好地显示了根据location参数的菜单项列表。 (听起来不错?)
apply操作方式与call相同,唯一的区别是传递参数的方式。 Apply将第一个参数作为与调用相同的引用作为该引用,但其他参数将作为数组传递。
另一个可以修改this功能的功能 参考是bind 。 不像call apply 返回函数的结果, bind返回一个函数。 然后可以在以后的任何阶段调用此返回的函数。 它将在显式传递的bounded参数的上下文中运行。
hotel.displayMenu.bind(ccd)返回具有this ccd引用的函数。 当我们运行boundFunction ,它将在ccd上下文中运行 因此在位置A上打印Espresso。
我试图尝试自己的直觉。 如果我将已经绑定的函数绑定到其他引用,那么该函数是否可以在以后的绑定上下文中运行? 如果这是真的,我可以无休止地做到这一点,并尽我所能继续更改此参考! 这不是真的。 您只能将一个功能绑定一次。 在上面的示例中,我尝试再次用pizzaCentre绑定有界函数,但是当我执行此函数时,它将打印先前的结果,这意味着其引用仍指向第一个绑定( ccd )。
同样,JavaScript是一种非常规语言。 如果您可以自由修改任何东西,可以有保护完整性的守卫。 这样的杰作之一就是我们可爱的Arrow功能。
这就是它的工作方式。 简单明了。
为什么我们在本文中甚至谈论箭头功能? 他们有什么特别之处?
在arrow函数中, this的值是其封闭的词法上下文的值
词法上下文是块级作用域。
它不会随call , apply或bind而改变。
箭头函数保持其包围的词法上下文的绑定。
黑纱! 这只肥箭夺走了我的糖果。
如您所见, whosCandyIsThis被定义为箭头函数。 因此它将保持其词法上下文的约束。 在实例化Candy时, niks为她的糖果设置了所有者和口味。 whosCandyIsThis 函数会绑定到niks参考。
因此,当我尝试通过使用调用,应用或绑定传递此引用来显式调用它时,它将无法正常工作。 niks.whosCandyIsThis的绑定niks.whosCandyIsThis都无法更改niks.whosCandyIsThis功能!
您可能喜欢我之前有关服务人员的文章-
服务人员基础知识 — https://hackernoon.com/service-workers-62a7b14aa63a
构建Pokemon应用程序以评估浆果和服务人员的能力 — https://hackernoon.com/building-pokemon-app-to-evaluate-the-power-of-berries-service-worker-176d7c4e70e3
From: https://hackernoon.com/lets-get-this-this-once-and-for-all-f59d76438d34