一、函数的定义方式
(1)、函数声明
function fn(){
console.log('this is a function')
}
(2)、函数表达式
var fn = function(){
console.log('赋值表达式函数')
}
(3)、内置构造函数的形式
var fn3 = new Function("num1", "num2", "return num1+num2");
console.log(fn3(2, 3));
推荐使用第二种形式,第三种用的相对较少,但也是必须掌握的内容。
二、函数的调用方法
2.函数的调用方法
(1)、函数名()
var fn1 = function () {
console.log("函数名()");
}
window.fn1();
fn1();
(2)、自调用
var fn2 = (function () {
console.log("自调用");
})();
(3)、call()调用
fn1.call();
(4)、对象中的函数调用
var obj1 = {
name: "小刚",
say: function () {
console.log(this.name + "喜欢吃辣条!")
}
}
obj1.say();
(5)、数组中函数调用
var arr = [
100,
"哈哈",
function () {
console.log(this);
console.log(this[1] + "我是数组中的元素,也是函数");
},
function () {
console.log("66666");
},
true
];
console.log(arr[2]);
arr[2]();//
arr[3]();
(6)、函数作为参数的调用
function fn3(x) {
x();
}
fn3(function () { console.log("我是函数也是参数"); });
(7)、函数作为返回值的调用
function fn4() {
return function () { console.log("我是函数也是返回值!") }
}
fn4()();
三、闭包
(1)定义
闭包就是能够读取其他函数内部变量的函数,由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 “定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的用途:
可以在函数外部读取函数内部成员
让函数内成员始终存活在内存中
原理
实际上在js的作用域机制中,有一个作用域是永恒的,就是window全局作用域,只要浏览器窗口不关闭,全局作用域是不会销毁的,这个windows全局作用域就是永恒的,在全局作用域中定一个函数,无论调用几次,这几次调用都可以共享操作同一个全局变量。
然而局部作用域不是啊,它只是在函数调用的时候有效,函数调用完成之后,局部作用域就关闭了,也也就意味着局部变量就失效了。所以我们在全局作用域下获取不到局部变量,再其他的作用域下也获取不到局部变量。
但是闭包不一样啊,可以让它的父函数作用域永恒,像windows全局作用域,一直在内存中存在。这也是闭包的本质。也是咱们讲的闭包的第二个作用锁住局部变量。当闭包函数调用时,它会动态开辟出自己的作用域,在它之上的是父函数的永恒作用域,在父函数作用域之上的,是window永恒的全局作用域。闭包函数调用完了,它自己的作用域关闭了,从内存中消失了,但是父函数的永恒作用域和window永恒作用域还一直在内存是打开的。闭包函数再次调用时,还能访问这两个作用域,可能还保存了它上次调用时候产生的数据。 只有当闭包函数的引用被释放了,它的父作用域才会最终关闭(当然父函数可能创建了多个闭包函数, 就需要多个闭包函数全部释放后,父函数作用域才会关闭)。
闭包之访问函数内部变量
一些关于闭包的例子
案例一:
var name = "this window";
var obj1 = {
name: "this object",
getName: function () {
var name = "this func1";
console.log(this.name);;//this object
console.log(name);//this window
return function () {
// 闭包函数
// var name = "this func2";
console.log(name);//this func1
console.log(this.name);//this window
}
console.log(this.name);
console.log(name);
}
}
obj1.getName()()
案例二:
function getRandom(min, max) {
var num = Math.floor(Math.random() * (max - min) + min);
console.log("num:" + num)
// 闭包
return function () {
console.log(num);
}
}
var result = getRandom(1, 10);
result();
result();
(4)、闭包之锁住变量
一些关于闭包的例子
// 1、随机数
/* function getRandom(min, max) {
var num = Math.floor(Math.random() * (max - min) + min);
console.log(num);
}
getRandom(10, 20);
getRandom(10, 20);
getRandom(10, 20);
getRandom(10, 20);
getRandom(10, 20);
getRandom(10, 20);
getRandom(10, 20);
getRandom(10, 20);
getRandom(10, 20); */
function getRandom(min, max) {
var num = Math.floor(Math.random() * (max - min) + min);
return function () {
console.log(num);
}
}
var result = getRandom(10, 20);
// console.log(result);
result();
result();
result();
result();
result();
result();
result();
result();
/* getRandom(10, 20)();
getRandom(10, 20)();
getRandom(10, 20)();
getRandom(10, 20)();
getRandom(10, 20)();
getRandom(10, 20)(); */
(5)、闭包案例一
一些关于闭包的例子
<script>
// 1
/* function fn() {
var name = "hello";
return function () {
return name;
}
}
var fnc = fn();
console.log(fnc()); */
// 2
/* var inner = "hi";
function fn() {
var inner = "hello";
inner = function () {
return inner;
}
console.log(inner());
}
fn(); */
//3
/* function fn() {
var name = "hello";
return function callback() {
return name;
}
}
var fn1 = fn();
function fn2(f) {
console.log(f());
}
fn2(fn1); */
//4
/* (function () {
var name = "hello";
var fn1 = function () {
return name;
}
fn2(fn1);
})()
function fn2(f) {
console.log(f());
} */
// console.log(name);
//5
/* function fn() {
var name = 'hello'
setName = function (n) {
name = n;
}
getName = function () {
return name;
}
return {
setName: setName,
getName: getName
}
}
var fn1 = fn();
console.log(fn1.getName());
fn1.setName('world');
console.log(fn1.getName()); */
// 6
/* var fn = (function () {
var arr = [];
console.log(1);
return function (val) {
if (arr.indexOf(val) == -1) {
// 数组中没有val才执行
arr.push(val);
console.log('函数被执行了', arr);
} else {
console.log('此次函数不需要执行');
}
console.log('函数调用完打印一下,方便查看已缓存的数组:', arr);
}
})();
fn(10);
fn(10);
fn(1000);
fn(200);
fn(1000); */
// 7
var arr = [];
for (var i = 0; i < 3; i++) {
// console.log(i);
arr[i] = (function (i) {
console.log(i);//2
return function () {
console.log(i);
}
})(i);
// console.log(i);
}
arr[0]();
arr[1]();
arr[2]();
/*
这个例子是闭包函数的一个典型应用,示例中只有两个函数嵌套,但是加上window全局作用域,
一共会有三个嵌套作用域。其中for循环了三次,三次调用了匿名自执行函数,就开了三个函数作用域,
开第一个作用域时保存的i的值是0,开第二个作用域保存的是1,第三个保存的是2。
三次调用父函数,又创建了三个闭包函数,每个闭包函数沿着它自己的作用域链向上访问,访问的值就都不相同。
三个闭包函数调用完了,它们自己的作用域就关闭了,
但是各自的父函数作用域还一直在内存中处于打开状态,下次闭包函数再调用时,再接着访问它自己的作用域。
就像在window全局作用域定义了一个函数,函数调用几次,全局作用域都在,每次调用都接着访问全局作用域。
*/
</script>
- 、闭包案例二
一些关于闭包的例子
4.函数递归
递归执行模型
5.斐波那契数列
斐波那契数,指的是这样一个数列:1、1、2、3、5、8、13、21、……在数学上,斐波那契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字来说,就是斐波那契数列由 0 和 1 开始,之后的斐波那契数列系数就由之前的两数相加。
常用的计算斐波那契数列的方法分为两大类:递归和循环。
(1)、递归
方法一:普通递归
代码优美逻辑清晰。但是有重复计算的问题,如:当n为5的时候要计算fibonacci(4) + fibonacci(3),当n为4的要计算fibonacci(3) + fibonacci(2) ,这时fibonacci(3)就是重复计算了。运行 fibonacci(50) 会出现浏览器假死现象,毕竟递归需要堆栈,数字过大内存不够。
function fibonacci(n) {
if (n == 1 || n == 2) {
return 1
};
return fibonacci(n - 2) + fibonacci(n - 1);
}
fibonacci(30)
方法二:改进递归-把前两位数字做成参数避免重复计算
function fibonacci(n) {
function fib(n, v1, v2) {
if (n == 1)
return v1;
if (n == 2)
return v2;
else
return fib(n - 1, v2, v1 + v2)
}
return fib(n, 1, 1)
}
fibonacci(30)
方法三:改进递归-利用闭包特性把运算结果存储在数组里,避免重复计算
var fibonacci = function () {
let memo = [0, 1];
let fib = function (n) {
if (memo[n] == undefined) {
memo[n] = fib(n - 2) + fib(n - 1)
}
return memo[n]
}
return fib;
}()
fibonacci(30)
方法四:改进递归-摘出存储计算结果的功能函数
var memoizer = function (func) {
let memo = [];
return function (n) {
if (memo[n] == undefined) {
memo[n] = func(n)
}
return memo[n]
}
};var fibonacci=memoizer(function(n){
if (n == 1 || n == 2) {
return 1
};
return fibonacci(n - 2) + fibonacci(n - 1);
})
fibonacci(30)
(2)、循环
方法一:普通for循环
function fibonacci(n) {
var n1 = 1, n2 = 1, sum;
for (let i = 2; i < n; i++) {
sum = n1 + n2
n1 = n2
n2 = sum
}
return sum
}
fibonacci(30)
方法二:for循环+解构赋值
var fibonacci = function (n) {
let n1 = 1; n2 = 1;
for (let i = 2; i < n; i++) {
[n1, n2] = [n2, n1 + n2]
}
return n2
}
fibonacci(30)
各种方法运行耗时如下图:普通递归>改进递归>for循环