函数
函数
函数好处:
1:方便代码的复用。
2:方便代码的维护。
函数的定义的语法:
function 函数名(参数列表){ 函数体 }
解释:
1:函数,也称为方法。
2:function:js 的关键字。用来定义函数的。
3:函数名:是一种标识符,命名规范和变量的规范一致,首字符小写,多个单词构成,从第二个单词的首字符大写。一定要可以通过函数名体现出要实现的功能。
4:(参数列表):小括号必不可少,可以为空。可以称为形参列表,简称参数、或者是形参。
参数代表了,函数体功能中变化的量,有几个变化的量,就要写几个参数,使用逗号分隔。
语法:(参数1,参数2,参数3…)
上面的参数1,参数2,这些参数的命名都要符合变量的命名,本质上就是变量。
5: { 函数体 }:大括号中是函数的主体实现部分。
函数的使用:
通过 函数名(实参列表); 调用函数。
<script>
//自定义方式,实现打印任意两个数的和。
function logSum(num1,num2) {
var sum = num1 + num2;
console.log (`${num1}+${num2}=${sum}`);
}
logSum(1,2);
</script>
函数的分类:
1:定义的源
a: js的内置函数 isNaN
b:自定义函数 开发者自定义函数
2:是否有参数
a:有参函数
b:无参函数
3:是否有返回值
a:有返回值函数 Math.random();
b: 无返回值,返回值为undefined。
函数的参数说明:
定义函数的参数部分:形参列表。
形参列表:
1:参数的存在可以更好的扩展函数的功能。
2:形参代表了函数这个功能体需要的外部提供的数据列表。
3: 形参的个数没有限制,个数取决于希望外部提供几个数据。就需要
定义几个参数来接收外部提供的数据。
4:形参的名字和变量的命名方式一样,希望接收外部什么样的数据就起什么样的名字。
5:参数之间使用逗号分开。最后一个参数后不需要添加逗号。
调用函数的时候使用的参数:实参列表。
实参列表:
1:实际参与函数内部运算的数据的列表。
2:通常情况下,实参的个数和形参的个数应该是一致的。其他语言中必须一致。
3:在js中实参的个数可以和形参的个数不一致。不建议。
4:实参的个数多于形参的个数,实参多出来的部分直接被忽略。没有形参接收这些数据。
5:实参个数少于形参的个数,那么多余的形参的值为undefined。
<script>
//自定义方式,实现打印任意两个数的和。
function logSum(num1,num2) {//num1,num2是形参列表。
console.log (num1);
console.log (num2);
var sum = num1 + num2;
console.log (`${num1}+${num2}=${sum}`);
}
logSum(1,2);//传参的过程
logSum(1);
</script>
函数的定义:
function 函数名(参数列表){函数体}
参数:形式参数(形参);实际参数:实参
形式参数: 函数中形式上参与运算的数据。 只是一个占位符。
1:函数定义的时候的小括号中的内容:形参列表。
2:形参的作用,用来接收方法的使用者传递的实际的参数。
3:形参的个数没有限制,也不需要添加var 关键字。直接写参数的名字即可。
形参的名字和变量的名字的规范一致。形参就是局部变量。只能在方法内使用。
多个形参之间使用逗号分隔。
4:形参的名字要能体现出要接收的实参的数据的内容。
实际参数:函数中实际参与运算的数据。
1:实参的个数通常要和形参的个数一致。
2:实参的个数如果少于形参的个数。多余的形参的值为 undefined。
3:函数调用的时候,是将实参的值按照顺序依次赋值给形式参数。
4:实参可以是任意的有返回值的js的表达式。
5: 实参之间使用逗号分隔。
函数调用的传参问题
函数调用的时候。
会进行参数的传递:将实参的值依次赋值给形式参数的过程。
<script>
var num1 = 10;
var num2 = 20;
/* var temp = num1;
num1 = num2;
num2 = temp;*/
change (num1, num2);
console.log (`num1 = ${num1}\tnum2 = ${num2}`);
function change(val1, val2) {
console.log (`val1 = ${val1}\tval2 = ${val2}`);
var temp = val1;
val1 = val2;
val2 = temp;
console.log (`val1 = ${val1}\tval2 = ${val2}`);
}
</script>
程序中的内存说明
js中的数据占用的内存,主要分为了2部分。
1:栈内存 stack
全局变量、函数内的局部变量。
栈是一种数据结构。
数据结构:具有某种关系的一组数据的集合。类似一个容器,容器中保存了
很多的数据,数据之间存在某种关系。不同的数据结构中的数据的关系是不同的。
不同的关系,导致了访问数据结构中的数据的特点不同。
学习数据结构,要了解数据结构中的数据的关系和特点。
2:堆内存 heap
对象
函数的返回值
函数的分类:
按照返回值:有返回值的函数、无返回值的函数。
1:如果希望函数执行完毕之后,函数自身会带有一个结果,那么该函数就需要有返回值。
2:如果希望函数执行完毕之后,不要一个结果,只是实现了某些功能,那么就不需要有返回值。
没有返回值的函数有一个默认的返回值 undefined。
如何让一个函数执行完毕之后有一个结果?
要通过一个js关键字实现:return;
如果我们希望一个函数执行完毕之后返回一个结果,那么在函数体内,必须使用关键字return 后面
跟你需要返回的结果的表达式。
格式:return 结果;
return 的使用方式:
1:在有返回值的函数内,使用return 返回需要返回的值。 return 值;
2: 在不需要返回值的函数内使用,直接使用 return; 用来结束函数的执行的。
3:return 只能在函数中使用,别管后面是否有值,那么一旦使用了return,那么函数立即结束执行。
一般称为 【函数返回】。
<script>
//需求,求任意两个数的最大值。 这个需求的函数应该要有返回值,返回的是两个实参中比较大的那个
function max(num1, num2) {
var max = num1 > num2 ? num1 : num2;
return max;
}
/**
* 检查 指定的数是质数还是合数,打印结果
* @param num 要检查的数
* 如果不是合法的数据,返回 -1,如果是质数返回0,如果是合数返回1.
*/
function checkNumber(num) {
//特殊情况
var str = typeof num;
if (str !== "number") {//不是数值类型
return -1;
} else if (num <= 1) {
return -1;
} else {//大于1 的数。
//小数的情况
var temp = ~~num;
if (temp != num) {//小数
return -1;
} else {
//一般情况
var flag = false;
for (var i = 2; i < num; i++) {
if (num % i === 0) {//合数
flag = true;
break;
}
}
if (flag)
return 1;
else
return 0;
}
}
}
console.log (max (1, 2));
console.log (checkNumber (null));
console.log (checkNumber (1));
console.log (checkNumber (3));
console.log (checkNumber (4));
//求任意数的绝对值
function abs(num) {
if (num < 0)
return -num;
return num;
}
function abs(num) {
return num < 0 ? -num : num;
}
console.log (abs (-1));
console.log (abs (2));
//
/**
* 判断一个数是否是质数
* @param num 被判断的数
* 如果num 值质数,返回true,否则false。
*/
function isPrime(num) {
//特殊情况
var str = typeof num;
if (str !== "number") {//不是数值类型
return false;
}
if (num <= 1) {
return false;
}
//大于1 的数。
//小数的情况
var temp = ~~num;
if (temp != num) {//小数
return false;
}
//一般情况
var flag = false;
for (var i = 2; i < num; i++) {
if (num % i === 0) {//合数
return false;
}
}
return true;
}
console.log (isPrime (null));//false
console.log (isPrime (0));//false
console.log (isPrime (2));//true
console.log (isPrime (11));//true
console.log (isPrime (12));//false
//判断某个一数知否是 true
function isTrue(val) {
return val === true;
}
//实现求四舍五入的函数
function round(num) {
//正数情况
var val = num - ~~(num);
if (val >= 0.5) {
return ~~(num + 1);
}
return ~~num;
}
console.log (round (1.1));//1
console.log (round (1.5));//2
console.log (round (1.51));//2
console.log (round (1.9));//2
</script>
函数的使用
有返回值的函数的使用:
1:直接调用。(很少)
2: 用一个变量接收方法的返回值。(最多的)
3: 作为操作数使用。(不多)
4: 作为函数调用的实参使用。(不多)
5: 作为log函数的实参直接打印。(现阶段比较多)
无返回值的函数的使用:
1:直接调用。
<script>
function random(min, max) {
return ~~(Math.random () * (max - min) + min);
}
//1
random (1, 100);
//2
var num = random (1, 299);
//3
num = 1 + random (1, 20);
//4
num = random (random (1, 20), random (30, 80));
//5
console.log (random(1,20));
//1: 无参直接调用。
function log(msg) {
console.log (msg);
}
log(true);
</script>
js中不存在函数重载
函数的重载:overload。
1:概念:方法名字相同,形参列表不同的函数,称为函数的重载。
在js中不存在重载这个问题。如果多个方法的名字相同,那么后定义的会覆盖前面的
<script>
//会被覆盖掉
function add(num1, num2) {
return num1 + num2;
}
//这个会覆盖掉上面的
function add(num1, num2, num3) {
return num1 + num2 + num3;
}
console.log (add(1,2));//NaN
console.log (add(1,2,3));//6
</script>
函数的定义的几种方式
js中关于函数定义的几种方式:
1:方法声明方式 使用最多的。
function 方法名(参数列表){方法体}
2:方法的直接赋值方式 使用一般多。
var 变量名 = function(参数列表){方法体}
匿名方法定义的形式。
3: 使用构造函数。 基本不用
var 变量名 = new Function("参数列表","方法体");
所有的function 实例。函数对象都是通过 构造函数 Function 创建出来的。
三种定义方式的区别:
1:第一种方式,可以在定义之前和之后调用该方法。而且方法是有名字的。
2: 第二种方式,是匿名的函数,是将一个匿名函数赋值给了一个变量。还有该方式,必须先
定义后使用。
3:第三种方式,很繁琐,必须先定义后使用。
<script>
//
test();
//函数定义的直接声明方式
function test() {
console.log ("helloworld!");
}
test();
// sum 是一个对象变量,通过变量名来访问。
sum(1,2);
var sum = function (num1,num2) {
return num1 + num2;
}
console.log (sum(1,2));
var value = 10;
// 使用构造函数
var log = new Function("","{alert('helloworld')}");
log();
</script>
局部变量和全局变量
变量的作用域:
变量可以被访问的范围。
变量的分类
1:全局变量
在方法外定义的,script标签内定义的变量。就是全局变量。
2: 局部变量
在方法内定义的变量就是局部变量。
局部变量和全局变量的不同
1:作用域不同。
a:全局变量的作用域是整个script代码块。包括里面的方法体中.
b:局部变量的作用域就是其所在的方法范围内。
2: 生命周期不同。
a:局部变量的生命周期从方法开始调用开始,从方法结束调用结束。
依赖于所在的方法何时被调用。
b:全局变量的生命周期从script代码块开始加载就开始了。关闭浏览器的时候结束。
3:如果全局变量和局部变量的作用域存在冲突:被访问的优先级不同。
a:局部的高于全局的。
b:如果在冲突的作用域中访问全局的变量,可以通过 window.变量 或者是 this.变量 访问
总结:
1:全局的变量会一直占用内存空间。
2:js中可以重复定义变量,可能会出现多次定义一个全局变量。在函数内没有使用var 定义变量污染了全局数据。
<script>
//全局变量
var num = 0;
var val = 10;
var number = 100;
function fn() {
//局部变量 和 全局变量存在命名的冲突。
var num = 10;
console.log ("局部:"+num);//10
//访问全局变量
console.log (window.num);//0
console.log (this.num);//0
var value = 10;
//访问全局变量
val = 100;
number = 100;
}
fn();
var val = 200;
function a() {d();}
function c() {}
function d() { c();}
function test() {
a();
}
</script>
let和var的区别
let 关键字:用来定义变量的。let的后期版本推出的新的关键字。
var 关键字:用来定义变量的。
不同点:
1-1:let 定义的变量具有块级作用域。在代码块定义的变量只能在代码块中使用。出了代码块就不能被访问了。
1-2:var 定义的变量不具有块级作用域。在代码块中定义的变量,就是全局的变量。
2-1:使用var声明的全局变量会作为window的属性存在。可以通过 window. 访问全局变量。
2-2: 使用let 定义的全局变量不会作为window的属性存在。
3-1: var 可以重复定义同名变量,后面会覆盖前面的。
3-2: let 不可以重复定义同名变量。
总结:var 定义变量不够严谨,let定义变量更加的严谨。
<script>
let value = 10;
// let value = 10;
var num = 10;
var num = 100;
if (true) {
//把外面定义的变量污染了。
var num = 100;
//不会污染全局的value。
let value = 20;
}
console.log (num);//100
console.log (value);//10
function test() {
console.log ("hello");
}
console.log (window.num);//100
window.test ();//hello
console.log (window.value);//undefined
</script>
方法的执行符
方法的执行符:()
调用方法,必须使用方法名+()
执行符的作用:定义匿名函数,并直接执行。
<script>
function test() {
console.log ("hello");
}
//直接打印方法的名字,输出的是方法的完整的定义的形式。
/*
ƒ test() {
console.log ("hello");
}
*/
console.log (test);
//调用方法
test();
var fn = function () {
console.log ("world");
}
/*
* ƒ () {
console.log ("world");
}
* */
console.log (fn);
fn();
//定义匿名函数,并执行该函数,不要有全局方法名字。
(function () {
console.log ("world11111");
})();
//使用函数作为实参,在方法内执行实参函数。
function fn1(fn) {
fn();
}
console.log ("------------");
fn1(test);
fn1(function () {
console.log ("我是匿名函数")
});
</script>
传参示意图
栈
函数的执行符
函数的执行符:()
对象引用
立即自执行匿名函数
函数作为实参使用
<script>
//第二种定义函数的方式
//log 对象变量、对象引用。指向了堆内存中的一个匿名函数。
var log = function (msg) {
console.log (msg);
}
function show(){
console.log ("可是你却并不在意");
}
//如何让匿名函数执行。通过对象引用+执行符。来执行匿名函数。
log ("千万里我追寻着你");
//调用其他方法的方法 funName 方法的名字,或者是方法的对象引用。
function invokeFunction(funName) {
funName();
}
invokeFunction(show);
invokeFunction(log);
//立即自执行匿名函数.就是希望函数定义之后立即执行。而且我只需要执行一次。
(function (msg) {
console.log (msg);
})("你不像是在我梦里,在梦里你是我的唯一!");
~function test() {
console.log ("helloworld!")
}();
</script>
函数的属性和方法介绍
函数是一种引用数据类型。
是我们接触到的第一种引用数据类型。
函数是一种特殊的对象,对象就可以具有属性和功能(函数、方法)。
函数的属性:
name:函数的名字
length: 代表了函数的形参的个数。是个整数.
函数的方法:
toString(): 得到函数的字符串表示形式。
<script>
function show() {
console.log ("我今生注定要独行,热情以被你耗尽!");
}
console.log (typeof show);//function
var log = function(){
console.log ("我已经变的不再是我,而你却依然是你!");
}
console.log (show.name);//show
console.log (log.name);//log
//调用其他方法的方法。
function invokeFunction(fnName) {
if(fnName.name = = = "show"){
console.log ("*************");
}else if(fnName.name === "log"){
console.log ("-----------");
}
}
invokeFunction(log);
invokeFunction(show);
console.log (invokeFunction.length);//1
console.log (show.length);
/* ƒ (){
console.log ("我已经变的不再是我,而你却依然是你!");
}*/
console.log (log);
/*function(){
console.log ("我已经变的不再是我,而你却依然是你!");
}*/
console.log (log.toString());
</script>
函数的arguments对象
函数的arguments对象
1:arguments 参数的意思。
2:arguments 是一个对象,是函数的一个对象,只能在函数体中使用。
3: arguments 是一个集合。类数组对象。可以像访问数组一样去访问它。通过下标。
下标从0开始。 arguments[下标] 访问。
4:有一个length属性。代表了元素的个数。
5:arguments 还有一个属性是一个对象。callee。是当前函数自身。主要用于递归调用。
6:arguments 对象中的元素是实参的内容。可以通过该对象来访问传入的实参的内容。
7:通常情况下都是使用方法的形参来接收实参的,不建议使用arguments来访问实参。
8:如果传入的实参的个数不确定。可以使用arguments来访问传入的实参。
补充:arugments不太建议使用。因为在严格模式下,arguments无效。
arguments 在一定程度上弥补了js不存在重载的问题。
<script>
function show(msg) {
console.log (msg);
// ["time time again,you ask me", callee: ƒ, Symbol(Symbol.iterator): ƒ]
console.log (arguments);
console.log (arguments.callee);
}
show ("time time again,you ask me");
//实现任意个数的累加和的方法。就可以使用arguments来处理。
function add(num1, num2) {
return num1 + num2;
}
function add(num1, num2, num3) {
return num1 + num2 + num3;
}
function add() {
//在方法的内部使用arguments来访问所有的实参。
const LEN = arguments.length;
if (LEN === 0)
return NaN;
//遍历arguments中的所有的内容。累加返回
var sum = 0;
for (let i = 0; i < LEN; i++) {
sum += arguments[i];
}
return sum;
}
console.log (add());//NaN
console.log (add(1,2,3,4,5));//15
console.log (add(1,2,2,1,1,2,3,4,4));//20
</script>
递归
递归:recursion 指的就是函数调用。
概念:函数直接的或者是间接的调用了自身。
使用递归的解决的问题特点:
1:问题可以分解为若干个子问题。
2:子问题的解决的方案和问题本身的解决方案一致。
3:最终一定存在某一个子问题可以直接解决。
4:最终问题的解决要依赖于所有的子问题的解决。
求 n! n的阶乘。
n! == n*(n-1)(n-2)…321;
n!= n(n-1)!
5! = 54321;
5! = 54!;
4! = 43!
3! = 32!
2! = 21!
1! = 1;
<script>
function test(n) {
//直接递归调用
// test(n-1);
//间接递归调用。
test2 ();
}
function test2() {
test (1);
}
// Maximum call stack size exceeded
// test(1);
//使用递归求n的阶乘
/**
* 求n的阶乘。返回n的阶乘的结果。
* @param n
*/
function factorial(n) {
let result = 1;
if (n === 1)
return result;
result = n * factorial (n - 1);
return result;
}
console.log (factorial (5));//120
console.log (factorial (6));//720
//斐波那切数列,第一个和第二个数为1,从第三个数开始,是它前两个数之和。
//求第n个位置上的数的值。
function fibo(n) {
if (n === 1 || n === 2)
return 1;
return fibo (n - 1) + fibo (n - 2);
}
// overflow
for (let i = 1; i < 10; i++) {
document.write (fibo (i) + ",");
}
</script>