(一) 创建函数的两种方式
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))
(三) 函数里面几个成员介绍
- arguments: 实参 arguments.length 为实参的个数;
- caller: 调用者 --比如在函数f2中调用f1,那么此时f1的调用者就是f2;
- length:形参的个数;
- name:函数名,不可修改;
- prototype: 原型对象;
- 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 ) );
};
};
(六) 闭包
- 闭包是指 有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
- 闭包的作用:缓存数据,延长作用域链
- 闭包的优点和缺点:缓存数据
- 闭包的模式:函数模式闭包 对象模式闭包
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,建议只在绝对必要时再考虑使用闭包。虽然像 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));