前端基础学习之js高级-函数(高阶函数、闭包、沙箱、递归)

(一) 创建函数的两种方式

1.函数声明
f1();//可以调用
//函数声明
function f1() {
	console.log("我是f1的函数内容");
}
2.函数表达式
 f2();//报错
//函数表达式
 let f2=function () {
	console.log("我是f2的函数内容");
 }

因为预解析的原因,用函数声明的方式创建函数,在函数声明前也可以调用,而用函数表达式的时候则会报错。

 if(true){
 	function f2() {
		console.log("11111");
	}
 }else{
 	function f2() {
 		console.log("222222");
 	}
 }
 f2();//谷歌中是11111 但是在IE8中是22222
 



var f1;
if(true){
	f1=function () {
		console.log("11111");
	};
}else{
	f1=function () {
		console.log("2222");
	}
}
f1();// 谷歌IE都是11111


//综上所述---一般情况下最好使用函数表达式,这样减少因浏览器版本而造成的问题

(二) 函数也是对象

对象中都有__proto__属性,构造函数中都有prototype属性, 因为函数中也有__proto__,函数实际上都是Function构造函数的实例对象。

let f1=function () {
	console.log("heihei");
}
console.log(f1);
console.dir(f1);

//对象中都有__proto__ 是对象
//构造函数中都有prototype 是对象
//函数也是一种对象
//因为函数中有prototype,也有__proto__
//且 所有的函数其实都是Function 的实例对象
//即:
let f2=new Function("num1","num2","return num1+num2")
console.log(f2(20,30));

//上面这种写法就等价于
function f3(num1,num2) {
	return num1+num2;
}
console.log(f3(20,30))

(三) 函数里面几个成员介绍

在这里插入图片描述

  1. arguments: 实参 arguments.length 为实参的个数;
  2. caller: 调用者 --比如在函数f2中调用f1,那么此时f1的调用者就是f2;
  3. length:形参的个数;
  4. name:函数名,不可修改;
  5. prototype: 原型对象;
  6. proto 指向构造函数Function的原型对象;
function f1(x,y) {
	console.log(f1.name);
	console.log(f1.arguments);
	console.log(f1.length);
	console.log(f1.caller);
}
f1(10,20,30,40);
console.dir(f1);
function f2() {
	console.log("这是f2的代码块");
	f1();
}
f2();

(四) 函数作为参数使用 --回调函数

  • 最常见的使用途径:定时器
function f1(fn) {
	setInterval(function () {
		console.log("定时器的开始");
		fn();
		console.log("定时器的末尾");
	},1000)
}
f1(function () {
	console.log("我是参数中的函数调用");
})
  • 匿名函数作为sort函数的参数使用的小例子:
let arr=[10,20,100,200,120,260,30,300,40];
// arr.sort();
// console.log(arr);


//匿名函数作为sort函数的参数使用
arr.sort(function (obj1,obj2) {//从小到大排
	if(obj1>obj2){
		return 1;
	}else if(obj1 == obj2){
		return 0;
	}else{
		return -1
	}
});
console.log(arr);

arr.sort(function (obj1,obj2) {//从大到小排
	if(obj1>obj2){
		return -1;
	}else if(obj1 == obj2){
		return 0;
	}else{
		return 1
	}
});
console.log(arr);


let arr2=["wqe","zxc","asdd","qweqeq"];
arr2.sort(function (obj1,obj2) {
		if(obj1>obj2){
		return 1;
	}else if(obj1 == obj2){
		return 0;
	}else{
		return -1
	}
});
console.log(arr2);

(五) 函数作为返回值使用

//demo1
function f1() {
	console.log("f1被调用啦");
	return function () {
		console.log("我是f1的返回值函数,啊被调用了 ");
	}
}
let ff=f1();
ff();

//demo2 判断传入的类型是不是传入的参数的类型
//首先类推一下子
let num1=10;
console.log(typeof num1);//number typeof 是获取对象类型

let obj1={};
console.log(obj1 instanceof Object);//true

console.log(Object.prototype.toString());//[object Object]

console.log(Object.prototype.toString.call(num1));//[object Number]

console.log(Object.prototype.toString.call([]));//[object Array]

console.log(Object.prototype.toString.call(new Date()));//[object Date]


//因此可以写一个函数来判断
function judgeType(type) {
	return function (obj) {
		return Object.prototype.toString.call(obj) === type;
	}
}
let jt=judgeType("[object Array]");
let res=jt([10,20,30]);
console.log(res);//true

还是基于数组的sort方法,用函数作为返回值的小例子:

function Movie(name,size,time) {
	this.name=name;
	this.size=size;
	this.time=time;
}
let movie1=new Movie("josn.avi","900M","2019-09-19");
let movie2=new Movie("tomson.avi","800M","1999-09-19");
let movie3=new Movie("word2020.avi","400M","2009-09-19");

let arr=[movie1,movie2,movie3];

function getSort(attr) {
	return function (obj1,obj2) {
		if(obj1[attr]>obj2[attr]){
			return 1;
		}else if(obj1[attr] == obj2[attr]){
			return 0;
		}else{
			return -1
		}
	}
}
let sortF=getSort("name");
arr.sort(sortF);
for(let i=0;i<arr.length;i++){
	console.log("电影:"+arr[i].name+" 大小:"+arr[i].size+" 出版时间:"+arr[i].time);
}

判断数据的类型的小例子

var Type = {}; 
for ( var i = 0, type; type = [ 'String', 'Array', 'Number' ][ i++ ]; ){ 
 (function( type ){ 
 Type[ 'is' + type ] = function( obj ){ 
 return Object.prototype.toString.call( obj ) === '[object '+ type +']'; 
 } 
 })( type ) 
};
Type.isArray( [] ); // 输出:true 
Type.isString( "str" ); // 输出:true

