"大哥,割草机借我用一下,我修整一下草坪。" ---- 谈谈this与JavaScript函数调用的不解之缘

在写上一篇有关apply和call的博文时(闲聊JS中的apply和call),起初我还是担心大家理解起来比较困难,因为要理解apply调用方式的前提是,至少先理解在JavaScript中函数是什么?this到底代表什么意思?等等。不过从大家的反馈来看,我的担心是多余的,诸位园友都是高手,理解这些基础的东东是小菜一碟。
话虽这样讲,不过今天我还是和大家聊聊JavaScript中与this相关的各种函数调用方式,可以把知识补充完整,日后回顾起来也比较方便。

【背景介绍】光明小区是一个别墅小区,家家户户门前屋后都有一块小草坪。小区的物业公司为了提高业主的满意度,为小区的业主提高了修整草坪的服务,并且还把割草机需要用的汽油列入每月的预算,确保随时都能提供优质的服务。

接下来我们就结合具体的业务场景,来讲解JavaScript中的函数调用方式。

1. 方法调用

针对光明小区为业主提供修整草坪服务的情况,我们可以用如下代码描述:

var GuangMing = {
    oilVolume:5000,
    cutGrass:function( grass_num ){
        var oilConsumption = grass_num * 20 ;    //假设每平米草坪需要耗油 20 ml
        console.log( ‘正在割 ‘ + grass_num + ‘ 平米的草坪,需要用油:‘ +  oilConsumption + ‘ mL。‘ );
        console.log( ‘原来有油:‘ + this.oilVolume + ‘ mL‘ );
        this.oilVolume = this.oilVolume - oilConsumption ;
        console.log( ‘用完之后,剩余的油量为:‘ + this.oilVolume + ‘ mL‘ );
        return ;
    }
};
GuangMing.cutGrass( 15 );       //小区门口有15平米的草坪需要割
console.log( ‘>>光明小区中剩余的油:‘ + GuangMing.oilVolume );

在浏览器的控制台运行之后,输出结果为:

正在割 15 平米的草坪,需要用油:300 mL。
原来有油:5000 mL
用完之后,剩余的油量为:4700 mL
>>光明小区中剩余的油:4700

这就是所谓的方法调用,与我们预期中的一样(特别是之前搞过Java等静态语言的高手),毫无违和感。
"oilVolume 和 cutGrass 都是GuangMing这个对象的成员,在cutGrass这个方法中调用的this.oilVolume当然是指的是和它同属于一个对象(GuangMing)的oilVolume 。"
不过,我们上面的这种理解从本质上来讲,是错误的。
cutGrass函数体中引用的this.oilVolume指代了GuangMing.oilVolume这个变量,不是因为在GuangMing对象的‘声明‘时,cutGrass和oilVolume都是GuangMing这个对象的成员,而是因为,我们是通过GuangMing.cutGrass( 15 ); 的方式来使用这个cutGrass这个函数的
也就是说:函数体内的this指向谁,不是看声明时,而是看运行时。
完整的结论我们随着后面的讲解逐步来完善。

2. 函数调用

光明小区的这项服务推出之后,得到了广大业主的好评。小区所属的红旗街道办也知道了这事,街道办的同志觉得这项服务是提升单位形象的好举措,于是也宣称可以提供割草的服务。不过,红旗街道办没有去自己购买割草机设备,而是当有人要求服务的时候,直接通知光明小区的物业公司帮忙提供一下就可以了。

