#JS续:3.0 this的前置了解

前言

了解this之前我们看看这篇文章:
[译]让我们一起解决“this”难题第一部分

真的看完之后无数次都啧啧称奇,老外的文章看得太爽了吧。这里我只截取部分我认为重要的知识来记录我对this的理解,部分表述有出入。

以下对this的理解,默认你已经了解一些JavaScript的背景知识,讲到global、window、this、prototype等等时,你知道它们是什么意思。虽然文章中默认所使用global和window,在这里是同一回事,可以互换。实际上我查了资料,其实是不一样的,简要说明一下:(不过不要过于纠结)

global: 全局对象global是在全局上下文中的变量对象,是一个不依赖宿主环境的对象,(注:常见的宿主环境:浏览器和nodejs)一切全局里存在的变量和函数都是它的属性和方法。global 不能直接被访问而是间接访问,访问其属性。如Math、String等等都可以看作是全局对象 global的属性,对global的属性的访问往往省略掉前缀,比如Math.abs(1)其实就是[[global]].Math.abs(1)

window:‘usestrict’模式下,我们更强调window的全局对象的作用(除此之外,则常用global作为全局对象)。window对象是相对于Web浏览器而言的,它并不是ECMAScript规定的内置对象,它是浏览器的WebAPI,是存在于浏览器之中的,也就是离开浏览器这个宿主环境的话就不存在此对象了。所以,Global对象是包含于window对象这种情况的开始

一、this的第1条规则:默认绑定

默认绑定规则规定,当一个函数执行独立调用时,例如只是funcName();,这时函数的“this”被指向 global 对象。这里要注意的是,严格模式下,全局对象是不会默认绑定的,因此如果你在开启了严格模式,那么控制台输出的就是 undefined,而不再是全局变量。
先看下面这个代码:

//例子1
function foo() {
 function bar() {
  console.log(this); 
 } 
 bar();
}

foo();

foo 先被调用,然后又调用bar,bar将“this”打印到控制台中。这里的技巧是看看函数是如何被调用的:foo和bar都被单独调用,因此,他们内部的“this”都是指向 global 对象。但是由于 bar 是唯一执行打印的函数,所以我们看到 global 对象在控制台中输出了一次。

再看一下面的代码:

//例子2
var a = 1;

function foo() {  
 console.log(this.a);  
}

foo();
//1

输出结果是 undefined?是 1?还是什么?
如果你已经很好地理解了之前讲解的内容,那么你应该知道控制台输出的是“1”。为什么?首先,默认绑定作用于函数 foo。因此 foo 中的“this”指向 global 对象,并且 a 被声明为 global 变量,这就意味着 a 是 global 对象的属性(也称之为全局对象污染),因此this.avar a就是同一个东西。

二、this的第2条规则:隐式绑定。

隐式绑定规则规定,当一个函数被作为一个对象方法被调用时,那么它内部的“this”应该指向这个对象。
先看下面这个代码:

//例子3
var obj = {  
 a: 1,   
 foo: function() {  
  console.log(this);   
 }  
};

obj.foo();

如果函数调用前面有多个对象(obj1.obj2.func()),那么函数之前的最后一个对象(obj3)会被绑定。

需要注意的一点是,函数调用必须有效,那也就是说当你调用obj.func()时,必须确保 func 是对象 obj 的属性。

因此,在上面的例子中调用obj.foo()时,“this”就指向 obj,因此 obj 被打印输出在控制台中。

三、进阶:默认绑定和隐性绑定两种规则结合

【重要】
我们必须知道,this在调用一个函数函数前,并没有绑定在这个函数,
判断运用了那种绑定规则,要密切注意这个函数是如何是否被单独调用。
如果单独调用的话,就是默认绑定,函数的“this”都指向全局对象global;如果是对象属性的调用,那么就是隐性绑定。

1、先看以下代码:

//例子4
function logThis() {  
 console.log(this);  
}

