javascript复习笔记(3)

this

上次讲了闭包,其实我感觉我上次没讲什么,闭包的确神秘,但是又不是很复杂,需要长篇大论去解释。刚好,我看到了js中的另一个重点,this。this也是会让初学者头疼的一个知识点。有时候感觉莫名其妙,直觉上应该是这个答案,实际上却是错的。

错误的认识

  • this不只在js中出现,java, c++ 这些强类型语言也是有this的,不过它们都是叫this, 本质是不太一样的。学过这些强类型语言的同学很容易就认为this是指向自己的。然而,这在js中,是不正确的。下面的代码会打脸。
function fun (val) {
  this.a = val;
}
fun(0);
fun.a++;
console.log(fun.a);
console.log(a);

如果this是指向自己的话,那么现在fun.a应该是1咯,对吧。然而真的是这样么? 打开chrome, F12将代码拷贝在控制台看看结果,fun.a的输出是NaN,第二个log出却是0。傻眼了吧?好吧,其实当时我也是一脸懵逼。这个结果,告诉了我们js的this不如你想象的那样。这是你不知道的javaScript的this。

  • 除了认为this指向自己,《你不知道的javaScript》中也提到另一种错误的理解。this指向函数的作用域。详细点说,就是一个函数,它的this是指向它的父级作用域。嗯,好吧,我觉得这种理解很自然,很舒服。下面的代码,就是这种错误理解导致的错误写法。
function a () {
  console.log(this.c);
}
function b() {
  let c = 1;
  this.a();
}
b(); // undefined

其实上面的结果不会输出1,所以说明了这个理解也是错误的。

this的正确使用姿势

this的指向呢,其实一句话就概况了。

this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

其实this的指向就看谁调用了它吧,总结下来,有以下4种情况:

  1. 默认绑定
    默认绑定,我曾经也听到说成是直接函数调用。下面的代码就是默认绑定
function fun () {
  console.log(this.a);
}
var a = 1;
fun() // 1

默认绑定的情况,函数中的this是绑定在window上,当然是在非严格模式下。在严格模式下,this是指向undefined, 上面的代码就会抛出一个错误。说回非严格模式下的默认绑定,由于this指向了window。所以, this.a其实就是window.a。那么自然输出了1。

  1. 隐式绑定
    当一个函数是某个对象的属性时,我们直接对象.函数名调用该函数时,该函数的this是指向该对象的。其实,我个人认为,默认绑定的情况下 fun() 等价于 window.fun(),这也正好解释了为什么默认绑定时,this是指向了全局对象。下面是隐式绑定的一个例子:
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}

obj.func() // '123'

注意:隐式绑定当使用不当时,会出现this的丢失现象。比如:

// 情况一
window.name = '1234';
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}
func = obj.func;
func(); // '1234'

// 情况二
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return function () {
      console.log(this.name); 
    }
  }
}
func = obj.func();
func(); // '1234'

// 情况三
window.name = '1234';
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}

setTimeout(obj.func, 1000); // '1234'

情况一:不是隐式绑定了obj么,输出应该是123才对啊。结果却输出了1234。嗯,我们要根据代码来分析,this的指向要对比四种情况,然后去找到对应的情况来确定。func()很明显就是之前我们说的默认绑定,所以输出了全局对象中的a属性的值。
情况二:情况二其实就是返回了一个函数,然后这个函数在全局作用域直接调用了,所以就是默认绑定,如果需要绑定原来的obj,可以用以下的办法:

// 解法1
window.name = '1234';
let obj = {
  name: '123',
  func () {
    let that = this;
    return function () {
      console.log(that.name); 
    }
  }
}
func = obj.func();
func(); // '123'

// 解法2
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return () => console.log(this.name); 
   }
 }
func = obj.func();
func(); // '123'

// 解法3
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return function () {
      console.log(this.name); 
    }.bind(this);
   }
 }
func = obj.func();
func(); // '123'

情况三: setTimeout的第一个参数是一个函数,其实setTimeout的内部实现的伪代码应该是这样的

function setTimeout (fn, delay) {
  // 等了 delay 毫秒
  fn()
}

所以其实也是变成了默认绑定嘛。

  1. 显式绑定
    显式绑定就是用js的call, apply, bind的这些函数来绑定this啦。
window.name = 'window';
let xiaoming = { name: 'xiaoming' };
function bar() { console.log(this.name); }
// call
bar.call(xiaoming); // 'xiaoming';
// apply
bar.apply(xiaoming); // 'xiaoming';
// bind
bar.bind(xiaoming)(); // 'xiaoming';

它们间的异同:

    • 它们三者可以用来改变函数this的指向, 第一个参数都是要指向的对象
    • apply 和 call 接受的参数不太一样,call只能一个一个传入参数,apply可以传入一个参数的数组
    • bind返回的是绑定后的函数,apply 和 call是绑定时同时执行。
  1. new 绑定
    在js中,我们使用new运算符时,其实,经过了下面的操作

1、创建(或者说构造)一个全新的对象
2、这个新对象会被执行[[原型]]连接
3、这个新对象会被绑定到函数的this
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

综上,那么下面的代码,

function fn (a) { this.a = a; }
let a = new fn(1);
console.log(a.a); // 1

就是上面的四个步骤过一遍的结果了,this绑定到了的这个新对象a上。

总结

逼逼了这么多。。其实this的指向只要弄清楚这四种情况,绝大部分的情况都可以自己判断出来。其实还是会有些很奇葩的情况的,如果我没记错,但是根据我目前的开发经验,只要你或者你同事不作死,就不会写出那么奇葩的情况。如果说是面试题,当我没说。来来来,最后总结遍,四个方法就是:

  • 如果是new出来的对象,那么this指向该对象无误。
  • 如果有用显式绑定,那么this指向绑定的对象
  • 是否作为一个对象的属性来调用,是的话,this指向该对象。当然要注意this丢失的情况。
  • 如果上面都没有,那么默认绑定应该无误。

多练习慢慢就会一眼就看出this的指向了,工多手熟。如果有误,请指出。还有祝大家国庆快乐!

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • -1- 很少玩游戏的我,也被身边朋友们安利了这款老少皆宜、带妹儿无敌、堪称社交神器的—王者荣耀! 下面就来说说我遇...
    8小雪儿8阅读 876评论 0 1
  • 喜欢绿色,因为孕育生命 喜欢藤蔓,因为她无可依靠 却坚强地追逐高度、阳光 不愿匍匐在低处,随波逐流 梦想,正如藤蔓...
    会飞的猪心形的月亮阅读 234评论 0 1
  • 有句话说:因为一个人,爱上一座城。而我是因为“一个人”,爱上一座城! 我是一个北方人,度过了一个人的世界已经28年...
    当年饭店阅读 501评论 0 0
  • “我觉得我男朋友要跟我分手了,我是不是得先把他踹了。” 正在吃饭的我一口菜汤喷在桌子上,“何以见得?”为了不表现出...
    小小橘子皮阅读 749评论 0 3