用代码表示如下:

 1 var oilVolume = 8000 ;
 2
 3 var GuangMing = {
 4     oilVolume:5000,
 5     cutGrass:function( grass_num ){
 6         var oilConsumption = grass_num * 20 ;    //假设每平米草坪需要耗油 20 ml
 7         console.log( ‘正在割 ‘ + grass_num + ‘ 平米的草坪,需要用油:‘ +  oilConsumption + ‘ mL。‘ );
 8         console.log( ‘原来有油:‘ + this.oilVolume + ‘ mL‘ );
 9         this.oilVolume = this.oilVolume - oilConsumption ;
10         console.log( ‘用完之后,剩余的油量为:‘ + this.oilVolume + ‘ mL‘ );
11         return ;
12     }
13 };
14
15 var hongQiCutgrass = GuangMing.cutGrass ;    //光明小区所在的红旗街道也说给大家提供割草服务
16                                              //但是既然光明小区已经有割草机,所以街道办就没有单独购买割草机,
17
18 console.log( ‘>>光明小区中原来的油:‘ + GuangMing.oilVolume );                                             //而是直接使用光明小区的割草机。
19 hongQiCutgrass( 20 );
20 console.log( ‘>>光明小区中剩余的油:‘ + GuangMing.oilVolume );

运行后的输出结果如下:

>>光明小区中原来的油:5000
正在割 20 平米的草坪,需要用油:400 mL。
原来有油:8000 mL
用完之后,剩余的油量为:7600 mL
>>光明小区中剩余的油:5000

关键代码解析如下:

第1行:为了解释函数调用方式中,函数体内的this会指向全局对象(浏览器中就是window),增加一个全局变量oilVolume。

第15行:声明了一个全局变量hongQiCutgrass指向GuangMing对象的成员函数cutGrass。

第19行:运行全局函数hongQiCutgrass。

从最后输出的结果来看:cutGrass()中的this已经不是指代GuangMing对象了,而是指向‘全局对象‘。
我们再回顾一下前面得出的结论:
"函数体内的this指向谁,不是看声明时,而是看运行时。"
而刚才的场景中,运行时的代码是:hongQiCutgrass( 20 );变量hongQiCutgrass指向一个函数,变量hongQiCutgrass没有隶属于任何的其他对象,所以,变量hongQiCutgrass是一个全局变量,隶属于全局对象。
这也就意味着,运行hongQiCutgrass( 20 );时,
1. cutGrass函数体中的this是指向‘全局对象‘,
2. this.oilVolume 就指代另外一个全局变量oilVolume。运行前的值是8000,运行之后变成7600。

讲到这里,有些同学可能会问,hongQiCutgrass不是指向了GuangMing.cutGrass了吗?这可能需要理一下JavaScript中‘变量‘和‘变量所指的对象‘的关系。

(具体JavaScript引擎如何实现,我无从考证,下图是我对在JavaScript中‘变量‘和‘变量所指的对象‘之间的关系的理解,不对之处,欢迎各位大侠指出。)

我们知道,在JavaScript中:

1. 对象传递,都是引用传递(包括:赋值、参数传递等)。

2. 函数也是对象。

代码执行到第13行时,状态如(a)所示,对象GuangMing的成员cutGrass指向具体的函数对象F008。

当代码执行到第15行时,因为赋值的过程其实传递的也是引用,所以,GuangMing.cutGrass 和 hongQiCutgrass都指向了F008这个函数对象。

当代码执行到第19行时,因为hongQiCutgrass不属于某个具体的对象,所以,函数体内的this就指代全局对象,这就是所谓的‘函数调用方式‘。

如果把全局变量hongQiCutgrass理解为‘全局对象‘的成员,把全局变量oilVolume也理解为‘全局对象‘的成员,那么,其实‘方法调用方式‘和‘函数调用方式‘的原理是一致的。

某一天,旁边的东方小区见光明小区提供的‘割草服务‘很好,所以,它也向业主宣称可以提供割草服务。但是,东方小区也没有自己去购买一台割草机,而是配了一把‘光明小区‘的割草机的钥匙。如果用代码表示如下:

//...前面的代码不变,增加dongfang小区的声明部分
var DongFang = {
    oilVolume:6000,
    cutGrass:GuangMing.cutGrass
};
DongFang.cutGrass( 6 );

运行后的输出结果如下:

正在割 6 平米的草坪,需要用油:120 mL。
原来有油:6000 mL
用完之后,剩余的油量为:5880 mL

这样做,业务功能是达到了,但是存在的缺点也很明显,一方面比较繁琐,另一方面与常识不太相符,容易导致混乱,不好理解。
相当于小明家有两个小孩,小红家也有两个小孩。