var myObject = {  
 a: 1,   
 logThis: logThis  
};

logThis();  
myObject.logThis();

跟在 myObject 后面的这个全局调用logThis()单独被调用的时候,通过 console.log(this)打印的是global对象;而myObject.logThis(),此时logThis()就是作为函数的属性进行调用的, 打印是 myObject 对象。

2、一个有趣的现象:

console.log(logThis === myObject.logThis); // true

为什么不呢?它们当然是相同的函数,但是你可以看到 如何调用_logThis_ 会让其中的“this”发生改变。当logThis被单独调用时,使用默认绑定规则,但是当 logThis 作为前面的对象属性被调用时,使用隐式绑定规则。

四、继续探讨this的运用

1、先看这个例子:

//例子5
function foo() {  
 var a = 2;  
 this.bar();  
}

function bar() {  
 console.log(this.a);  
}

foo();
//undefined

就像例子2 的 var a一样,bar也是全局对象的属性。因为foo被单独调用了,它内部的“this”就是全局对象(默认绑定)。因此 foo 内部的 this.bar 就是 bar。控制台中输出什么?如果你猜的没错,“undefined”会被打印出来。

注意 bar 是如何被调用的?看起来,隐式绑定在这里发挥作用。隐式绑定意味着 bar 中的“this”是其前面的对象引用。bar前面的对象引用是全局对象,在 foo 里面是全局对象,对不对?因此在 bar 中尝试访问 this.a等同于访问 [global object].a。而全局对象没有属性a,没有什么意外的话,控制台会输出 undefined

2、再看一下下面例子:

//例子6
var obj = {  
 a: 1,   
 foo: function(fn) {  
  console.log(this);  
  fn();  
 }  
};

obj.foo(function() {  
 console.log(this);  
});

//{a: 1, foo: ƒ}
//window

是不是瞬间看不懂这个代码是什么呢?不要着急,把它简化一下吧,把
obj.foo(function() { console.log(this); });简化成 obj.foo();
这样看来,函数foo是接受一个回调函数作为参数。我们所做的就是在调用foo的时候在参数里面放了一个函数。
obj.foo( function() { console.log(this); } );
foo 是 如何 被调用的?它是一个单独调用吗?当然不是,因此第一个输出到控制台的是对象 obj 。我们传入的回调函数是什么?在 foo 内部,回调函数变为 fn 。那么 fn 是 如何被调用?fn中的“this”是指向全局对象,因此第二个被输出到控制台的是全局对象。

3、看一个和构造函数相关的例子:

//例子7
var arr = [1, 2, 3, 4];

Array.prototype.myCustomFunc = function() {
 console.log(this);
};

arr.myCustomFunc();
//[1, 2, 3, 4]

是不是又傻眼了?如果对于原型不是很熟悉,就把它当做一个普通的对象,然后一个执行this的函数赋值给它,然后调用myCustomFunc时,就是调用这个函数对象,此刻发挥了隐性绑定的作用(我们始终不要忘记两种绑定规则的判定标准),此时函数内部的this就是指向myCustomFunc这个对象,arr调用之后,输出结果就是:[1, 2, 3, 4]

4、再看一下下面的例子:

//例子8

var obj = {  
 a: 2,  
 foo: function() {  
  console.log(this);  
 }  
};

obj.foo();

var bar = obj.foo;  
bar();

不要被这里面的花哨代码所分心,只需注意函数是如何被调用的,就可以弄明白“this”的含义。你现在一定已经掌握这个技巧了吧。首先obj.foo()被调用,因为 foo 前面有一个对象引用,所以首先输出的是对象obj。bar当然是被独立调用的,因此下一个输出是全局变量。这里要注意的是,bar和foo是对同一个函数的引用,唯一区别是它们被调用的方式不同。

做到这里我已经够理解this(其实是时间不够)。其实还有几个例子,我全看完了,自己做了之后全对,希望看到这篇的你也能学会this的指向。

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

推荐阅读更多精彩内容