关于 JS 函数的一切
本文基于: Bilibili - 自由的加百利
前置条件:
- 需掌握函数的编写、传参、返回、调用
- 理解作用域、掌握定时器的用法
- 知道引用类型和基本数据类型的区别
- 知道函数也是引用类型
- 听说过同步异步的概念
- 了解类和对象的关系
匿名函数
来看一下一个函数的基本属性:

匿名函数的自运行
我们可以将一个普通函数去掉它的名字,这样就成功的创建了一个匿名函数,并且编译器不会报错。

那么这个函数既然没有名字,我们又该怎么调用它呢?这时只需要使用一个小括号包裹住整个函数,再在函数体的末尾添加一个小括号就可以在创建函数之后立即执行这个函数。

这种写法,也叫作 匿名函数的自运行
其与直接在外部书写函数体内部的语句相比,优点就是不会造成变量污染,会在匿名函数内形成一个 封闭的作用域
小括号的作用
在匿名函数的外部加上一个小括号,实际的作用是 将该函数的声明变成了一个优先计算的表达式
( function(){...} )()
而表达式的运算结果就是这个 匿名函数 本身。拿到了函数本身之后,就可以在其后面加上一个小括号来调用它了。
把函数变成表达式?
既然小括号的作用是将函数的声明变成表达式,那么在函数周围加上运算符会不会有同样的效果呢?
+function(){...}()
!function(){...}()
~function(){...}()
void function(){...}()
delete function(){...}()
以上的几种写法都可以成功执行匿名函数,而且使用 +function(){...}() 这种方式执行函数自运行的效率是最高的。
递归函数
递归函数 是指一个函数直接或间接的调用自身,并在特定的情况下结束并放回运行结果
这里我们举一个 阶乘 的例子:
function F(N) {
return N * F(N - 1);
}
表面看上去,这个函数可以接收一个参数,并计算出这个数的阶乘。但是仔细想想就会发现不对劲,当 N = 1 时函数并没有停止自身的继续传递,也就是说这个函数没有停止条件,最终便会陷入一个死循环。结果就是 会在某一时刻,大量的函数将内存空间占满导致内存溢出。
也就是说我们上面写的这个函数,只有 递 没有 归
改造递归
我们尝试改变一下上面的 递归函数
首先要弄清楚,我们需要计算的是一个数 它的阶乘是多少。计算一个数字的阶乘便是让这个数每次乘以比他自身小 1 的数,直到乘到1。(说得不是很清楚,大家自行理解)
那么关键点就在于这个 直到
我们不能让它无止境的传递下去,在上面的例子中,参与递归的 N 为 1 时还在继续向内传递,0, -1, -2, -3... 我们所要做的就是当函数传递到 N = 1 时停止向内传递,直接返回 1 自身,将其自己交给外部的函数来调用,代码更改如下:
function F(N) {
if (N == 1) return 1;
return N * F(N - 1);
}
上面 if 语句的作用是:当 N 为 1 时,直接返回 1
这时运行一下就会发现,函数不报错了,而且也得到了我们想要的结果。
回调函数
回调函数,并不是指一种特殊的函数,而是指函数的使用方式
看一下下面的代码:
function f1(){
console.log(111);
}
function f2(){
console.log(222);
}
f1();
f2();
输出结果的顺序自然是先输出 111,再输出 222
但是如果我们给 f1() 添加一个定时器呢?
function f1(){
setTimeout(function(){
console.log(111);
}, 1000)
}
function f2(){
console.log(222);
}
f1();
f2();
这时便会先输出 222,一秒后输出 111。这种含有异步操作的函数就被称为 异步函数 ,异步函数最大的特点就是 后续的代码不需要排队,异步函数时可以和后续的代码并行的。f1() 就是一个典型的异步函数,你无法知道 f1() 和 f2() 哪一个会先结束。
回调函数引出
那么在有异步函数的情况下,如果我希望先输出 111,再输出222,要怎么做呢?
目前看来,唯一的办法是 把函数 f2() 放在 f1() 的内部调用
function f1(){
setTimeout(function(){
console.log(111);
f2();
}, 1000)
}
function f2(){
console.log(222);
}
f1();
假设有这样一个场景,项目组里有小白、小黄、小绿三个人,有一个工具函数 getToken()
function getToken(){
//异步函数......
}
它是一个异步函数,大家都在使用这个函数完成自己的业务,并且每个人都希望在 getToken() 结束后执行自己的代码,于是它们将函数写成了下面这样:

但是这种写法显然是错误的,因为异步函数保证不了函数的执行顺序。那么现在只能想办法将自己所写的函数放在异步函数内部,才有机会在其后面执行。
首先,我们给 getToken() 函数增加一个参数 callback
function getToken(callback){
//异步函数......
}
之后,三个人的代码就可以改成这样:

把自己的函数传进去,最后在 getToken() 的最后调用这个 callback
function getToken(callback){
//异步函数......
callback();
}
现在,所有人的代码都会在异步函数最后执行,这极大的提高了代码的可复用性,降低了开发维护的成本。
这种函数调用的方式就叫回调
字面意思就是:把自己的函数交给别人,回头再调。
构造函数
- 这一节需要理解 什么是面向对象
一个函数除了可以被当作函数,还可以被当作
class
function fn(){
}
let obj = new fn();
console.log( typeof obj );
我们可以直接使用 new 关键字来声明一个对象,这个时候,我们就说 fn() 是一个构造函数
那么 fn() 明明是一个空函数,这个对象是怎么来的呢?
构造函数的执行流程
问题的关键就在于这个 new 关键字。当你调用函数时在前面加上了 new 关键字,浏览器就会启动 构造函数 的执行流程:
function fn(){
this = {}
// 创建一个空对象,将其保存在this关键字中
...... //your code
return this;
}
let obj = new fn();
当然了,上面部分代码是不可见的。一个函数到底是普通函数还是构造函数,取决于你来怎么使用它。
但是通常,按照习惯,我们会将构造函数的首字母大写,普通函数的首字母小写。也就是说,如果你看到一个函数的首字母是大写的,在绝大多数的时候,它不应该被直接调用。
function User() {
......
}
let user = User(); ×
let user = new User(); √
在最新版的 JavaScript 已经支持了 class 关键字,你可以像 Java 一样定义一个类,并通过构造方法来生成对象。

闭包函数
function a(){
let x = 1;
function b(){
console.log(x);
}
}
函数 b() 是一个定义在函数 a() 内部的函数,所以其可以访问到变量 x ,变量 x 相对于函数 b() 来说就是一个全局变量。
如果我们把函数 b() 作为函数 a() 的返回值:
function a(){
let x = 1;
return function b(){
console.log(x);
}
}
let c = a();
c();
我们已知,函数 c() 就是函数 b() ,有由于函数 c() 是全局变量,因此,相当于在全局范围调用了函数 b() ,打破了函数 b() 只能在局部使用的限制,最终我们打印出了变量 x

