这篇,我们来学习下自定义函数以及即时函数的内容。

四、自定义函数

  函数可以动态定义,也可以分配给变量。如果创建了一个新函数,并且将其分配给保存了另外函数的同一个变量,那么就以一个新函数覆盖了旧函数。在某种程度上,回收了旧函数指针以指向一个新函数。而这一切发生在旧函数体的内部。在这种情况下,该函数以一个新的实现覆盖并重新定义了自身。

var scareMe = function() {
alert("Boo!");
scareMe = function() {
alert("Double Boo!");
};
}; // 使用自定义函数
scareMe();
scareMe();
scareMe();

  当您的函数有一些初始化准备工作要做,并且仅需要执行一次,那么这种模式就非常有用。因为并没有理由去执行本可以避免的重复工作,即该函数的一些部分可能并不再需要。在这种情况下,自定义函数(self-defining function)可以更新自身的实现。

  这里多说下,个人觉得,这里的自定义,并非单纯的指开发者创建的非内置函数,而是指自己定义自己的函数,也就是self-defining。所以,这里要尤其注意一下。

  使用此模式可以显著地帮助您提升应用程序的性能,这是由于重新定义的函数仅执行了更少的工作。

  这种函数的另一个名称是“惰性函数定义”(lazy function definition),因为该函数直到第一次使用时才被正确的定义,并且其具有后向惰性执行了更少的工作。

  该模式的其中一个缺点在于,当它重定义自身时已经添加到原始函数的任何属性都会丢失。此外,如果该函数使用了不同的名称,比如分配给不同的变量或者以对象的方法来使用,那么重定义部分将永远不会发生,并且将会执行原始函数体。

  下面的例子,我们将上面的scareMe()函数以第一类对象的使用方式来使用:

  1. 添加一个新的属性
  2. 函数对象被分配给一个新的变量。
  3. 该函数也以一个方法的形式使用。
var scareMe = function() {
alert("Boo!");
scareMe = function() {
alert("Double Boo!");
};
}; // 1、添加一个新的属性
scareMe.property = "propertly";
// 2、赋值给另一个不同名称的变量
var prank = scareMe;
// 3、作为一个方法使用
var spooky = {
boo:scareMe
}; // calling with a new name
prank(); //输出“Boo!”
prank(); //输出“Boo!”
console.log(prank.property); // 输出“properly” // 作为一个方法来调用
spooky.boo(); //输出“Boo!”
spooky.boo(); //输出“Boo!”
console.log(spooky.boo.property); //输出“properly” // 使用自定义函数
scareMe(); //输出“Double Boo!”
scareMe(); //输出“Double Boo!”
console.log(scareMe.property); //输出undefined

  正如上面代码所示,当将该函数分配给一个新的变量时,如预期的那样,函数的自定义(self-definition)并没有发生。每次当调用prank()时,它都通知"Boo!"消息,同时它还覆盖了全局scareMe()函数,但是prank()自身保持了旧函数的可见,其中还包括属性。当该函数以spooky对象当boo()方法使用时,也发生了同样的情况。所有这些调用不断的重写全局scareMe()指针,以至于当它最终被调用时,他才第一次具有更新函数主体并通知“Double boo”消息的权利。此外,它也不能访问scareMe.property属性。

  再多说两句,个人理解:

// 我们先来看,为什么上面的代码访问不到property属性。
// 我们把代码简化一下:
var scareMe = function() {
alert("Boo!");
scareMe = function() {
alert("Double Boo!");
};
};
scareMe.property = "propertly"; console.log(scareMe.property); //输出“propertly”
scareMe(); //输出“Boo!”
console.log(scareMe.property); //输出“undefined”
scareMe(); //输出“Double Boo!”
console.log(scareMe.property); //输出undefined

  这是为什么呢?在第一次执行scareMe()方法后,就找不到property属性了。因为第一次执行后,绑定的是外层变量的指针,此时在绑定属性的时候,是绑定在这个指针上的。而当函数执行了一次后,内部的scareMe()函数,替换了原来的函数指针。它已经不是曾经的它了!所以property属性是绑定在外层的,那当然再就找不到了被。

  那么,由于它被覆盖了。并且,以后不会再有新的东西覆盖掉这个“新函数指针”,所以,以后每次执行都不会执行旧的内容。所以,以后每次的执行都会打印"Double Boo!"。那么,我们再看代码:

// 我们先来看,为什么上面的代码访问不到property属性。
// 我们把代码简化一下:
var scareMe = function() {
alert("Boo!");
scareMe = function() {
alert("Double Boo!");
scareMe = function () {
alert("Third Boo!");
}
};
};
scareMe.property = "propertly"; console.log(scareMe.property); //输出“propertly”
scareMe(); //输出“Boo!”
console.log(scareMe.property); //输出“undefined”
scareMe(); //输出“Double Boo!”
console.log(scareMe.property); //输出undefined
scareMe(); //输出“Third Boo!”
scareMe(); //输出“Third Boo!”
scareMe(); //输出“Third Boo!”

  我们来看这段代码,我自以为是的又加了一层,于是,我希望不用我说,你也已经懂了。

  最后,再说一下,为什么赋值给一个其它名字的变量以及用对象的方法来使用的时候,重定义永远没有发生。个人理解,因为你每次在执行的时候,赋值的动作是有的,但是并没有把我覆盖,所以,每次都是重定义,每次都无法执行新的内部逻辑。所以,在最开始的那个例子里,当你第一次调用scareMe()的时候,就走了Double Boo!语句。因为前面prank()或者spooky.boo()的每一次执行,都重新定义了scareMe()。希望我说的,你理解了。

五、即时函数

  即时函数模式(Immediate Function pattern)是一种可以支持在定义函数后立即执行该函数的语法。

(function() {
alert('watch out!');
}());

  这种模式本质上只是一个函数表达式(无论是命名还是匿名的),该函数会在创建后立刻执行。在ECMAScript标准中并没有定义术语“即时函数(immediate function)”,但是这种模式非常简洁。

  该模式由一下几部分组成:

  • 可以使用函数表达式定义一个函数(函数声明是不可以的)。
  • 在末尾添加一组括号,这将导致该函数立即执行。
  • 将整个函数包装在括号中(只有不将该函数分配给变量才需要这样做)。
(function() {
alert('watch out!');
})();

  这样的语法也可以,但是JSLint偏好第一种。

  这种模式是非常有用的,因为它为初始化代码提供了一个作用域沙箱。比如:当页面加载时,代码必须初始化执行一些设置任务,比如附加事件处理程序、创建对象等诸如此类的任务。所有这些工作仅需要执行一次,因此没有理由去创建一个可复用的命名函数。但是代码也还需要一些临时变量,而在初始化阶段完成后就不再需要这些变量。然而,以全局变量形式创建所有哪些变量是一个差劲的方法。这就是为什么需要一个即时函数的原因,用以将所有代码包装到它的局部作用域中,且不会将任何变量泄露到全局作用域中;

(function () {
var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
today = new Date(),
msg = 'Today is' + days[today.getDay()] + ', ' + today.getDate();
alert(msg);
}());

  如果上面的代码没有包装在即时函数中,那么days、today和msg等变量将会成为全局变量,并遗留在初始化代码中。

即时函数的参数

  也可以将参数传递到即时函数中:

(function (who,when) {
console.log("I met " + who + " on " + when);
}("Zaking",new Date()));

  一般情况下,全局对象是以参数方式传递给即时函数的,以便于在不使用window指定全局作用域限定的情况下可以在函数内部访问该对象,这样将使得代码在浏览器环境之外时具有更好的操作性。

(function (global) {
// 通过global访问全局变量
}(this));

  请注意,一般来说,不应该传递过多的参数到即时函数中,因为这样将迅速成为一种阅读负担,导致在理解代码运行流程时需要不断地滚动到该函数的顶部和底部。

