web培训中JavaScriptthis指的是什么?这篇说的真的太清楚了!

因为工作原因,经常关注有关互联网行业的***动态。说起到WEB的工作,如今这个市场行情,做WEB前端开发工作,没有经验不好找工作,但是基础的操作都不会,按时压根就找不到工作啊。所以,由于市场的引导,web培训机构也是层出不穷。web培训发展已十余年之久,服务与课程体系都已成熟。而且还是包就业哦!那么***小编先给大家说点实际的吧!我们***带来的是web培训课程中JavaScript
this的应用。
要说JavaScript这门语言**容易让人困惑的知识点,this关键词肯定算一个。JavaScript
语言面世多年,一直在进化完善,现在在服务器上还可以通过node.js来跑JavaScript。显然,这门语言还会活很久。
所以说,我一直相信,如果你是一个JavaScript开发者或者说web开发者,学好JavaScript
的运作原理以及语言特点肯定对你以后大有好处。
开始之前
在开始正文之前,我强烈推荐你先掌握好下面的知识:
变量作用域和作用域提升
JavaScript的函数
闭包
如果没有对这些基础知识掌握踏实,直接讨论JavaScript的this关键词只会让你感到更加地困惑和挫败。
什么是this?
在我开始讲解前,如果你学过一门基于类的面向对象编程语言(比如C#,Java,C++),那请将你对this
这个关键词应该是做什么用的先入为主的概念扔到垃圾桶里。JavaScript的this关键词是很不一样,因为JavaScript
本来就不是一门基于类的面向对象编程语言。
虽说ES6里面JavaScript提供了类这个特性给我们用,但它只是一个语法糖,一个基于原型继承的语法糖。
this就是一个指针,指向我们调用函数的对象。
我难以强调上一句话有多重要。请记住,在Class添加到ES6之前,JavaScript中没有Class这种东西。Class
只不过是一个将对象串在一起表现得像类继承一样的语法糖,以一种我们已经习惯的写法。所有的魔法背后都是用原型链编织起来的。
如果上面的话不好理解,那你可以这样想,this的上下文跟英语句子的表达很相似。比如下面的例子
Bob.callPerson(John);
就可以用英语写成“BobcalledapersonnamedJohn”。由于callPerson()是Bob发起的,那this
就指向Bob。我们将在下面的章节深入更多的细节。到了这篇文章结束时,你会对this关键词有更好的理解(和信心)。
执行上下文
执行上下文是语言规范中的一个概念,用通俗的话讲,大致等同于函数的执行“环境”。具体的有:变量作用域(和
作用域链条,闭包里面来自外部作用域的变量),函数参数,以及this对象的值。
引自:Stackoverflow.com
记住,现在起,我们专注于查明this关键词到底指向哪。因此,我们现在要思考的就一个问题:
是什么调用函数?是哪个对象调用了函数?
为了理解这个关键概念,我们来测一下下面的代码。
varperson={
name:""Jay"",
greet:function(){
console.log(""hello,""+this.name);
}
};
person.greet();
谁调用了greet函数?是person这个对象对吧?在greet()调用的左边是一个person对象,那么this关键词就指向
person,this.name就等于""Jay""。现在,还是用上面的例子,我加点料:
vargreet=person.greet;//将函数引用存起来;
greet();//调用函数
你觉得在这种情况下控制台会输出什么?“Jay”?undefined?还是别的?
正确答案是undefined。如果你对这个结果感到惊讶,不必惭愧。你即将学习的东西将帮助你在JavaScript旅程中打开关键的大门。
this的值并不是由函数定义放在哪个对象里面决定,而是函数执行时由谁来唤起决定。
对于这个意外的结果我们暂且压下,继续看下去。(感觉前后衔接得不够流畅)
带着这个困惑,我们接着测试下this三种不同的定义方式。
我为什么要学this?
如果上面的简单介绍没有说服你来深入探索this关键词,那我用这节来讲讲为什么要学。
考虑这样一个重要问题,假设开发者,比如DouglasCrockford(译者注:JavaScript领域必知牛人),不再使用new和
this,转而使用完完全全的函数式写法来做代码复用,会怎样?
事实上,基于JavaScript内置的现成的原型继承功能,我们已经使用并且将继续***使用new和this关键词来实现代码复用。
理由一,如果只能使用自己写过的代码,你是没法工作的。现有的代码以及你读到这句话时别人正在写的代码都很有可能包含this
关键词。那么学习怎么用好它是不是很有用呢?
因此,即使你不打算在你的代码库中使用它,深入掌握this的原理也能让你在接手别人的代码理解其逻辑时事半功倍。
理由二,拓展你的编码视野和技能。使用不同的设计模式会加深你对代码的理解,怎么去看、怎么去读、怎么去写、怎么去理解。我们写代码不仅是给机器去解析,还是写给我们自己看的。这不仅适用于
JavaScript,对其他编程语言亦是如此。
随着对编程理念的逐步深入理解,它会逐渐塑造你的编码风格,不管你用的是什么语言什么框架。
就像毕加索会为了获得灵感而涉足那些他并不是很赞同很感兴趣的领域,学习this会拓展你的知识,加深对代码的理解。
找出this的指向
上一节我们已经对this做了测试。但是这块知识实在重要,我们需要再好好琢磨一下。在此之前,我想用下面的代码给你出个题:
varname=""JayGlobal"";
varperson={
name:'JayPerson',
details:{
name:'JayDetails',
print:function(){
returnthis.name;
}
},
print:function(){
returnthis.name;
}
};
console.log(person.details.print());//?
console.log(person.print());//?
varname1=person.print;
varname2=person.details;
console.log(name1());//?
console.log(name2.print())//?
console.log()将会输出什么,把你的答案写下来。如果你还想不清楚,复习下上一节。
准备好了吗?放松心情,我们来看下面的答案。
答案和解析
person.details.print()
首先,谁调用了print函数?在JavaScript中我们都是从左读到右。于是this指向details而不是
person。这是一个很重要的区别,如果你对这个感到陌生,那赶紧把它记下。
print作为details对象的一个key,指向一个返回this.name的函数。既然我们已经找出this指向details
,那函数的输出就应该是'JayDetails'。
person.print()
再来一次,找出this的指向。print()是被person对象调用的,没错吧?
在这种情况,person里的print函数返回this.name。this现在指向person了,那'JayPerson'
就是返回值。
console.log(name1)
这一题就有点狡猾了。在上一行有这样一句代码:
varname1=person.print;
如果你是通过这句来思考的,我不会怪你。很遗憾,这样去想是错的。要记住,this关键词是在函数调用时才做绑定的。name1()
前面是什么?什么都没有。因此this关键词就将指向全局的window对象去。
因此,答案是'JayGlobal'。
name2.print()
看一下name2指向哪个对象,是details对象没错吧?
所以下面这句会打印出什么呢?如果到目前为止的所有小点你都理解了,那这里稍微思考下你就自然有答案了。
console.log(name2.print())//??
答案是'JayDetails',因为print是name2调起的,而name2指向details。
词法作用域
你可能会问:“什么是词法作用域?”
逗我呢,我们不是在探讨this关键词吗,这个又是哪里冒出来的?好吧,当我们用起ES6的箭头函数,这个就要考虑了。如果你已经写了不止一年的
JavaScript,那你很可能已经碰到箭头函数。随着ES6逐渐成为现实标准,箭头函数也变得越来越常用。
JavaScript的词法作用域并不好懂。如果你理解闭包,那要理解这个概念就容易多了。来看下下面的小段代码。
//outerFn的词法作用域
varouterFn=function(){
varn=5;
console.log(innerItem);
//innerFn的词法作用域
varinnerFn=function(){
varinnerItem=""inner"";//错了。只能坐着电梯向上,不能向下。
console.log(n);
};
returninnerFn;
};
outerFn()();
想象一下一栋楼里面有一架只能向上走的诡异电梯。
#FormatImgID_0#建筑的顶层就是全局windows对象。如果你现在在一楼,你就可以看到并访问那些放在楼上的东西,比如放在二楼的outerFn和放在三楼的
window对象。
这就是为什么我们执行代码outerFn()(),它在控制台打出了5而不是undefined。
然而,当我们试着在outerFn词法作用域下打出日志innerItem,我们遇到了下面的报错。请记住,JavaScript
的词法作用域就好像建筑里面那个只能向上走的诡异电梯。由于outerFn的词法作用域在innerFn上面,所以它不能向下走到innerFn
的词法作用域里面并拿到里面的值。这就是触发下面报错的原因:
test.html:304UncaughtReferenceError:innerItemisnotdefined
atouterFn(test.html:304)
attest.html:313
this和usestrict
为了让JavaScript更加健壮及尽量减少人为出错,ES5引进了严格模式。一个典型的例子就是this
在严格模式下的表现。你如果想按照严格模式来写代码,你只需要在你正在写的代码的作用域**顶端加上这么一行""usestrict;""。
记住,传统的JavaScript只有函数作用域,没有块作用域。举个例子:
functionstrict(){
//函数级严格模式写法
'usestrict';
functionnested(){return'AndsoamI!';}
return""Hi!I'mastrictmodefunction!""+nested();
}
functionnotStrict(){return""I'mnotstrict."";}
代码片段来自MozillaDeveloperNetwork。
不过呢,ES6里面通过let关键词提供了块作用域的特性。
现在,来看一段简单代码,看下this在严格模式和非严格模式下会怎么表现。在继续之前,请将下面的代码运行一下。
(function(){
""usestrict"";
console.log(this);
})();
(function(){
//不使用严格模式
console.log(this);
})();
正如你看到的,this在严格模式下指向undefined。相对的,非严格模式下this指向全局变量window。大部分情况下,开发者使用
this,并不希望它指向全局window对象。严格模式帮我们在使用this关键词时,尽量少做搬起石头砸自己脚的蠢事。
举个例子,如果全局的window对象刚好有一个key的名字和你希望访问到的对象的key相同,会怎样?上代码吧:
(function(){
//""usestrict"";
varitem={
document:""Mydocument"",
getDoc:function(){
returnthis.document;
}
}
vargetDoc=item.getDoc;
console.log(getDoc());
})();
这段代码有两个问题。
this将不会指向item。
如果程序在非严格模式下运行,将不会有错误抛出,因为全局的window对象也有一个名为document的属性。
在这个简单示例中,因为代码较短也就不会形成大问题。
如果你是在生产环境像上面那样写,当用到getDoc
返回的数据时,你将收获一堆难以定位的报错。如果你代码库比较大,对象间互动比较多,那问题就更严重了。
值得庆幸的是,如果我们是在严格模式下跑这段代码,由于this是undefined,于是立刻就有一个报错抛给我们:
test.html:312UncaughtTypeError:Cannotreadproperty'document'of
undefinedatgetDoc(test.html:312)attest.html:316attest.html:317
明确设置执行上下文
先前假定大家都对执行上下文不熟,于是我们聊了很多关于执行上下文和this的知识。
让人欢喜让人忧的是,在JavaScript中通过使用内置的特性开发者就可以直接操作执行上下文了。这些特性包括:
bind():不需要执行函数就可以将this的值准确设置到你选择的一个对象上。还可以通过逗号隔开传递多个参数,如func.bind(this,
param1,param2,...)。
apply():将this的值准确设置到你选择的一个对象上。第二个参数是一个数组,数组的每一项是你希望传递给函数的参数。***,执行函数。
call():将this的值准确设置到你选择的一个对象上,然后想bind
一样通过逗号分隔传递多个参数给函数。如:print.call(this,param1,param2,...)。***,执行函数。
上面提到的所有内置函数都有一个共同点,就是它们都是用来将this
关键词指向到其他地方。这些特性可以让我们玩一些*操作。只是呢,这个话题太广了都够写好几篇文章了,所以简洁起见,这篇文章我不打算展开它的实际应用。
重点:上面那三个函数,只有bind()在设置好this关键词后不立刻执行函数。
this和箭头函数
在ES6里面,不管你喜欢与否,箭头函数被引入了进来。对于那些还没用惯箭头函数或者新学JavaScript的人来说,当箭头函数和this
关键词混合使用时会发生什么,这个点可能会给你带来小小的困惑和淡淡的忧伤。那这个小节就是为你们准备的!
当涉及到this关键词,箭头函数和普通函数主要的不同是什么?
答案:
箭头函数按词法作用域来绑定它的上下文,所以this实际上会引用到原来的上下文。
引自:hackernoon.com
我实在没法给出比这个更好的总结。
箭头函数保持它当前执行上下文的词法作用域不变,而普通函数则不会。换句话说,箭头函数从包含它的词法作用域中继承到了this的值。
我们不妨来测试一些代码片段,确保你真的理解了。想清楚这块知识点未来会让你少点***,因为你会发现this关键词和箭头函数太经常一起用了。
示例
仔细阅读下面的代码片段。
varobject={
data:[1,2,3],
dataDouble:[1,2,3],
double:function(){
console.log(""thisinsideofouterFndouble()"");
console.log(this);
returnthis.data.map(function(item){
console.log(this);//这里的this是什么??
returnitem*2;
});
},
doubleArrow:function(){
console.log(""thisinsideofouterFndoubleArrow()"");
console.log(this);
returnthis.dataDouble.map(item=>{
console.log(this);//这里的this是什么??
returnitem*2;
});
}
};
object.double();
object.doubleArrow();
如果我们看执行上下文,那这两个函数都是被object调用的。所以,就此断定这两个函数里面的this都指向object
不为过吧?是的,但我建议你拷贝这段代码然后自己测一下。
这里有个大问题:
arrow()和doubleArrow()里面的map函数里面的this又指向哪里呢?
上一张图已经给了一个**的提示。如果你还不确定,那请花5分钟将我们上一节讨论的内容再好好想想。然后,根据你的理解,在实际执行代码前把你认为的this
应该指向哪里写下来。在下一节我们将会回答这个问题。
回顾执行上下文
这个标题已经把答案泄露出来了。在你看不到的地方,map函数对调用它的数组进行遍历,将数组的每一项传到回调函数里面并把执行结果返回。如果你对
JavaScript的map函数不太了解或有所好奇,可以读读这个了解更多。
总之,由于map()是被this.data调起的,于是this将指向那个存储在data这个key里面的数组,即
[1,2,3]。同样的逻辑,this.dataDouble应该指向另一个数组,值为[1,2,3]。
现在,如果函数是object调用的,我们已经确定this指向object对吧?好,那来看看下面的代码片段。
double:function(){
returnthis.data.map(function(item){
console.log(this);//这里的this是什么??
returnitem*2;
});
}
这里有个很有迷惑性的问题:传给map()的那个匿名函数是谁调用的?答案是:这里没有一个对象是。为了看得更明白,这里给出一个map
函数的基本实现。
//Array.mappolyfill
if(Array.prototype.map===undefined){
Array.prototype.map=function(fn){
varrv=[];
for(vari=0,l=this.length;i rv.push(fn(this[i])); returnrv; }; } fn(this[i]));前面有什么对象吗?没。因此,this关键词指向全局的windows对象。那,为什么 this.dataDouble.map使用了箭头函数会使得this指向object呢? 我想再说一遍这句话,因为它实在很重要: 箭头函数按词法作用域将它的上下文绑定到原来的上下文 现在,你可能会问:原来的上下文是什么?问得好! 谁是doubleArrow()的初始调用者?就是object对吧?那它就是原来的上下文 我们学习任何一门技术都得先从企业需求的角度来分析,到底这个市场需要怎样的前端开发人才,这样才有让我们这些开发从业者有机会去思考满足需求这个问题,进而才能正确地确立一个可行的职业发展方向,***达到自己想要的目标。web的前景无限啊!web培训大门为你敞开,欢迎你的加入。
非本网作品均来自互联网,转载目的在于传递更多信息,并不**本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其他问题,请及时与本网联系,我们将及时删除内容。