学会JS的this这一篇就够了,根本不用记

以前看某本书上讲:掌握了JS中this的用法才算真正的跨过了JS的门槛 我深以为是! 

但是

JS的this却并不是那么简单的内容,许多同学都很困惑,也肯定有同学像我曾经一样查阅各种资料想知道How to understand this of javascript。很幸运的是,网上有非常非常多的文章关于this的,简直就是琳琅满目…

曾经,我看到某些文章非常开心,因为他们讲得确实非常好,以至于我确信我已掌握了this的用法。然而可能是由于我太笨了,经常过一段时间就忘得一干二净了,这真的是一件很尴尬的事情……直到后来我仔细地琢磨又琢磨,终于感觉我可能以后不会再忘记了。

所以想把我琢磨的内容和大家分享一下,说到this,就不得不提到function,相信看过其它类似文章的同学也知道,正是由于调用function的对象不同,才导致了this的指向不同。

所以以前老是去记忆每种调用function的情况所对应的this,因为情况有限而且很少,所以这当然是可行的——对于聪明人来说。所以我不得不思考另外一些方式来让我记住。

那么首先我们需要明确的一个事情是:

function也是对象

同时我们还需要明确的一个事情是:

function执行时是在某个特定的上下文中执行的。那什么是上下文呢?打个比方,比如你练会了辟邪剑谱,那这时候你的掌门让你用辟邪剑谱砍人。

如果仅仅是这样的话,你是没法完成这个任务的,因为你必须得知道要砍谁吧,其次去哪儿砍吧,那么是个地下通道还是一望无尽的大草原,要是地下通道你走路都困难,还怎么用辟邪剑谱呢对吧。

这就是上下文,函数执行时它也需要一些额外的信息来支撑它的运行。那么既然function是对象的话,就会有方法。而function中最核心的方法是call方法。因此我们就从这儿入手。

call方法

先来看一下如何使用call方法:

functionsay(content){console.log("From "+this+": Hello "+ content); }

say.call("Bob","World");//==> From Bob: Hello World

接下来仔细分析一下call的用法:

Step1: 把第二个到最后一个参数作为函数执行时要传入的参数

Step2: 把函数执行时的this指向第一个参数

Step3: 在上面这个特殊的上下文中执行函数

上面例子中,我们通过call方法,让say函数执行时的this指向"Bob",然后把"World"作为参数传进去,所以输出结果是可以预见的。

js执行函数时会默认完成以上的步骤,你可以把直接调用函数理解为一种语法糖

比如:

functionsay(word){console.log(world); }

say("Hello world"); say.call(window,"Hello world");

以上可以把say("Hello world")看做是say.call(window,"Hello world")的语法糖。

这个结论非常关键 所以以后每次看见functionName(xxx)的时候,你需要马上在脑海中把它替换为functionName.call(window,xxxx),这对你理解this的指向非常重要。

不过也有例外,在ES5的strict mode中call的第一个参数不是window而是undefined。之后的例子我假设总是不在strictmode下,但你需要记住strictmode有一点儿不同。

对于匿名函数来说,上面的结论也是成立的

(function(name){ // })("aa"); //等价于 (function(name){ // }).call(window,"aa");

函数作为对象的方法被调用

直接来看代码:

varperson = {

name:"caibirdme",

run:function(time){

console.log(this.name +"has been running for over "+ time+" minutes");

} };

person.run(30);//==> caibirdme has been running for over 30 minutes 

//等价于 person.run.call(person, 30);

 // the same

你会发现这里call的第一个参数是person而不是window。

当你明白了这两点,下意识地把函数调用翻译成foo.call()的形式,明确call的第一个参数,那基本上this的问题就难不住你了。

还是来举几个例子吧

例一:

functionhello(thing){

console.log(this +" says hello "+ thing);

}

person = { name:"caibirdme"}

person.hello = hello;

person.hello("world")

// 相当于执行 person.hello.call(person,"world")

//caibirdme says hello world hello("world")

// 相当于执行 hello.call(window,"world")

//[object DOMWindow]world

例二:

varobj = {

x:20,

f:function(){

console.log(this.x);

} };

obj.f();// obj.f.call(obj) //==> 20

obj.innerobj = {

x:30,

f:function(){

console.log(this.x);

} }

obj.innerobj.f();// obj.innerobj.f.call(obj.innerobj) // ==> 30

例三:

varx =10;

varobj = {

x:20,f:function(){

console.log(this.x);//this equals obj // ==> 20

varfoo =function(){

console.log(this.x);

}

foo();// foo.call(window) //foo中this被指定为window,所以==> 10 } }; obj.f();

 // obj.f.call(obj)// ==> 20 10

由例三引出一个非常common的问题,如果我想让foo输出20怎么办?这时候需要用到一点小技巧

例四:

varx =10;

varobj = {

x:20,

f:function(){

console.log(this.x);

varthat =this;//使用that保留当前函数执行上下文的this

varfoo =function(){console.log(that.x); }

//此时foo函数中的this仍然指向window,但我们使用that取得obj foo(); 

// foo.call(window) } };

obj.f();

obj.f.call(obj)// ==> 20 20

再来一个稍微难一点点的(但其实用call替换法一点儿也不难)

例五:

varx =10;

varobj = {

x:20,

f:function(){

console.log(this.x);

} };

obj.f();// obj.f.call(obj) // ==> 20

varfOut = obj.f;

fOut();// fOut.call(window) //==> 10

varobj2 = {x:30,f: obj.f } obj2.f();// obj2.f.call(obj2) //==> 30

例五有些同学会可能出错的原因,是没有明确我上面说的:

this是在执行是才会被确认的

他可能会认为说obj.f那个函数定义在obj里面,那this就该指向obj。

如果看完这篇文章你还这么想的话,我会觉得我的表达水平太失败了…… 用于构造函数 先看一段代码:

funcperson(name){ this.name = name; }

varcaibirdme =newperson("deen");

// caibirdme.name == deen

我上面也说了,函数在用作构造函数时同样可以用call方法去代替,那这里怎么代替呢?

这里你又需要明确一点:

newconstrcut()是一种创建对象的语法糖 它等价于

functionperson(name){this.name = name; }

varfoo =newperson("deen");//通过new创建了一个对象 //new是一种语法糖,

newperson等价于varbar = (function(name){var_newObj = {constructor: person,__proto__: person.prototype, };

_newObj.constructor(name);// _newObj.constructor.call(_newObj, name) return _newObj; })();

So you can see……为什么new的时候this就指向新的对象了吧?

通过我这篇文章,我希望学会通过把一个函数调用替换成funcName.call的形式,从而理解运行时上下文中this到底指向谁。

总结来说就是下面两个等价变形:

foo() ---> foo.call(window)

obj.foo() --> obj.foo.call(obj) 只要理解以上两个变形,this就不再是问题啦!!

希望我的这种方法对各位同学认识this有所帮助,不要再像我曾经一样掉入this的坑中,相关面试题也不再怕怕啦,哈哈。如果还有问题的话,欢迎下面留言。

如果您在学习编程的过程中遇到难题,欢迎关注微信公众号【筑梦编程】,大家一起交流解决!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容