即时函数的返回值

  正如任何其他函数一样,即时函数可以返回值,并且这些返回值也可以分配给变量:

var result = (function() {
return 2 + 2;
}());

  另一种方式也可以达到效果,即忽略包装函数的括号,因为将即时函数的返回值分配给一个变量时并不需要这些括号:

var result = function() {
return 2 + 2;
}();

  这个语法虽然比较简单,但是看起来可能有点令人误解。在没有注意到该函数尾部的括号时,一些阅读代码的人可能会认为result变量指向一个函数。实际上,result指向由即时函数返回的值。

  另一种语法也可以得到同样的结果:

var result = (function() {
return 2 + 2;
})();

  实际上,即时函数不仅可以返回原始值,还可以返回任意类型的值,包括另外一个函数。因此,可以使用即时函数的作用域以存储一些私有数据,而这特定于返回的内部函数。

var getResult = (function() {
var res = 2 + 2;
return function () {
return res;
}
}());

  上面这段代码,即时函数返回的值是一个函数,它将分配给变量getResult,并且将简单的返回res值,该值被预计算并存储在即时函数的闭包中。

  当定义对象属性时也可以使用即时函数。想象一下,如果需要定义一个在对象生存期内永远都不会改变的属性,但是在定义它之前需要执行一些工作以找出正确的值。此时,可以使用一个即时函数包装这些工作,并且即时函数的返回值将会成为属性值。

var o = {
message:(function () {
var who = "me",
what = "call";
return what + " " + who;
}()),
getMsg:function () {
return this.message;
}
};
console.log(o.getMsg())
console.log(o.message)

  这个例子中,message是一个字符串属性,而不是一个函数,但是它需要一个在脚本加载时执行的函数来帮助定义该o.message属性。

优点和用法

  即时函数模式得到了广泛的使用。它可以帮助包装许多想要执行的工作,且不会在后台留下任何全局变量。定义的所有这些变量将会是用于自调用函数的局部变量,并且不用担心全局空间被临时变量所污染。

  还可以使用即时函数模式来定义模块(当然ES6中以及由模块的概念了,但是这样的方法仍旧有学习的地方):

// 文件module1.js中定义的模块module1
(function() {
//模块1的所有代码
}());

  使用这种方式,可以编写其他模块。然后,将该代码发布到在线站点时,可以决定哪些功能准备应用于黄金时间,并且使用构建脚本将对应文件合并。

  这篇文章就到这里了。后面还有...

《JavaScript 模式》读书笔记(4)— 函数3的更多相关文章

  1. JavaScript模式读书笔记 第4章 函数

    2014年11月10日 1.JavaScript函数具有两个特点: 函数是第一类对象    函数能够提供作用域         函数即对象,表现为:         -1,函数能够在执行时动态创建,也 ...

  2. JavaScript模式读书笔记 文章3章 文字和构造

    1.对象字面量     -1.Javascript中所创建的自己定义对象在任务时候都是可变的.能够从一个空对象開始,依据须要添加函数.对象字面量模式能够使我们在创建对象的时候向其加入函数.       ...

  3. 《你不知道的javascript》读书笔记2

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. 这篇笔记是这本书的下半部分,上半部分请见<你不知道的java ...

  4. 《编写可维护的javascript》读书笔记(中)——编程实践

    上篇读书笔记系列之:<编写可维护的javascript>读书笔记(上) 上篇说的是编程风格,记录的都是最重要的点,不讲废话,写的比较简洁,而本篇将加入一些实例,因为那样比较容易说明问题. ...

  5. C语言深度解剖读书笔记(6.函数的核心)

    对于本节的函数内容其实就没什么难点了,但是对于函数这节又涉及到了顺序点的问题,我觉得可以还是忽略吧. 本节知识点: 1.函数中的顺序点:f(k,k++);  这样的问题大多跟编译器有关,不要去刻意追求 ...

  6. Javascript & JQuery读书笔记

    Hi All, 分享一下我学JS & JQuery的读书笔记: JS的3个不足:复杂的文档对象模型(DOM),不一致的浏览器的实现和便捷的开发,调试工具的缺乏. Jquery的选择器 a. 基 ...

  7. 《你不知道的javascript》读书笔记1

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. js的工作原理 引擎:从头到尾负责整个js的编译和运行.(很大一部 ...

  8. JavaScript中的this—你不知道的JavaScript上卷读书笔记(三)

    this是什么? this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件.this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式.当一个函数被调用时,会 ...

  9. JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)

    前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...

  10. SQL反模式读书笔记思维导图

    在写SQL过程以及设计数据表的过程中,我们经常会走一些弯路,会做一些错误的设计.<SQL反模式>这本书针对这些经常容易出错的设计模式进行分析,解释了错误的理由.允许错误的场景,并给出更好的 ...