单例模式的例子

var getSingle = function ( fn ) { 
 var ret; 
 return function () { 
 return ret || ( ret = fn.apply( this, arguments ) ); 
 }; 
};

(六) 闭包

  1. 闭包是指 有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数
  2. 闭包的作用:缓存数据,延长作用域链
  3. 闭包的优点和缺点:缓存数据
  4. 闭包的模式:函数模式闭包 对象模式闭包

由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,建议只在绝对必要时再考虑使用闭包。虽然像 V8 等优化后的 JavaScript 引擎会尝试回收被闭包占用的内存,但请大家还是要慎重使用闭包.—JavaScript高级程序设计

  • 函数模式闭包-函数中有函数
function f1() {
	let num=110;
	function f2() {
		console.log(num);
	}
	f2();
}
f1();
  • 对象模式闭包-函数中有对象
function f3() {
	let num=20;
	let obj={
		age:num
	}
	console.log(obj.age);
}
f3();
  • 闭包实现数据缓存-延长作用域链的小例子

如下的函数f4中的num应该是局部变量,只能在函数内部可以访问,但是通过闭包的方式,在f4外也能访问这个num的值。f5的同理。

function f4() {
	let num=30;
	return function () {
		console.log("num value:"+num);
		return num;
	}
}
let ff=f4();
let res=ff();
console.log(res);


function f5() {
	let num=50;
	return {
		age:num
	}
}
let obj=f5();
console.log(obj.age);
  • 对比普通函数调用,和闭包的不同
// 普通的函数调用
 function f1() {
 	let num=10;
 	num++;
 	console.log(num);
 	return num;
 }
 console.log(f1());//10
 console.log(f1());//10
 console.log(f1());//10




 function f2() {
 	let num=10;
 	return function () {
 		num++;
 		return num;
 	}
 }

 let f3=f2();
 console.log(f3());//11
 console.log(f3());//12
 console.log(f3());//13
 //上面这个小例子就可以明显看出函数闭包的数据缓存功能

  • 闭包案例-产生相同的随机数 普通函数产生不同的随机数
function showRandom() {
	let num=parseInt(Math.random()*10+1);
	return num;
}
console.log(showRandom());
console.log(showRandom());
console.log(showRandom());


function showRandom1() {
	let num=parseInt(Math.random()*10+1);
	return function () {
		return num;
	}
}
let ff=showRandom1();
console.log(ff());
console.log(ff());
console.log(ff());

  • 闭包实现点赞案例
    在这里插入图片描述
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
	    ul {
	      list-style-type: none;
	    }

	    li {
	      float: left;
	      margin-left: 10px;
	    }

	    img {
	      width: 200px;
	      height: 180px;
	    }

	    input {
	      margin-left: 30%;
	    }
  </style>
</head>
<body>
	<ul>
	  <li><img src="G:\动漫头像\0b02d12a2834349b1a1e5e62cdea15ce37d3beef.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
	  <li><img src="G:\动漫头像\2fdda3cc7cd98d10b915821b263fb80e7aec90d3.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
	  <li><img src="G:\动漫头像\37f7b93eb13533fa6647377ca0d3fd1f40345b9a.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
	  <li><img src="G:\动漫头像\a9f9227f9e2f0708f0c615e7ed24b899ab01f2c1.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
	</ul>
<script>

  //首先获取所有的按钮
  let btnObjs=document.getElementsByTagName("input");

  //函数闭包实现数据缓存
  function getValue() {
  	let value=2;
  	return function () {
  		this.value="赞("+(value++)+")";
  	}
  }

  //遍历按钮注册点击事件
  for(let i=0;i<btnObjs.length;i++){
  	btnObjs[i].onclick=getValue();
  }

</script>
</body>
</html>

(七) 沙箱

  • 沙箱:环境 黑盒 -------- 在一个虚拟的环境中模拟真实世界,做实验,实验结果和真实世界的结果是一样的,但是不会影响真实世界

  • 函数的自调用其实就是沙箱。

  • 函数的自调用方式1

//沙箱--小环境
(function () {
	console.log("我是一个小环境---沙箱");
})();

  • 函数的自调用方式2
//沙箱-小环境
(function () {
	console.log("我也是一个沙箱");
}());
  • 沙箱的小案例
//沙箱小例子 --全局环境中的变量,和沙箱中的变量同名,但不冲突
let num=100;
(function () {
	let num=10;
	console.log(num);//10
}());
console.log(num);//100

(八) 递归

递归:函数中调用函数自己,此时就是递归 递归一定要有结束条件

let i=0;
function f1() {
	i++;
	if(i<5){
		f1();
	}
	console.log("春有百花秋有月,夏有凉风冬有雪。")
}
f1();
  • 递归案例-求n个数字的和
//遍历循环的写法 1=2=3=4=5
let sum=0;
for(let i=1;i<=5;i++){
	sum+=i;
}
console.log(sum);


//递归的写法 --- 反过来想5+4+3+2+1
function sumF(n) {
	if(n==1){
		return 1;
	}
	return n+sumF(n-1);
}
console.log(sumF(5));
  • 递归案例-求一个数字各个位数上的数字的和
//例如123---和为6
function getEverySum(x) {
	if(x<10){
		return x;
	}
	return x%10+getEverySum(parseInt(x/10));
}
console.log(getEverySum(123));
  • 递归案例-斐波那契
//1   2   3   4   5   6
//1   1   2   3   5   8
function fbi(x) {
	if(x==1 || x==2){
		return 1;
	}
	return fbi(x-1)+fbi(x-2); 
}
console.log(fbi(6));
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值