>>开始时小明的妈妈跟小明交代说,你去买一个冰淇淋给你弟弟吃。(相当于声明时的样子)
>>后来,小明的妈妈远远地看到有人在喂冰淇淋。心里想着:‘应该是小明在给他弟弟小亮喂冰淇淋吧。‘
    而实际上呢,是小明在给小红的弟弟(小蓝)喂冰淇淋。(相当于运行时的样子)
>>这和‘小明妈妈‘的预期是不一样的!!
就是说,设计 DongFang 这个对象的设计师的这种搞法,GuangMing这个对象的设计师是预期不到的!某天GuangMing.cutGrass里面的功能变化了,DongFang.cutGrass的行为也会跟着变化,而这种关系,在代码中表现得并不明显。

所以,如果把某个‘方法f‘是‘声明‘在一个‘对象A‘里的,那么,这个对象的设计者当然是希望这个方法中的this,就是指向‘对象A‘,其他的对象如果要使用这个‘方法f‘也可以,但是,不能偷偷摸摸的‘引用‘,而是应该明确指出:‘对象B借用了对象A的方法f‘。‘对象B‘借用‘对象A‘的方法f,没错,这就是我们接下来马上要介绍的apply调用。
(看来JavaScript的设计者也是用心良苦哈,为了可能会‘乱用‘对象方法的熊孩子操碎了心^_^~~)

有人也许会问,JavaScript对this的处理方式,确实是不好理解哈,除了容易引起逻辑混乱之外,难道有什么好处吗?好处其实也是有的,我们这里先留一个悬念,等在讲解构造器调用时再细讲。

3. apply调用

关于apply调用模式的细节,前一篇博文"兄台息怒,关于arguments,您的想法和大神是一样一样的----闲聊JS中的apply和call" 已经讲得比较详细,这里就顺着今天我们的‘割草机‘的例子,展现一下相关的代码,一起回顾一下。

var GuangMing = {
    oilVolume:5000,
    cutGrass:function( grass_num ){
        var oilConsumption = grass_num * 20 ;    //假设每平米草坪需要耗油 20 ml
        console.log( ‘正在割 ‘ + grass_num + ‘ 平米的草坪,需要用油:‘ +  oilConsumption + ‘ mL。‘ );
        console.log( ‘原来有油:‘ + this.oilVolume + ‘ mL‘ );
        this.oilVolume = this.oilVolume - oilConsumption ;
        console.log( ‘用完之后,剩余的油量为:‘ + this.oilVolume + ‘ mL‘ );
        return ;
    }
};

var DongFang = {
    oilVolume:6000
};
//光明小区是全国文明小区,你要借用就光明正大的借用,偷偷配把钥匙放在自己的办公室就不应该了嘛
//正确的‘借用‘姿势
//因为我们只想割6平米的草坪,相当于只有一个‘参数列表‘,而不是一个参数数组,所以用call,而不是apply
GuangMing.cutGrass.call( DongFang , 6 ); 

运行后的输出结果如下:

正在割 6 平米的草坪,需要用油:120 mL。
原来有油:6000 mL
用完之后,剩余的油量为:5880 mL

4. 构造器调用

这种方式也许是应用最广泛的调用方式,特别是当我们刚刚学习用JavaScript写面向对象的应用时。尽管用这种方式存在没有私密性等诸多问题,大神Douglas Crockford也极力反对用这种方式创建对象,但是,因为这种方式"太方便"了,与传统的静态语言(Java等)的对象建模方式比较像,所以,似乎非常好‘理解‘。
闲话休说,我们还是回到我们的场景。据说光明小区物业配备‘割草机‘为广大业主割草的事获得了红旗街道办的好评,于是红旗街道办就给出了规定,辖区内每个小区的物业都应该配备这样的设备,提供割草服务,并指定相关的规章制度。详细的规章制度由小明来制定。于是,小明开始挑灯夜战,写下了如下的代码:

var Area = function( area_name ){
    this.name = area_name ;
    this.oilVolume = 5000 ;        //每个小区默认配备5000mL的油用于割草服务
}
Area.prototype.cutGrass = function( grass_num ){
        console.log( ‘**‘+ this.name + ‘小区**正在提供割草服务:‘ );
        var oilConsumption = grass_num * 20 ;    //假设每平米草坪需要耗油 20 ml
        console.log( ‘正在割 ‘ + grass_num + ‘ 平米的草坪,需要用油:‘ +  oilConsumption + ‘ mL。‘ );
        console.log( ‘原来有油:‘ + this.oilVolume + ‘ mL‘ );
        this.oilVolume = this.oilVolume - oilConsumption ;
        console.log( ‘用完之后,剩余的油量为:‘ + this.oilVolume + ‘ mL‘ );
        return ;
}

//创建一个东方小区
var dongfang = new Area( ‘东方‘ );
dongfang.cutGrass( 2 );
//创建一个劳动小区
var laodong = new Area( ‘劳动‘ );
laodong.cutGrass( 10 );

运行后的输出结果如下:

**东方小区**正在提供割草服务:
正在割 2 平米的草坪,需要用油:40 mL。
原来有油:5000 mL
用完之后,剩余的油量为:4960 mL
**劳动小区**正在提供割草服务:
正在割 10 平米的草坪,需要用油:200 mL。
原来有油:5000 mL
用完之后,剩余的油量为:4800 mL

在这里,我们做了如下的动作:
1. 构建构造器函数Area,以便可以用来创建不同的小区。
2. 为构造器函数Area的原型对象(prototype)增加割草服务的方法:cutGrass。因为我们知道,即使是不同的小区,提供割草服务的作业流程是一样的,所以把方法定义到原型对象中。
3. 使用new运算符,依次创建了‘东方小区‘(dongfang)和‘劳动小区‘(laodong),并分别调用了它们的割草服务(cutGrass)。
发现运行的结果和我们预期的一样。

现在,我们就来看一下new运算符(构造器调用)的原理,以var dongfang = new Area( ‘东方‘ ); 为例说明:

1. 执行new运算时,JavaScript引擎会生成一个全新的对象,这个对象的原型链,会‘链向‘构造函数的原型对象(prototype)。例子中的构造函数就是Area。
   注意:是一个全新的对象哦,就是JavaScript引擎会单独开辟一块空间给新建的对象。
   (‘变量‘和‘变量所引用的对象‘的关系,我们前面有介绍,这里就不再赘述。)
2. 在将新建的‘全新对象‘返回之前,JavaScript引擎会执行‘构造函数‘(Area)函数体内的代码,
   这时候,函数体内的this就是指代这个‘新建的全新的对象‘。 (注意:这就是构造器调用与其他调用方式的不同所在。)
   显然,如果没有这个new运算符而直接调用构造器函数,那么,依据前面我们分析的规则,函数体中的this将会指向‘全局对象‘,

而这种情况发生时,JavaScript不会报任何的错误!这也是许多大神为什么对‘构造器调用‘方式创建对象比较诟病的原因。
3. 构造器函数执行到最后,
    如果用return语句返回一个不是对象类型的值,或者没有执行return语句,则返回前面创建的全新的对象(这种情况是符合我们预期的)。
    如果用return语句返回一个是‘对象类型‘的值,那么,返回的就是指定的这个‘对象‘,而不一定是之前创建的全新的对象。
    (后面这种情况是我们需要避免的,试想,辛辛苦苦创建了一个对象,JavaScript引擎为其开辟了空间,但是被没有返回,没有变量引用它,意味着在瞎搞。)

关于构造器调用方式,整个过程就是这样。

我们再回去分析一下在讲解‘函数调用‘时留下的悬念,当时经过我们的分析,JavaScript的this动态绑定特性(函数体内的this指向谁,不是看声明时,而是看运行时。)似乎除了把问题搞复杂,没有带来什么好处。现在,我们来剖析一下例子中的dongfang.cutGrass( 2 );这个语句的来龙去脉,就能体会到JavaScript中的这个特性还是有它的意义的。
我们知道:

1. dongfang这个对象是前面通过new运算符创建的全新对象(约定:‘变量‘和‘变量所指的对象‘在不影响语义理解的情况下,我们就不作区别)。
2. 我们并没有为dongfang这个对象增加cutGrass的成员属性。

那么,为什么我们能够这样执行dongfang.cutGrass( 2 );呢?
答案是:JavaScript的原型继承机制。

当JavaScript引擎发现dongfang并没有cutGrass的成员属性时,它就会顺着‘原型链‘去找,而根据前面的new运算符的特点,dongfang这个全新的对象所‘链向‘的‘原型对象正是Area.prototype这个对象,在Area.prototype对象中发现有cutGrass这个函数,于是就调用了Area.prototype中的cutGrass这个函数。
(注意:我们的行文中用‘链向‘这个词,以便于区别‘指向‘或‘引用‘的含义,关于原型链的前世今生,我们有空再聊。)
整个过程的来龙去脉已经有点眉目了。
我们再综合后面的语句laodong.cutGrass( 10 ),从执行结果来看,laodong 和 dongfang 是两个独立的对象,它们都调用了Area.prototype中定义的cutGrass。
正是因为(函数体内的this指向谁,不是看声明时,而是看运行时。)这个特性,才确保了:
- 执行 dongfang.cutGrass( 2 ); 时,cutGrass()中的this指向 dongfang 这个对象。
- 执行 laodong.cutGrass( 10 ); 时,cutGrass()中的this指向 laodong 这个对象。
如果JavaScript不是采用这种‘动态‘的机制,cutGrass中的this就只能指向Area.prototype,那么,‘函数‘这种重要的数据类型,在‘原型继承‘的这种机制中就很难发挥强大的作用。

用闭包实现业务建模

4种调用模式已经讲解完毕,我们举了一个‘小区提供割草服务‘这样的例子,显然,前面的例子主要是为了讲解特性的方便而设计的,从‘业务建模‘的角度来看,是存在很多不足的。
如果真的要求创建一个小区这样的对象,然后对外提供割草服务该如何搞呢?请看代码:

var createArea = function( area_name ){
    var _name = area_name ;
    var _oilVolume = 5000 ;    //默认还是配备5000mL的油

    var cutGrass = function( grass_num ){
        console.log( ‘**‘+ _name + ‘小区**正在提供割草服务:‘ );
        var oilConsumption = grass_num * 20 ;    //假设每平米草坪需要耗油 20 ml
        console.log( ‘正在割 ‘ + grass_num + ‘ 平米的草坪,需要用油:‘ +  oilConsumption + ‘ mL。‘ );
        console.log( ‘原来有油:‘ + _oilVolume + ‘ mL‘ );
        _oilVolume = _oilVolume - oilConsumption ;
        console.log( ‘用完之后,剩余的油量为:‘ + _oilVolume + ‘ mL‘ );
        return ;
    }
    var new_area = {};
    new_area.cutGrass = cutGrass ;
    new_area.setOilVolume = function( oil_volume ){
        _oilVolume = oil_volume ;
        console.log( _name + ‘小区中割草机用的汽油配备到:‘ + oil_volume + ‘mL‘ );
        return _oilVolume;
    }
    new_area.getOilVolume = function( ){
        return _oilVolume;
    }
    return new_area ;
};

var dongfang = createArea( ‘东方‘ );
dongfang.cutGrass( 2 );
console.log( ‘还剩余可用的油:‘ + dongfang.getOilVolume() );
dongfang.setOilVolume( 9000 );
dongfang.cutGrass( 7 );

运行后的输出结果如下:

**东方小区**正在提供割草服务:
正在割 2 平米的草坪,需要用油:40 mL。
原来有油:5000 mL
用完之后,剩余的油量为:4960 mL
还剩余可用的油:4960
东方小区中割草机用的汽油配备到:9000mL
**东方小区**正在提供割草服务:
正在割 7 平米的草坪,需要用油:140 mL。
原来有油:9000 mL
用完之后,剩余的油量为:8860 mL

