深入JavaScript之Scope(作用域)以及Context(执行上下文)

Scope和Context: 咱俩不是一个东西

我是Scope(作用域),那是隔壁邻居Context(执行上下文)。总有人以为咱俩是一个东西,但是真的不是啊!记住啊,他是隔壁老C!我先介绍我自己吧。

Scope(作用域)借锤子

对于我Scope, 我先给自己贴个标签——和变量的可见性相关

(╯﹏╰)这么文绉绉的话是不是很不好理解,不好理解就对啦。要不然也不用举个栗子了:

        function community() {
            var hammer = 'The community owns a hammer';

            function myHome() {
                var wifi = 'This is my wifi, you cannot connect!'
                alert(hammer);
            }
            myHome();
            alert(wifi);
        }

        community();

这个栗子会报错,不用慌,让我给你解释解释:
首先呢,社区(community)里有个锤子(hammer),我家(myHome)呢,开着wifi。今天啊,我起了个早,寻思着找个锤子把我家门给钉死咯,以防隔壁家那谁谁谁来串门。在我家找了一圈,发现我家没有锤子,那咋办呢,我就顺着作用域链(Scope chain)爬啊爬,爬到社区。社区老大哥,你家有没有锤子啊,有的话借我使使啊。社区老大哥果然有锤子,而且社区老大哥的锤子的作用域(Scope)覆盖面积贼大,我赶紧拿着就溜回家把门给钉死咯。所以alert(hammer);成功的得到:

alert个锤子

这我刚把门给钉死,社区老大哥就来alert(wifi);他自己又没有wifi,以为能把我内部定义的wifi给alert出来。门都没有!你忘了我把门给钉死了啊?我的wifi的作用域根本出不去!社区老大哥灰头土脸的走了,还在控制台里留下一抹伤心的报错:

wifi is not defined

不知道我这么一说,你是不是稍微理解了我给自己加的标签了——和变量的可见性相关
1.wifi呢,是我(myHome)这个函数内部定义的,对于我自己来说那肯定是可见的,也就是说wifi的作用域是在myHome函数内部的,我如果想用wifi那是随便用。
2.锤子呢,虽然是社区老大哥里定义的,但是因为他把我包起来了,那我就可以通过作用域链爬上去借他的锤子使使,所以锤子对于我(myHome)来说,也是可以用的。
3.我的wifi对于社区老大哥来说,那是不可见的,我可以顺着作用域链向上爬拿社区老大哥里的变量,但是他不能往下爬拿我的,这是规则。

你可能要问了,要是你自己也有锤子,社区老大哥那里也有锤子,你拿哪一个呢?
那当然是用我自己的了啊, 吃饱了撑得还爬绳子去拿别人的啊?是想累死我是么!

Context(执行上下文)拿this

这隔壁老C,看名字就贼难理解,还执行上下文?你当这是上语文课呢。算了算了,不调侃他那奇奇怪怪的名字了,等你理解了就会发现这名字取得还挺棒棒棒。接下来让他自己介绍一下吧。

我是Context, 我也给自己打个标签——我和我媳妇this形影不离。

为啥这么说呢,因为好多人一开始并不知道我,是因为想搞懂我媳妇this, 然后听到好多人说要搞懂this, 先要搞懂Context,所以才开始了解我。不是有好多人面试的时候看到各种关于this的面试题吗?看的头都大了,其实搞懂我Context之后,这些就全明白了。

一句话介绍自己Context——函数在被调用时的上下文环境,被调用函数中的this指向该上下文环境。先记住两个字调用!

那么怎么理解函数在被调用时的上下文环境呢?接下来又到了举栗子的时候了,真是百文不如十行代码啊:

  var myName = '温浪';
  function sayName (){
    alert(this.myName);
  }
  sayName();

函数sayName被调用了,那么你想过函数是在哪个环境下被调用吗?在这里函数是在全局环境下被调用的,因此函数中的this指向全局对象——window。因此alert(this.myName);可以输出:

我的名字

上面这个栗子实在来太简单啦,那咱们来点难的加深一下理解:

var name = 'window';
var me = {
  name: '温浪',
  sayName: function() {
    alert(this.name);
  }
}

var myFunction = me.sayName;
myFunction();

输出的不是温浪而是window

window

由此我们得出结论:

函数被调用时,this的指向与函数定义无关,而只与函数被调用时的上下文环境有关!再次强调!调用!

结合DOM事件的Context

通过上面的描述,你理解了函数调用时,this的指向由函数被调用时的我Context决定。那咱们再加点难度,结合DOM事件!
假设有以下代码:

<!DOCTYPE html>
<html>
<head>
    <title>easyTest</title>
</head>
<body>
    <button id="myButton" onClick="testContext()" name="dom">click me</button>
    <script>
        var name = 'window';
        var me = {
            name: '温浪',
            sayName: function() {
                alert(this.name);
            }
        }

        var testContext = me.sayName;
    </script>
</body>
</html>

代码中分别在dom节点对象,window对象和me对象中都定义了name属性,我们的目标是输出me对象中的温浪,那么点击这个button到底会输出哪个name呢?答案是:

如果事件是直接通过inline的方式写在html中,那么this指向window对象。所以输出为window.

window

如果将代码改成下面这样:

<html>
<head>
    <title>easyTest</title>
</head>
<body>
    <button id="myButton" name="dom">click me</button>
    <script>
        var name = 'window';
        var me = {
            name: '温浪',
            sayName: function() {
                alert(this.name);
            }
        }

        var testContext = me.sayName;
        document.getElementById('myButton').addEventListener('click', testContext);
    </script>
</body>
</html>

即通过js添加事件处理,那么会输出什么呢?答案是:

如果通过js添加事件处理,this指向该dom节点,在本例中也就是button节点。所以在点击的时候会alert出button节点上的name。

DOM

绕了一圈还是没有解决问题,现在我们的目标是显示me对象中的name。该怎么办呢, 其实很简单啊,要是你理解了我Context,就知道下面的代码可以解决这个问题:

<!DOCTYPE html>
<html>
<head>
    <title>easyTest</title>
</head>
<body>
    <button id="myButton" name="dom" onclick="me.sayName()">click me</button>
    <script>
        var name = 'window';
        var me = {
            name: '温浪',
            sayName: function() {
                alert(this.name);
            }
        }
    </script>
</body>
</html>

哦!点击button的时候,让me去执行sayName,那么sayName的this就指向了me!成功解决问题!(虽然看上去这个例子很傻,但是希望能加深对Context的理解啦(╯﹏╰))

最后的最后

Scope和Context这两个家伙真是让人头大的不行,键盘都砸烂了!最后的最后,介绍另外两个兄弟,call和apply, 他俩就厉害了,可以手动指定方法被调用时this的指向!戳我去观光call和apply

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

推荐阅读更多精彩内容