当前位置: 首页 > Web前端 > JavaScript

用JavaScript深入浅出的解释

时间:2023-03-27 12:27:52 JavaScript

作者最近看了你所不知道的JavaScript卷,个人觉得这方面的解释很精彩。JavaScript中的this是一个核心概念,有些同学会比较模糊,害怕。原因是现在讨论这个的文章很多,让我们觉得这是不规则的,像鬼一样。如果你对此还不了解,或者对此感到模糊,这篇文章就是专门为你准备的。如果你对它比较熟悉,那么你也可以把它当作复习来巩固你的知识。本文为读书笔记。当然,我也补充了很多个人的理解。我想肯定对大家有帮助。执行上下文在理解这个之前,我们先来看看什么是执行上下文。简而言之,执行上下文是评估和执行JavaScript代码的环境。抽象概念。每当Javascript代码运行时,它都会在执行上下文中运行。JavaScript中有三种类型的执行上下文。全局执行上下文——这是默认或基本上下文,任何不在函数内部的代码都在全局上下文中。它做了两件事:创建一个全局窗口对象(在浏览器的情况下),并将其值设置为等于全局对象。程序中只有一个全局执行上下文函数执行上下文——每次调用一个函数时,都会为该函数创建一个新的上下文。每个函数都有自己的执行上下文,但它是在调用函数时创建的。函数上下文可以有任意数量的eval函数执行上下文——在eval函数内执行的代码也将有自己的执行上下文,但由于JavaScript开发人员不经常使用eval,我不会在这里讨论它。我们先下个结论。在非严格模式和严格模式下,this指向顶级对象(浏览器中的窗口)。console.log(这个===窗口);//true'usestrict'console.log(this===window);//truethis.name='vnues';console.log(this.name);//在vnues之后,我们讨论的更多的是函数执行上下文,这是什么?为什么要用这个This是在运行时绑定的,而不是在编写时绑定的。它的上下文取决于调用函数时的各种条件。请记住:this的绑定与函数声明的位置无关,它只取决于Howfunctionsarecalled当函数被调用时,会创建一个活动记录(有时称为执行上下文)。这条记录会包含函数调用位置(调用栈)、函数的调用方式、传入的参数等信息。this是记录的属性之一,在函数执行过程中会用到,看个例子就明白为什么用this了。有时,我们需要实现类似下面的代码:console.log(greeting);}varme={name:"Kyle"};speak(me);//你好,我是凯尔。此代码的问题在于您需要显示传递的上下文对象。如果代码变得越来越复杂,这种方法会让你的代码看起来很混乱。使用这个更优雅.call(这个);console.log(问候语);}speak.call(me);//你好,我是KYLE这四种绑定规则我们来看下函数上下文中的绑定规则,有以下四种默认绑定隐式绑定显式绑定定义新的绑定默认绑定最常用的函数调用类型:独立函数调用,其中也是优先级最低的,this指向全局对象。注意:如果使用严格模式,全局对象将无法使用默认绑定,所以this会绑定到undefined,如下图vara=2;//全局对象中的变量声明functionfoo(){console.log(this.a);//输出a}functionbar(){'usestrict';控制台日志(这个);//未定义}foo();bar();implicitbinding对我们来说是可以的我一开始说的:this的绑定与函数声明的位置无关,它只取决于函数如何被调用我们来看一个例子:functionfoo(){console.log(this.a);}varobj={a:2,foo:foo};obj.foo();//2调用obj.foo()时,this指向obj对象。当函数引用具有上下文对象时,隐式绑定规则会将函数调用中的this绑定到上下文对象。因为调用foo()时this绑定到obj,this.a和obj.a是一样的记住:只有对象属性引用链中最顶层或最后一层才会影响调用位置functionfoo(){console.log(这是);}varobj2={a:42,foo:foo};varobj1={a:2,obj2:obj2};obj1.obj2.foo();//42间接引用另一个请注意,可以(有意或无意)创建对函数的“间接引用”,在这种情况下调用该函数将应用默认绑定规则functionfoo(){console.log(this.a);}vara=2;varo={a:3,foo:foo};varp={a:4};o.foo();//3(p.foo=o.foo)();//2另外需要注意的是,你可能(有意或无意地)创建一个函数的“间接引用”,在这种情况下调用这个函数将应用默认的绑定规则赋值表达式p.foo=o的返回值。foo是对目标函数的引用,因此调用点是foo()而不是p.foo()或o.foo()。按照我们之前所说的,这里会应用默认绑定,会应用显式绑定。在分析隐式绑定时,我们必须在一个对象内部包含一个指向函数的属性,通过这个属性间接引用函数,从而使this间接(隐式)绑定到这个对象上。那么,如果我们不想在对象内部包含函数引用,而是想强制在对象上调用函数怎么办?Javascript提供了apply、call和bind方法让我们实现差异化。区别在于call()和apply()是立即执行函数,接受不同的参数:call(this,arg1,arg2,...)apply(this,[arg1,arg2,...])和bind()创建一个新的包装函数,并返回,而不是立即执行bind(this,arg1,arg2,...)请看下面的例子:functionfoo(b){console.log(this.a+''+b);}varobj={a:2,foo:foo};变量a=1;foo('戈帕尔');//1Gopalobj.foo('Gopal');//2Gopalfoo.call(obj,'Gopal');//2Gopalfoo.apply(obj,['Gopal']);//2Gopalletbar=foo.bind(obj,'Gopal');酒吧();//2Gopal如果把null或者undefined作为this的绑定对象传给call,apply或者bind会被忽略,调用的时候会忽略这些值,实际应用的是默认的绑定规则functionfoo(){console.log(this.a);}vara=2;foo.call(null);//2使用这个用法来使用apply(..)“展开”一个数组,并将其作为参数传递给一个函数。类似地,bind(..)可以柯里化参数(预设一些参数)functionfoo(a,b){console.log("a:"+a+",b:"+b);}//将数组“扩展”为参数foo.apply(null,[2,3]);//a:2,b:3//使用bind(..)柯里化varbar=foo.bind(null,2);酒吧(3);//a:2,b:3newbinding当我们使用构造函数创建实例时,这个实例指向什么?我们先看看在使用new调用函数时,或者调用构造函数时,会发生什么,如下所示:创建(或构造)一个全新的对象。这个新对象会执行[[prototype]]连接,将对象(实例)的__proto__绑定到构造函数的原型上。这个新对象将绑定到函数调用的this。如果函数没有返回其他对象,new表达式中的函数调用会自动返回这个新对象。原理类似下面:functioncreate(ctr){//创建一个空对象letobj=newObject()//链接到构造函数的原型对象letCon=[].shift.call(arguments)obj.__proto__=Con.prototype//绑定这个letresult=Con.apply(obj,arguments);//如果返回的是对象,直接返回对象,否则返回实例returntypeofresult==='object'?result:obj;}注:letresult=Con.apply(obj,arguments);实际上意味着新对象将被绑定到函数callthisfunctionFoo(a){this.a=a;}varbar=newFoo(2);控制台日志(bar.a);//2个特例——箭头函数我们之前介绍的四个规则已经可以包含所有的普通函数了。但是ES6引入了一种不能使用这些规则的特殊函数类型:箭头函数箭头函数没有使用this的四个标准规则,而是在定义时根据外层(函数或全局)作用域来确定this。也就是说箭头函数不会自己创建this,只会继承thisfunctionfoo(){//返回一个箭头函数//this继承自foo()return(a)=>{console.log(this。A);}};varobj1={a:2};varobj2={a:3};varbar=foo.call(obj1);bar.call(obj2);//2,not3!foo()内部创建的箭头函数在调用时捕获了foo()的this。由于foo()的this绑定了obj1,所以bar(指箭头函数)的this也绑定了obj1,箭头函数的绑定不能修改。(new也不行!)总结——这个优先级决定了它是否是箭头函数,如果是,则遵循箭头函数的规则;否则,如果要确定一个正在运行的函数的this绑定,就需要找到这个函数的直接调用位置。找到后,可以应用以下四个规则来判断this的绑定对象是new?调用的吗?绑定到新创建的对象是通过call还是apply(或bind)来调用?绑定到指定对象是由上下文对象调用的?设置为那个context对象默认:在严格模式下绑定到undefined,否则绑定到全局对象如下图:参考[[翻译]理解JavaScript中的执行上下文和执行栈](https://juejin.im/post/684490...)关于滚动你不知道的JavaScript