代码中展示的业务模型,基于如下的业务理解:
1. 小区的名称,仅仅在创建小区的时候通过参数传入设置一下,之后就不允许修改了。
    就像在现实生活中,小区的门口会搬一块大石头放到那里,上面刻上"爱情湾畔"几个大字,整好之后就不会去改了。
2. 小区对外提供割草服务(cutGrass)。
    可以看看小区现在还有多少割草机的储备油(getOilVolume),以便判断是否够这次割草作业。
    也可以给小区添加储备的油(setOilVolume),例如:物业公司规定,到了月底,储备的油要增加到8000mL,于是可以执行dongfang.setOilVolume( 8000 );。

这是一个采用‘闭包‘的方式构建的对象,我们发现,构建出来的dongfang对象,似乎也能满足我们的业务要求,创建好对象之后,不能改变小区的名称,只能通过setOilVolume和getOilVolume操作变量_oilVolume。更重要的特点时,居然‘没有用到this‘!
把这个例子补充在这里作为对比,我们不难发现:"在应用开发的过程中,一切应该以业务目标为导向,语言的各种特性只是一种手段"。
因为例子中的小区对象是一个非常具体的‘业务对象‘,所以可以不使用this。如果你是在写框架性质的对象,那你一定要理解this,一定要理解apply调用方式。"
闭包是一个"简单但很有内涵"的特性,前面我们聊过‘闭包‘与‘原型继承‘的使用,后续如果有时间,我们可以聊聊闭包的其他故事。

【总结】

JavaScript中函数的多种调用方式,是它作为动态语言,具有强大表现力的基础,此外,不对函数的参数类型进行校验、可以把函数作为对象到处传递,也是JavaScript的动态语言特性的体现。我们可以与其他静态语言作一个对比。

1. 类型检查

静态语言中,对传入的参数会做类型检查。而动态语言中,则不会对传入的参数类型以及个数做检查。
在Java社区,一天熊孩子小东去跟社区的王叔叔请求帮助。
小东:"张叔叔,今天我和小杰想去‘卫星农场‘割草,你可不可以帮我?"
老张:"好啊,你在这里登记一下,这是割草机的钥匙,让王叔叔跟你们一起去。"
小东找到老王,让他帮忙去‘卫星农场‘去割草,老王问了一下情况,跟小东说:"‘卫星农场‘哪里有草坪啊?那时麦地!"。

同样的场景,在JavaScript社区就会发生不一样的情况,
小东:"张叔叔,今天我和小杰想去‘卫星农场‘割草,你可不可以帮我?"
老张:"好啊,这是割草机的钥匙,你拿去用吧。"
到了‘卫星农场‘,小东就启动"割草机",不一会儿功夫,就把一块麦地里的小麦放倒了。
回来之后的日记是这么写的:"今天,天气不错,万里无云的天空飘着朵朵白云......这真是有意义的一天啊!"
【分析】
在静态语言(例如:Java)中,在编译阶段就会对方法的参数类型以及参数数量做检查,发现不对劲就提示‘编译错误‘,相当于每个方法边上都站着一个‘老王‘,负责对方法的类型和数量进行检查。
在动态语言(例如:JavaScript)中,则不会对方法做这方面的检查,如果把方法比作‘割草机‘,那么对于JavaScript引擎来说,它才不管你割的是‘小草‘还是‘小麦‘,不管你往‘割草机‘的油箱中加入的是‘汽油‘还是‘酱油‘,等运行的时候,发现错误才给你指出这里出错了。
虽然JavaScript在函数这个层面没有提供类型检查的服务,作为补充,它也提供了typeof, instanceof等检查类型的方式,所以,我们经常看到一些函数的函数体的开始部分,是一堆的if-else,对传入的参数进行检查。
想起了高考考场的门卫保安:‘有没有带准考证?没带的出去。有没有带手机?带了的也出去。脱一下....‘
没办法,毕竟‘考生‘不是‘保安‘,‘保安‘也不是‘考生‘。
"小李,这是杜老板的儿子。"
"..."

2. 方法调用