随机推荐

  1. Vue3.5中解构props,让父子组件通信更加丝滑

    前言 在Vue3.5版本中响应式 Props 解构终于正式转正了,这个功能之前一直是试验性的.这篇文章来带你搞清楚,一个String类型的props经过解构后明明应该是一个常量了,为什么还没丢失响应式 ...

  2. ARM汇编: B、BL 与R14(LR)、R15(PC)

    1. b与bl指令的作用是什么? b与bl指令的作用:实现程序跳转,也就是调用子程序. 2. b与bl指令的区别是什么? b与bl指令的区别: b指令:简单的程序跳转,跳转到到目标标号处执行. bl指 ...

  3. 【USB3.0协议学习】Topic4·USB3.0的Port Connect State Machine和设备枚举

    上一节的文章[USB3.0协议学习]Topic2·USB3.0的LTSSM分析中我们详细分析了USB3.0协议中的链路训练状态机(LTSSM)的各状态和跳转条件,覆盖了所有LTSSM状态.本文我们将从 ...

  4. Android平台下的cpu利用率优化实现

    目录 背景 CPU调频 概念 实现 验证 线程CPU亲和性 概念 亲和性控制 API 应用层控制实现 验证 线程优先级 概念 实现 验证 背景 为了进一步优化APP性能,最近针对如何提高应用对CPU的 ...

  5. .NET云原生应用实践(一):从搭建项目框架结构开始

    开篇 很早之前就想做一套案例,介绍.NET下如何从零开始搭建一个云原生的应用程序.不过这个话题有点大,会要包含很多内容.我本打算从新建一个ASP.NET Core Web API应用程序开始介绍,但又 ...

  6. 对抗生成网络(GAN)简单介绍

    对抗生成网络主要由生成网络和判别网络构成,GAN在图像领域使用较多.利用生成网络生成假的图像,然后利用判别器是否能判断该图像是假的. 1.用于医学图像分割,一般我们可以利用一个U-Net网络生成分割结 ...

  7. modbus基础

    Modbus是一种单主站的主从通信模式,Modbus只能有一个主站,允许多个从站(0-247):从站之间不能交流:主站发送数据,从站应答: 一主多从 : 1. 地址码,表,功能码 地址码一般是Modb ...

  8. Vue 项目优化解决方案 有哪些 ?

    1. 打包的时候 , 通过插件把css代码单独抽离出来使用 link 引入 2. 打包的时候 , 通过webpack内置插件optimization  把 公共的js代码抽离出来 ps:打包之后,代码 ...

  9. 06 导师不敢和你说的水论文隐藏技巧,顶刊、顶会、水刊的论文读哪个,如何做一个称职的学术裁缝.md

    博客配套视频链接: https://www.bilibili.com/video/BV11g41127Zn/?spm_id_from=333.788&vd_source=b1ce52b6eb3 ...

  10. 马斯克对于CEO职能,发挥人才天赋,激励人才的想法

    Time Interview with Elon Musk, 29 September 2011. Content 1 Have people do be focused on doing usefu ...