在这里,函数 a() 所形成的作用域,叫做 闭包,函数 b() 被称作 闭包函数
函数的柯里化
这一节来源于知乎:https://zhuanlan.zhihu.com/p/163838720#:~:text=函数柯里化,就是,后,才执行原函数
function add(a, b) {
return a + b
}
function curry(fn) {
return function (a) {
return function (b) {
return fn(a, b)
}
}
}
let fn = curry(add)(1)(2)
关于 JS 函数的一切的更多相关文章
- 3.3 js函数
1.函数语法: 函数声明的方式:function 函数名(参数1,参数2-){//函数体;}函数调用:函数名(参数1,参数2-); 函数内不一定都指定返回值. 如果需要指定返回值,可用 return ...
- Js函数function基础理解
正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定 ...
- js函数表达式和函数声明的区别
我们已经知道,在任意代码片段外部添加包装函数,可以将内部的变量和函数定义"隐 藏"起来,外部作用域无法访问包装函数内部的任何内容. 例如: var a = 2; function ...
- 通用js函数集锦<来源于网络> 【二】
通用js函数集锦<来源于网络> [二] 1.数组方法集2.cookie方法集3.url方法集4.正则表达式方法集5.字符串方法集6.加密方法集7.日期方法集8.浏览器检测方法集9.json ...
- 通用js函数集锦<来源于网络/自己> 【一】
通用js函数集锦<来源于网络/自己>[一] 1.返回一个全地址2.cookie3.验证用户浏览器是否是微信浏览器4.验证用户浏览器是否是微博内置浏览器5.query string6.验证用 ...
- 100多个基础常用JS函数和语法集合大全
网站特效离不开脚本,javascript是最常用的脚本语言,我们归纳一下常用的基础函数和语法: 1.输出语句:document.write(""); 2.JS中的注释为//3.传统 ...
- JS函数
1.document.write(""); 输出语句2.JS中的注释为//3.传统的HTML文档顺序是:document->html->(head,body)4.一个浏 ...
- js函数和运算符
函数是由事件驱动或者它被调用时执行可重复使用的代码块. <script> function myFunction(){ Alert(“hello World!”): } </scri ...
- JavaScript学习03 JS函数
JavaScript学习03 JS函数 函数就是包裹在花括号中的代码块,前面使用了关键词function: function functionName() { 这里是要执行的代码 } 函数参数 函数的 ...
- JSF页面中使用js函数回调后台bean方法并获取返回值的方法
由于primefaces在国内使用的并不是太多,因此,国内对jsf做系统.详细的介绍的资料很少,即使有一些资料,也仅仅是对国外资料的简单翻译或者是仅仅讲表面现象(皮毛而已),它们的语句甚至还是错误的, ...
随机推荐
- Beizer。。。。。
<html> <head>AS</head> <script> var cvs; var context; //context.fill();//填充 ...
- selenium屏蔽启动浏览器启动时的提示信息
代码 from selenium import webdriver from selenium.webdriver import Remote from webdriver_helper import ...
- Elasticjob执行job幂等
ElasticJob的幂等机制,是指作业分片执行的幂等,他需要做到以下两点: 同一个分片在当前作业实例上不会被重复执行 一个作业分片不能同时在多个作业实例上执行 如何实现幂等 场景模拟:存在任务A执行 ...
- Goutte爬虫
安装 composer require fabpot/goutte:4.0
- stream流的概述以及idea与stream
前面自己学过一些流的概念,比如IO流,用于读写本地的数据. stream流主要是用于对集合/数组进行操作 idea现在已经很好的支持Stream流操作,在debug的时候可以很好的看到详细内容 下面以 ...
- 【Uni-App】其他配置笔记
manifest.json manifest.json 文件是应用的配置文件,用于指定应用的名称.图标.权限等.HBuilderX 创建的工程此文件在根目录,CLI 创建的工程此文件在 src 目录. ...
- 【JavaScript】文件上传下载问题
问题原因 一般文件上传前端甚至可以不涉及JS来实现 input标签套在form标签,由form标签直接发送请求就可以实现上传功能 但是现在很多项目都使用前后端分离,AJAX一刀切所有. input标签 ...
- SpringBoot3-快速入门
前置准备 Jdk17环境 idea 2021.2.1+才支持Java17 Springboot 3.0.5+ 开发百科书 springboot starter 官方支持的场景 springboot配置 ...
- 动手煮面的一次经历,加餐DIY
去年和今年一直是有疫情的,也是如此像我这种在学校呆着的不是在家窝着就是在学校窝着,尤其是到了假期的时候,如果晚回家几天那食堂的伙食就会变得十分的恐怖. 面对这样的伙食也是十分的无奈,虽然也是十分理解当 ...
- 再探 游戏 《 2048 》 —— AI方法—— 缘起、缘灭(1) —— Firefox浏览器下自动运行游戏篇
四年前曾经写过一过博客: 对 游戏 < 2048 > 的一些思考 虽然过去几年了,但是这个游戏一直没有搞懂该怎么使用AI算法来进行求解,这里再次对这个问题进行一些探索. ========= ...