在静态语言(Java)中,当我们需要使用某个对象(类)的方法的时候,我们至少是要通过这个对象(类)来调用的,或者继承这个类,然后在子类中使用父类的方法,或者将这个类的对象作为当前对象的一个成员,然后通过这个成员调用。相当于,要使用社区的‘割草机‘,至少是应该打声招呼登记一下的。
但是在动态语言(JavaScript)中,函数在内存中似乎是独立存在的(我们知道:在JavaScript中,函数也是对象),尽管声明时好像这个函数是‘属于‘对象A的,但只有真正使用的时候(运行时)才表现出来它是‘属于‘谁的,函数体内的this是指代谁的。
并且,引用某个函数并没有太多的限制,就像你有割草机的钥匙,你就可以启动这台割草机,使用这台割草机。

很显然,JavaScript的某些特性(原型链、apply调用方式、高阶函数属性、闭包等等)使JavaScript变得非常灵活,当然也容易出错。这跟现实生活一样,太多的条条框框,你会觉得不自由,行为受到束缚,但是,没有了一些规则约束,没有了父母的唠叨和提醒,有时也确实会犯错误。
所以,在使用JavaScript开发应用时,一方面我们通过编程规范(共同的约定)来减少错误的发生。比如:在社区中,大家约定从‘业主之家‘借的梯子,用完之后要还回去。另一方面,我们也可以利用JavaScript中的某些特性(例如:闭包),来构建封装性、稳定性更好的具有弹性的应用。比如:我们担心熊孩子在使用社区的割草机时,可能会把‘酱油‘当做‘汽油‘倒到割草机的油箱中,这时候我们可以把割草机的油箱锁起来,不让随便往里面加东西。
显然熊孩子是玩不好JavaScript的,在更多的时候,我们要使用JavaScript,是因为我们要开发的应用只能用它来开发,既然没得其他的选择,那就只有‘好好学习‘一条路了。正如某位前辈所言,只有我们理解了它的精华,也明白它的糟粕,才能真正用好它。
好像有人在敲门,应该是网上预定的宵夜送到了.....
好,今天就和大伙聊到这里,感谢诸位捧场。

时间: 2024-10-13 00:24:45

"大哥,割草机借我用一下,我修整一下草坪。" ---- 谈谈this与JavaScript函数调用的不解之缘的相关文章

思考在路上——土包子的“拱形之旅”

 土 包 子 的 “拱 形 之 旅” 摘要:又是一年盛夏日,也是土包子背起行囊远行的日子了.作为一个土包子,常听说这么一句话,旅行是一件“花钱买罪受”的事,抱着将信将疑的态度我开启了自己的拱形之旅(我要验证一下这个说法的可靠性).之所以成为拱形之旅,因为从地图上看的话我的路线:蚌埠-泰安-青岛-南京-杭州构成了一个拱形,无奈才疏学浅只能以“形”给自己此次的旅程命名啦!经过约一个星期的漂泊,通过亲身体验发现,旅行是一次肉体与灵魂分离的活动.伴随着肉体上各种折磨的同时,精神上却在享受着各种盛宴.为了

Web开发入门不得不看

如今,各种互联网的Web应用程序层出不穷,那么如何快速入门,成长为一个优秀的Web开发工作者呢? 这个问题不容易回答,几乎所有的培训机构都不能清晰地解答. 所以对于Web开发刚刚入门的菜鸟们,我觉得只有通过去做,去实验,学会Web开发,可能是学着学着,实验着实验着就会了. 没有人告诉你如何去做.我学习的时候,我的导师只是给了我一堆视频,一堆文档.我们从明确一个目标开始:"哦,这个阶段,我要独立完成一个简单的Web应用程序,例如新闻发布系统". 或许学会如何开发Web应用程序只是在完成这

下一代大数据系统和4S标准

大数据行业发展到今天,它创造的价值和带来的社会效应,大家已经看得很明白,同时很多问题和不足也暴露出来,特别是hadoop能够提供的数据处理能力,现在已经挖掘到极限,但是现在各行业对数据的存储和计算需求,似乎却没有停止的迹象.在最近的一次大数据论坛上,大家开始讨论下一代大数据系统和系统标准,借此机会,我们Laxcus大数据实验室表达了自己的看法,提出了4S标准,得到与会者的普遍赞同和肯定.回来后,觉得这个话题应该再说说,所以借着CSDN这个平台,和诸位谈谈我们眼中的下一代大数据系统和4S标准. 概

