《你不知道的JavaScript》-- 精读(六)
栏目: JavaScript · 发布时间: 5年前
内容简介:这段代码可以在不同的上下文对象(me和you)中重复使用函数identify()和speak(),不用针对每个对象编写不同版本的函数。如果不使用this,那就需要给identify()和speak()显式传入一个上下文对象。然而,this提供了一种更优雅的方式来隐式“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用。
function identify(){ return this.name.toUpperCase(); } function speak(){ var greeting = "Hello, I'm " + identify.call(this); console.log(greeting); } var me = { name: "Kyle" } var you = { name: "Reader" } identify.call(me); // KYLE identify.call(you); // READER speak.call(me); // Hello,我是KYLE speak.call(you); // Hello,我是READER 复制代码
这段代码可以在不同的上下文对象(me和you)中重复使用函数identify()和speak(),不用针对每个对象编写不同版本的函数。
如果不使用this,那就需要给identify()和speak()显式传入一个上下文对象。
function identify(context){ return context.name.toUppperCase(); } function speak(context){ var greeting = "Hello, I'm " + identify(context); console.log(greeting); } identify(you); // READER speak(me); // hello,I'm + identify(context) 复制代码
然而,this提供了一种更优雅的方式来隐式“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用。
随着你的使用模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱,使用this则不会这样。
2.关于this的误解
2.1 指向自身
function foo(num){ console.log("foo:" + num); // 记录foo被调用的次数 this.count++; } foo.count = 0; var i; for(i = 0; i < 10; i++){ if (i > 5){ foo(i); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // foo被调用了多少次? console.log(foo.count); // 0 -- 什么?! 复制代码
执行foo.count = 0时,的确向函数对象foo添加了一个属性count。但是函数内部代码this.count中的this并不是指向那个函数对象,所以虽然属性名相同,根对象却并不相同,困惑随之产生。
实际上,如果深入探索,就会发现这段代码无意中创建了一个全局变量count,其值为NaN。
遇到这样的问题,大多数人会选择下面的解决方案,创建另一个带有count属性的对象。
function foo(num){ console.log("foo: " + num); // 记录foo被调用的次数 data.count++ } var data = { count: 0 } var i; for (i = 0; i < 10; i++){ if (i > 5){ foo(i); } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // foo被调用了多少次? console.log(data.count); 复制代码
上面使用词法作用域“解决”了问题,但是忽视了真正的问题--无法理解this的含义和工作原理。
如果要从函数对象内部引用它自身,那只使用this是不够的。一般来说你需要通过一个指向函数对象的词法标识符(变量)来引用它。
思考下面的这两个函数
function foo(){ foo.count = 4; // foo指向它自身 } setTimeout(function (){ // 匿名(没有名字的)函数无法指向自身 },10) 复制代码
第一个函数被称为具名函数,在它内部可以使用foo来引用自身。
第二个例子中,传入setTimeout(...)的回调函数没有名称标识符(这种函数被称为匿名函数),因此无法从函数内部引用自身。
所以,对于我们的例子来说,另一种解决方法是使用foo标识符替代this来引用函数对象:
function foo(num){ console.log("foo:" + num); // 记录foo被调用的次数 foo.count++; } foo.count = 0; var i; for(i = 0; i < 10; i++){ if (i > 5){ foo(i); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // foo被调用了多少次? console.log(foo.count); // 4 复制代码
然而,这种方法同样回避了this的问题,并且完全依赖于变量foo的词法作用域。
另一种方法是强制this指向foo函数对象:
function foo(num){ console.log("foo:" + num); // 记录foo被调用的次数 // 注意,在当前的调用方式下,this确实指向foo this.count++; } foo.count = 0; var i; for(i = 0; i < 10; i++){ if (i > 5){ foo.call(foo,i); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // foo被调用了多少次? console.log(foo.count); // 4 复制代码
2.2 它的作用域
第二种常见的误解是,this指向函数的作用域。这个问题有点复杂,因为在某种情况下它是正确的,但是在其他情况下它却是错误的。
this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过JavaScript代码访问,它存在于引擎内部。
function foo(){ var a = 2; this.bar(); } function bar(){ console.log(this.a); } foo(); // ReferenceError: a is not defined 复制代码
首先,这段代码试图通过this.bar()来引用bar()函数。这样调用能成功纯属意外,我们之后会解释原因。调用bar()最自然的方法是省略前面的this,直接使用词法引用标识符。
此外,编写这段代码的开发者还试图使用this联通foo()和bar()的词法作用域,从而让bar()可以访问foo()作用域里的变量a。这是不可能实现的,使用this不可能在词法作用域中查到什么。
每当你想要把this和词法作用域的查找混合使用时,一定要提醒自己,这是无法实现的。
3 this到底是什么
this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到。
总结
this既不指向自身也不指向函数的词法作用域。
this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
巴拉巴拉
关于懒
我本来想写的是关于习惯,后来想想还是不要美化自己了,其实就是懒,也没必要找借口,这个的起因是提交代码时不做确认,其实很多次我都做了,但是一遇到忙,或者提交的文件确实多的时候,我就懒得去检查了,这样就自然而然会导致很多问题,比如,之前就犯过API文档的接口名和实际的接口名不一致而被怼的情况,被教育一番,自己也很是羞愧,写了纸条提醒一定不要再犯,但是吧,还是那句话,忙起来就忘了,这也是我想把它归到习惯的原因,这次又是同样的问题,直接把代码粘贴过来改了一下,也没有检查,就提交了,自然又被批评了一番,心中的懊悔自不必说,意识到了自己需要改正的两个地方,1.提交的东西要严谨,2.尽量不粘贴代码,这次应该肯定不会因为什么就忘了,因为教训是惨痛的!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 《你不知道的JavaScript》-- 精读(四)
- 精读《React 性能调试》
- 精读《Typescript 4》
- 精读《正则 ES2018》
- AIStats 2017文章精读(四)
- 精读《如何比较 Object 对象》
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
爆款:如何打造超级IP
【美】安妮塔•埃尔伯斯 / 杨雨 / 中信出版社 / 2016-1-10 / 49
哈佛商学院IP运营与产品管理方法论第一书,翻转长尾理论的重要著作! 电影大片、当红炸子鸡、百万畅销书背后的运营逻辑是什么? 《五十度灰》、Lady Gaga、维多利亚的秘密有何共同秘密? 漫威如何将蜘蛛侠、X战警、绿巨人打造成金矿? 皇马如何打造体育IP,一跃成为全球收 入最高的足球俱乐部? 爆款策略如何运用于电影、电视、音乐、出版、体育与商业各领域? ----......一起来看看 《爆款:如何打造超级IP》 这本书的介绍吧!