当有人问起你JavaScript有什么特征的时刻,你能够立马就想到了单线程
、事宜驱动
、面向对象
等一堆词语,然则假如真的让你诠释一下这些观点,能够真诠释不清晰。有句话这么说:假如你不能向一个6岁小孩诠释清晰一个东西,那末你自身也不懂这个东西。这句话也许有点夸大,然则极为有原理。个人以为,假如须要控制一门言语,控制它的API只是学了外相,邃晓这门言语的精华才是重点。说起JavaScript的精华,this
、闭包
、作用域链
、函数
是当之无愧的。这门言语正式由于这几个东西而变得魅力无限。
博客的题目是《JavaScript中的this圈套的最全网络–没有之一》,很显然这篇博客论述的是this。置信做过JavaScript开辟的人都遇到过不少this的圈套,我自身自身也遇到过不少坑,然则假如非要给出一个体系的总结的话,还没有充足的秘闻。异常荣幸的是,本日早上起来看《Hacker News》的时刻,碰巧看到了一篇有关于JavaScript this的剖析:all this。因而,本着进修和同享的精力,决议将它翻译成中文。翻译的目标绝对不是为了当大自然的搬运工,在这个历程当中会完整弄邃晓他人的著作,加深熟悉,同时将好东西分享给他人,才让更多的进修者站在伟人的肩膀上行进。依据我自身的习气,会翻译的历程当中加上一些自身诠释(援用部份),毕竟中西方人的思索体式格局是有差别的。固然文章题目所述的最全也不是吹的,文章异常长。
原文翻译:
JavaScript来自一门健全的言语,所以你能够以为JavaScript中的this和其他面向对象的言语如java的this一样,是指存储在实例属性中的值。现实并非如此,在JavaScript中,最好把this当做哈利波特中的博格特的背包,有着深不可测的魔力。
下面的部份是我愿望我的同事在运用JavaScript的this的时刻应当晓得的。内容很多,是我进修好几年总结出来的。
JavaScript中很多时刻会用到this,下面细致引见每一种状况。在这里我想起首引见一下宿主环境这个观点。一门言语在运转的时刻,须要一个
环境
,叫做宿主环境
。关于JavaScript,宿主环境最罕见的是web浏览器
,浏览器供应了一个JavaScript运转的环境,这个环境内里,须要供应一些接口
,好让JavaScript引擎
能够和宿主环境
对接。JavaScript引擎才是真正实行JavaScript代码的处所,罕见的引擎有V8
(现在最快JavaScript引擎、Google临盆)、JavaScript core
。JavaScript引擎重要做了下面几件事变:
- 一套与宿主环境相联系的划定规矩;
- JavaScript引擎内核(基础语法范例、逻辑、敕令和算法);
- 一组内置对象和API;
- 其他商定。
然则环境不是唯一的,也就是JavaScript不单单议能够在浏览器内里跑,也能在其他供应了宿主环境的顺序内里跑,最罕见的就是nodejs。一样作为一个宿主环境,
nodejs
也有自身的JavaScript引擎–V8。依据官方的定义:
Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications
global this
- 在浏览器里,在全局局限内,this等价于window对象。
1 <script type="text/javascript">
2 console.log(this === window); //true
3 </script>
- 在浏览器里,在全局局限内,用var声明一个变量和给this或许window增加属性是等价的。
1 <script type="text/javascript">
2 var foo = "bar";
3 console.log(this.foo); //logs "bar"
4 console.log(window.foo); //logs "bar"
5 </script>
- 假如你在声明一个变量的时刻没有运用var或许let(ECMAScript 6),你就是在给全局的this增加或许转变属性值。
1 <script type="text/javascript">
2 foo = "bar";
3
4 function testThis() {
5 foo = "foo";
6 }
7
8 console.log(this.foo); //logs "bar"
9 testThis();
10 console.log(this.foo); //logs "foo"
11 </script>
- 在node环境里,假如运用REPL(Read-Eval-Print Loop,简称REPL:读取-求值-输出,是一个简朴的,交互式的编程环境)来实行顺序,this并非第一流的定名空间,第一流的是global.
> this
{ ArrayBuffer: [Function: ArrayBuffer],
Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
...
> global === this
true
- 在node环境里,假如实行一个js剧本,在全局局限内,this以一个空对象最先作为第一流的定名空间,这个时刻,它和global不是等价的。
1 test.js剧本内容:
2
3 console.log(this);
4 console.log(this === global);
5
6 REPL运转剧本:
7
8 $ node test.js
9 {}
10 false
- 在node环境里,在全局局限内,假如你用REPL实行一个剧本文件,用var声明一个变量并不会和在浏览器内里一样将这个变量增加给this。
1 test.js:
2
3 var foo = "bar";
4 console.log(this.foo);
5
6 $ node test.js
7 undefined
- 然则假如你不是用REPL实行剧本文件,而是直接实行代码,效果和在浏览器内里是一样的(神坑)
1 > var foo = "bar";
2 > this.foo
3 bar
4 > global.foo
5 bar
- 在node环境里,用REPL运转剧本文件的时刻,假如在声明变量的时刻没有运用var或许let,这个变量会自动增加到global对象,然则不会自动增加给this对象。假如是直接实行代码,则会同时增加给global和this
1 test.js
2
3 foo = "bar";
4 console.log(this.foo);
5 console.log(global.foo);
6
7 $ node test.js
8 undefined
9 bar
上面的八种状况能够人人已绕晕了,总结起来就是:在
浏览器
内里this是老大,它等价于window对象,假如你声明一些全局变量(不论在任何处所),这些变量都邑作为this的属性。在node内里,有两种
实行JavaScript代码的体式格局,一种是直接实行写好的JavaScript文件
,别的一种是直接在内里实行一行行代码
。关于直接运转一行行JavaScript代码的体式格局,global才是老大,this和它是等价的。在这类状况下,和浏览器比较类似,也就是声明一些全局变量会自动增加给老大global,顺带也会增加给this。然则在node内里直接剧本文件就不一样了,你声明的全局变量不会自动增加到this,然则会增加到global对象。所以雷同点是,在全局局限内,全局变量终究是属于老大的。
function this
- 无论是在浏览器环境照样node环境, 除了在DOM事宜处置惩罚顺序里或许给出了thisArg(接下来会讲到)外,假如不是用new挪用,在函数内里运用this都是指代全局局限的this。
1 <script type="text/javascript">
2 foo = "bar";
3
4 function testThis() {
5 this.foo = "foo";
6 }
7
8 console.log(this.foo); //logs "bar"
9 testThis();
10 console.log(this.foo); //logs "foo"
11 </script>
test.js
foo = "bar";
function testThis () {
this.foo = "foo";
}
console.log(global.foo);
testThis();
console.log(global.foo);
$ node test.js
bar
foo
- 除非你运用严厉形式,这时刻this就会变成undefined。
1 <script type="text/javascript">
2 foo = "bar";
3
4 function testThis() {
5 "use strict";
6 this.foo = "foo";
7 }
8
9 console.log(this.foo); //logs "bar"
10 testThis(); //Uncaught TypeError: Cannot set property 'foo' of undefined
11 </script>
- 假如你在挪用函数的时刻在前面运用了new,this就会变成一个新的值,和global的this离开相干。
1 <script type="text/javascript">
2 foo = "bar";
3
4 function testThis() {
5 this.foo = "foo";
6 }
7
8 console.log(this.foo); //logs "bar"
9 new testThis();
10 console.log(this.foo); //logs "bar"
11
12 console.log(new testThis().foo); //logs "foo"
13 </script>
我更喜好把新的值称作一个实例。
函数内里的this实在相对比较好邃晓,假如我们在一个函数内里运用this,须要注重的就是我们挪用函数的体式格局,假如是一般的体式格局挪用函数,this指代全局的this,假如我们加一个new,这个函数就变成了一个组织函数,我们就建立了一个实例,this指代这个实例,这个和其他面向对象的言语很像。别的,写JavaScript很常做的一件事就是绑定事宜处置惩罚顺序,也就是诸如button.addEventListener(‘click’, fn, false)之类的,假如在fn内里须要运用this,this指代事宜处置惩罚顺序对应的对象,也就是button。
prototype this
- 你建立的每个函数都是函数对象。它们会自动取得一个特别的属性prototype,你能够给这个属性赋值。当你用new的体式格局挪用一个函数的时刻,你就可以经由过程this接见你给prototype赋的值了。
1 function Thing() {
2 console.log(this.foo);
3 }
4
5 Thing.prototype.foo = "bar";
6
7 var thing = new Thing(); //logs "bar"
8 console.log(thing.foo); //logs "bar"
- 当你运用new为你的函数建立多个实例的时刻,这些实例会同享你给prototype设定的值。关于下面的例子,当你挪用this.foo的时刻,都邑返回雷同的值,除非你在某个实例内里重写了自身的this.foo
复制代码
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 console.log(this.foo);
6 }
7 Thing.prototype.setFoo = function (newFoo) {
8 this.foo = newFoo;
9 }
10
11 var thing1 = new Thing();
12 var thing2 = new Thing();
13
14 thing1.logFoo(); //logs "bar"
15 thing2.logFoo(); //logs "bar"
16
17 thing1.setFoo("foo");
18 thing1.logFoo(); //logs "foo";
19 thing2.logFoo(); //logs "bar";
20
21 thing2.foo = "foobar";
22 thing1.logFoo(); //logs "foo";
23 thing2.logFoo(); //logs "foobar";
- 实例内里的this是一个特别的对象。你能够把this想成一种猎取prototype的值的一种体式格局。当你在一个实例内里直接给this增加属性的时刻,会隐蔽prototype中与之同名的属性。假如你想接见prototype中的这个属性值而不是你自身设定的属性值,你能够经由过程在实例内里删除你自身增加的属性的体式格局来完成。
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 console.log(this.foo);
6 }
7 Thing.prototype.setFoo = function (newFoo) {
8 this.foo = newFoo;
9 }
10 Thing.prototype.deleteFoo = function () {
11 delete this.foo;
12 }
13 var thing = new Thing();
14 thing.setFoo("foo");
15 thing.logFoo(); //logs "foo";
16 thing.deleteFoo();
17 thing.logFoo(); //logs "bar";
18 thing.foo = "foobar";
19 thing.logFoo(); //logs "foobar";
20 delete thing.foo;
21 thing.logFoo(); //logs "bar";
- 或许你也能直接经由过程援用函数对象的prototype 来取得你须要的值。
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 console.log(this.foo, Thing.prototype.foo);
6 }
7
8 var thing = new Thing();
9 thing.foo = "foo";
10 thing.logFoo(); //logs "foo bar";
- 经由过程一个函数建立的实例会同享这个函数的prototype属性的值,假如你给这个函数的prototype赋值一个Array,那末统统的实例都邑同享这个Array,除非你在实例内里重写了这个Array,这类状况下,函数的prototype的Array就会被隐蔽掉。
1 function Thing() {
2 }
3 Thing.prototype.things = [];
4
5
6 var thing1 = new Thing();
7 var thing2 = new Thing();
8 thing1.things.push("foo");
9 console.log(thing2.things); //logs ["foo"]
- 给一个函数的prototype赋值一个Array一般是一个毛病的做法。假如你想每个实例有他们专属的Array,你应当在函数内里建立而不是在prototype内里建立。
1 function Thing() {
2 this.things = [];
3 }
4
5
6 var thing1 = new Thing();
7 var thing2 = new Thing();
8 thing1.things.push("foo");
9 console.log(thing1.things); //logs ["foo"]
10 console.log(thing2.things); //logs []
- 实际上你能够经由过程把多个函数的prototype链接起来的从而构成一个原型链,因而this就会魔法般地沿着这条原型链往上查找直到找你你须要援用的值。
1 function Thing1() {
2 }
3 Thing1.prototype.foo = "bar";
4
5 function Thing2() {
6 }
7 Thing2.prototype = new Thing1();
8
9
10 var thing = new Thing2();
11 console.log(thing.foo); //logs "bar"
- 一些人应用原型链的特征来在JavaScript模拟典范的面向对象的继续体式格局。任何给用于构建原型链的函数的this的赋值的语句都邑隐蔽原型链上游的雷同的属性。
1 function Thing1() {
2 }
3 Thing1.prototype.foo = "bar";
4
5 function Thing2() {
6 this.foo = "foo";
7 }
8 Thing2.prototype = new Thing1();
9
10 function Thing3() {
11 }
12 Thing3.prototype = new Thing2();
13
14
15 var thing = new Thing3();
16 console.log(thing.foo); //logs "foo"
- 我喜好把被赋值给prototype的函数叫做要领。在上面的例子中,我已运用过要领了,如logFoo。这些要领有着雷同的prototype,即建立这些气力的原始函数。我一般把这些原始函数叫做组织函数。在prototype内里定义的要领内里运用this会影响到当前实例的原型链的上游的this。这意味着你直接给this赋值的时刻,隐蔽了原型链上游的雷同的属性值。这个实例的任何要领都邑运用这个最新的值而不是原型内里定义的这个雷同的值。
1 function Thing1() {
2 }
3 Thing1.prototype.foo = "bar";
4 Thing1.prototype.logFoo = function () {
5 console.log(this.foo);
6 }
7
8 function Thing2() {
9 this.foo = "foo";
10 }
11 Thing2.prototype = new Thing1();
12
13
14 var thing = new Thing2();
15 thing.logFoo(); //logs "foo";
- 在JavaScript内里你能够嵌套函数,也就是你能够在函数内里定义函数。嵌套函数能够经由过程闭包捕捉父函数的变量,然则这个函数没有继续this
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 var info = "attempting to log this.foo:";
6 function doIt() {
7 console.log(info, this.foo);
8 }
9 doIt();
10 }
11
12
13 var thing = new Thing();
14 thing.logFoo(); //logs "attempting to log this.foo: undefined"
在doIt内里的this是global对象或许在严厉形式下面是undefined。这是形成很多不熟悉JavaScript的人深陷 this圈套的泉源。在这类状况下事变变得异常蹩脚,就像你把一个实例的要领看成一个值,把这个值看成函数参数通报给别的一个函数然则却不把这个实例通报给这个函数一样。在这类状况下,一个要领内里的环境变成了全局局限,或许在严厉形式下面的undefined。
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 console.log(this.foo);
6 }
7
8 function doIt(method) {
9 method();
10 }
11
12
13 var thing = new Thing();
14 thing.logFoo(); //logs "bar"
15 doIt(thing.logFoo); //logs undefined
- 一些人喜好先把this捕捉到一个变量内里,一般这个变量叫做self,来防止上面这类状况的发作。
博主异常喜好用这类体式格局
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 var self = this;
6 var info = "attempting to log this.foo:";
7 function doIt() {
8 console.log(info, self.foo);
9 }
10 doIt();
11 }
12
13
14 var thing = new Thing();
15 thing.logFoo(); //logs "attempting to log this.foo: bar"
- 然则当你须要把一个要领作为一个值通报给一个函数的时刻并不论用。
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 var self = this;
6 function doIt() {
7 console.log(self.foo);
8 }
9 doIt();
10 }
11
12 function doItIndirectly(method) {
13 method();
14 }
15
16
17 var thing = new Thing();
18 thing.logFoo(); //logs "bar"
19 doItIndirectly(thing.logFoo); //logs undefined
- 你能够经由过程bind将实例和要领统统通报给函数来处理这个题目,bind是一个函数定义在统统函数和要领的函数对象上面
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 console.log(this.foo);
6 }
7
8 function doIt(method) {
9 method();
10 }
11
12
13 var thing = new Thing();
14 doIt(thing.logFoo.bind(thing)); //logs bar
- 你一样能够运用apply和call来在新的高低文中挪用要领或函数。
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 function doIt() {
6 console.log(this.foo);
7 }
8 doIt.apply(this);
9 }
10
11 function doItIndirectly(method) {
12 method();
13 }
14
15
16 var thing = new Thing();
17 doItIndirectly(thing.logFoo.bind(thing)); //logs bar
- 你能够用bind来替代任何一个函数或许要领的this,即使它没有赋值给实例的初始prototype。
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4
5
6 function logFoo(aStr) {
7 console.log(aStr, this.foo);
8 }
9
10
11 var thing = new Thing();
12 logFoo.bind(thing)("using bind"); //logs "using bind bar"
13 logFoo.apply(thing, ["using apply"]); //logs "using apply bar"
14 logFoo.call(thing, "using call"); //logs "using call bar"
15 logFoo("using nothing"); //logs "using nothing undefined"
- 你应当防止在组织函数内里返回任何东西,由于这能够替代原本应当返回的实例。
1 function Thing() {
2 return {};
3 }
4 Thing.prototype.foo = "bar";
5
6
7 Thing.prototype.logFoo = function () {
8 console.log(this.foo);
9 }
10
11
12 var thing = new Thing();
13 thing.logFoo(); //Uncaught TypeError: undefined is not a function
奇怪的是,假如你在组织函数内里返回了一个原始值,上面所述的状况并不会发作而且返回语句被忽略了。最好不要在你将经由过程new挪用的组织函数内里返回任何范例的数据,即使你晓得自身正在做什么。假如你想建立一个工场形式,经由过程一个函数来建立一个实例,这个时刻不要运用new来挪用函数。固然这个发起是可选的。
- 你能够经由过程运用Object.create来防止运用new,如许一样能够建立一个实例。
1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4
5
6 Thing.prototype.logFoo = function () {
7 console.log(this.foo);
8 }
9
10
11 var thing = Object.create(Thing.prototype);
12 thing.logFoo(); //logs "bar"
- 在这类状况下并不会挪用组织函数
1 function Thing() {
2 this.foo = "foo";
3 }
4 Thing.prototype.foo = "bar";
5
6
7 Thing.prototype.logFoo = function () {
8 console.log(this.foo);
9 }
10
11
12 var thing = Object.create(Thing.prototype);
13 thing.logFoo(); //logs "bar"
- 由于Object.create不会挪用组织函数的特征在你继续形式下你想经由过程原型链重写组织函数的时刻异常有效。
1 function Thing1() {
2 this.foo = "foo";
3 }
4 Thing1.prototype.foo = "bar";
5
6 function Thing2() {
7 this.logFoo(); //logs "bar"
8 Thing1.apply(this);
9 this.logFoo(); //logs "foo"
10 }
11 Thing2.prototype = Object.create(Thing1.prototype);
12 Thing2.prototype.logFoo = function () {
13 console.log(this.foo);
14 }
15
16 var thing = new Thing2();
object this
- 在一个对象的一个函数里,你能够经由过程this来援用这个对象的其他属性。这个用new来新建一个实例是不一样的。
1 var obj = {
2 foo: "bar",
3 logFoo: function () {
4 console.log(this.foo);
5 }
6 };
7
8 obj.logFoo(); //logs "bar"
- 注重,没有运用new,没有运用Object.create,也没有运用函数挪用建立一个对象。你也能够将对象看成一个实例将函数绑定到上面。
1 var obj = {
2 foo: "bar"
3 };
4
5 function logFoo() {
6 console.log(this.foo);
7 }
8
9 logFoo.apply(obj); //logs "bar"
- 当你用这类体式格局运用this的时刻,并不会越出当前的对象。只要有雷同直接父元素的属性才经由过程this同享变量
1 var obj = {
2 foo: "bar",
3 deeper: {
4 logFoo: function () {
5 console.log(this.foo);
6 }
7 }
8 };
9
10 obj.deeper.logFoo(); //logs undefined
- 你能够直接经由过程对象援用你须要的属性
var obj = {
foo: "bar",
deeper: {
logFoo: function () {
console.log(obj.foo);
}
}
};
obj.deeper.logFoo(); //logs "bar"
DOM event this
- 在一个HTML DOM事宜处置惩罚顺序内里,this一直指向这个处置惩罚顺序被所绑定到的HTML DOM节点
1 function Listener() {
2 document.getElementById("foo").addEventListener("click",
3 this.handleClick);
4 }
5 Listener.prototype.handleClick = function (event) {
6 console.log(this); //logs "<div id="foo"></div>"
7 }
8
9 var listener = new Listener();
10 document.getElementById("foo").click();
- 除非你自身经由过程bind切换了高低文
1 function Listener() {
2 document.getElementById("foo").addEventListener("click",
3 this.handleClick.bind(this));
4 }
5 Listener.prototype.handleClick = function (event) {
6 console.log(this); //logs Listener {handleClick: function}
7 }
8
9 var listener = new Listener();
10 document.getElementById("foo").click();
HTML this
- 在HTML节点的属性内里,你能够安排JavaScript代码,this指向了这个元素
1 <div id="foo" onclick="console.log(this);"></div>
2 <script type="text/javascript">
3 document.getElementById("foo").click(); //logs <div id="foo"...
4 </script>
override this
- 你不能重写this,由于它是保留字。
1 function test () {
2 var this = {}; // Uncaught SyntaxError: Unexpected token this
3 }
eval this
- 你能够经由过程eval来接见this
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
eval("console.log(this.foo)"); //logs "bar"
}
var thing = new Thing();
thing.logFoo();
这会形成一个安全题目,除非不必eval,没有其他体式格局来防止这个题目。
- 在经由过程Function来建立一个函数的时刻,一样能够接见this
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = new Function("console.log(this.foo);");
var thing = new Thing();
thing.logFoo(); //logs "bar"
with this
- 你能够经由过程with来将this增加到当前的实行环境,而且读写this的属性的时刻不须要经由过程this
1 function Thing () {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5 with (this) {
6 console.log(foo);
7 foo = "foo";
8 }
9 }
10
11 var thing = new Thing();
12 thing.logFoo(); // logs "bar"
13 console.log(thing.foo); // logs "foo"
很多人以为如许运用是不好的由于with自身就饱受争议。
jQuery this
- 和HTML DOM元素节点的事宜处置惩罚顺序一样,在很多状况下JQuery的this都指向HTML元素节点。这在事宜处置惩罚顺序和一些轻易的要领中都是管用的,比方$.each
1 <div class="foo bar1"></div>
2 <div class="foo bar2"></div>
3 <script type="text/javascript">
4 $(".foo").each(function () {
5 console.log(this); //logs <div class="foo...
6 });
7 $(".foo").on("click", function () {
8 console.log(this); //logs <div class="foo...
9 });
10 $(".foo").each(function () {
11 this.click();
12 });
13 </script>
thisArg this
假如你用过underscore.js 或许 lo-dash 你能够晓得很多类库的要领能够经由过程一个叫做thisArg 的函数参数来通报实例,这个函数参数会作为this的高低文。举个例子,这适用于_.each。原生的JavaScript在ECMAScript 5的时刻也许可函数通报一个thisArg参数了,比方forEach。现实上,之前论述的bind,apply和call的运用已给你制造了通报thisArg参数给函数的时机。这个参数将this绑定为你所通报的对象。
1 function Thing(type) {
2 this.type = type;
3 }
4 Thing.prototype.log = function (thing) {
5 console.log(this.type, thing);
6 }
7 Thing.prototype.logThings = function (arr) {
8 arr.forEach(this.log, this); // logs "fruit apples..."
9 _.each(arr, this.log, this); //logs "fruit apples..."
10 }
11
12 var thing = new Thing("fruit");
13 thing.logThings(["apples", "oranges", "strawberries", "bananas"]);
这使得代码变得越发简介,由于防止了一大堆bind语句、函数嵌套和this暂存的运用。