前赴后继的净菜电商真只是伪需求?

据外媒报道,美国半成品净菜外卖平台独角兽Blue Apron (中文名"蓝围裙")在最近向纽约证交所提交了IPO招股书.作为一个外卖相关产业,净菜电商也算热门关注的领域,借Blue Apron上市的机会我们来谈谈净菜电商这一独特的电商模式. 据悉,Blue Apron成立于纽约布鲁克林,专门配送供2人或家庭的新鲜食材及不断更新的菜谱,供消费者烹饪使用.随着A轮融资的到位,Blue Apron在六个月内,就覆盖了密西西比州东部的所有城市.第二年在旧金山开设中央厨房,第三年在泽西市落成一家

Xamarin Evolve 2016 Keynote回顾

编者语:距离上一次Xamarin Evolve 大会足足有两年时间了,这两年整个行业都在变化,Xamarin是整个.NET行业的表表者.两年过去Xamarin终于并入微软,免费了,也开源了.还有什么大招呢?刚结束的Xamarin Evolve给了你很好的答案,下面让我和大家分享下. Xamarin对比起其他跨平台技术,优势在于原生的UI控件,原生的运行性能,和跟原生的API无缝对接.这个是对比起其他跨平台 技术来说都占了绝对的优势.所以Nat Friedman 上台后首先重复了这点. 有Xama

浅谈 JavaScriptCore

来源:XcodeMen(王瑞华) 链接:http://t.cn/RVqQI5p 本文由我们团队的王瑞华童鞋撰写. OS X Mavericks 和 iOS 7 引入了 JavaScriptCore 库,它把 WebKit 的 JavaScript 引擎用 Objective-C 封装,提供了简单,快速以及安全的方式接入世界上最流行的语言.在项目的实际开发中,由于需要与 H5 端有交互,所以采用了JavaScriptCore的交互方式,借此参与该项目的机会,对JavaScriptCore也有了一些

BAT三巨头:不看星座看地域

前一段时间,中国互联网行业有个说法,就是要当老大必须是天蝎座才行,理由是BAT三大巨头中,李彦宏和马化腾都是天蝎座.不过,马云可是天秤座 (这好歹也给老冀一点安慰).最近有位互联网的朋友给老冀投了一篇稿,这位朋友另辟蹊径,从地域的角度来看巨头们的风格,大家看看他说的是否有理. 为什么百度在北京.腾讯在深圳,而阿里巴巴却在杭州?借着这样的思考,我想谈谈BAT中国互联网三巨头的经营理念和地域文化对创始人的影响.(钛媒体注:还可参考此前钛媒体文章:<互联网三城记之:阿里巴巴为何选择杭州>) 从地域分

如何做好基层技术管理工作?

最近有朋友与我探讨了软件基层技术管理工作方面的话题,借此从动机和方法两方面谈谈我的看法. 动机 要做好基层技术管理工作,首先要确保自己有良好的动机,即明白自己为何要走上技术管理岗位.做管理的根本是为了获得权力,但获得权力的动机却存在很大的差别. 第一种单纯是为了利己.有相当数量的人往技术管理岗位"挤",是为了获得以后在工作中可以少做或挑做工作内容的权力:也有的人是为了更快.更多地获得公司动向的资讯,以体现"领导"的"与众不同":还有人是为了更高的

转载 《我用 TypeScript 语言的七个月》

快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中. <HTML开发Mac OS App 视频教程> 土豆网同步更新:http://www.tudou.com/plcover/VHNh6ZopQ4E/ 百度网盘同步:http://pan.baidu.com/s/1jG1Q58M 分享  [中文纪录片]互联网时代   http://pan.baidu.com/s/1qWkJfcS 官方QQ群:(申请加入,说是我推荐的) App实践出真知 4