typora-root-url: assets
文章目录
函数
在某些语言里,函数也叫方法
方法需要调用后才能执行:
定义函数的方法:
-
通过关键字function :
注意方法后面要加括号
function a(a,b,c) { console.log("a"); console.log("b"); console.log("c"); } //function是关键字 //a是函数名(方法名) //a,b,c是参数 // { }里面则未代码体
函数定义的区别
-
通过function定义的方法无论放再前面还是后面,代码执行到调用语句的时候就会去寻找function方法后执行
-
通过函数表达式定义的方法,必须把定义的方法放在调用函数前面:
sayHello(); sayHello1(); var sayHello =function (){ console.log ("hello world"); } function sayHello1(){ console.log("Hello world"); } //上面的代码会提示方法sayHello未定义而执行报错
函数的代码体会形成作用域:
虽然用var定义的变量没有作用域,但是定义在方法的{ }里面是会右作用域的。所以如果在function的{ }里面用var定义变量就会有作用域。
function a(){
var name="洪志国";
console.log(name);
}
//不会报错
function a(){
var name="洪志国";
}
console.log(name);
//显示未定义而报错
从上面的代码可以知道,function里的变量的作用域就在function{ }内,在代码体内可以随意的调用变量,出了function{ },变量就不能再被调用。
如果要使用后面就要使用关键字return
在function的外面定义的变量,function{ }也可以随意调用;
定义在function内部的变量叫局部变量。
在外面定义的变量叫全局变量。
方法的调用:
方法只能通过调用的方法去执行;
-
常规调用:使用方法名去调用:
方法名不包含括号
//方法名+(); //定义了一个无参函数(方法) function sayHello1(){ console.log("Hello world"); } //调用方法 sayHello1();
-
立即执行函数
在定义好的方法前面加一个**+或者!,在方法后面加一个();**,这样的话代码就会立即执行一次。并且不影响后面的调用。
+function sayHello1(){ console.log("Hello world"); }();
3. **函数表达式执行**:在定义好的function后面加一个`( );`就会 立即执行一遍。
```javascript
var sayHello1=function(){
console.log("Hello world");
}();
小结:
以上方法调用都带
( )
,后面会有不需要括号就可以调用的方法。
函数的使用方法:
function xinxi (name,sex,age){
console.log("姓名:"+name);
console.log("性别:"+sex);
console.log("年龄:"+age);
}
xinxi("张三","男",156);
上面的代码是在定义方法的时候给三个参数,然后在调用方法的时候给一个传参,实现方法内变量值的改变。
var name="张三";
var sex="男";
var age=12;
function data (){
console.log("姓名:"+name);
console.log("性别:"+sex);
console.log("年龄:"+age);
}
data();
name="李四";
sex="女";
age=15;
data();
上面的代码是通过在function外面定义全局变量,然后在调用方法前采用修改变量值的方法去修改变量值。
定义参数:
function 方法名(参数1,参数2......){
//代码体
}
方法名(参数值1,参数值2......);
-
在定义参数的时候注意不要写
var
,中间用逗号隔开;- 用传参的方法时候,在调用函数时的参数必须按照function定义的参数的个数去进行传参,传参顺序从左到右,
- 可以只传前几个,那么后面的参数都是undefined,
- 但是不能跳过某个或某几个参数去传参,这样会报错,但是可以传递空值(“ ”、null、undefined)。
- 当我们直接去调用不给参数值的时候,因为参数未赋值,所以都是undefined;
- 实参个数多于形参个数不会报错,按顺序将实参传递给实参,多余的实参也接收到了存在了arguments内,但是没有一个变量来将它打印出来。
function xinxi (name,sex,age){ console.log("姓名:"+name); console.log("性别:"+sex); console.log("年龄:"+age); } xinxi("张三","男",12); //正常输出
function xinxi (name,sex,age){ console.log("姓名:"+name); console.log("性别:"+sex); console.log("年龄:"+age); } xinxi("张三"); //输出: //姓名:张三 //性别:undefined //年龄:undefined
function xinxi (name,sex,age){ console.log("姓名:"+name); console.log("性别:"+sex); console.log("年龄:"+age); } xinxi("张三",,12); //报错
function xinxi (name,sex,age){ console.log("姓名:"+name); console.log("性别:"+sex); console.log("年龄:"+age); } var name="张三",sex="男",age="12"; xinxi(name,sex,age); /* 再调用方法的时候值会赋值给参数,这个赋值过程叫做传参。 */
function xinxi (name,sex,age){ console.log("姓名:"+name); console.log("性别:"+sex); console.log("年龄:"+age); } var name="张三",sex="男",age="12",add="wuhan"; xinxi(name,sex,age,add); /* 显示: 姓名:张三 性别:男 年龄:12 */
只要是形参未被赋值,就是undefined,所有的实参都会被argument所接收,只是没有形参对应这样的形参可使用,如需可向arguments索取(argument【n】)。
形式参数(形参):
1. 指在方法定义的过程中使用的默认没有值(undefined)的参数
2. 作用域为方法内,随着方法的产生而产生,随着方法的消失而消失。
实际参数(实参):
1. 指的是方法调用的过程中,里面的真实参数;
值是由实参传向形参
arguments参数集合
在所有的function里面,一个隐藏内置的对象,是当前方法所接受的参数集合。仅仅作用在方法内部,出了方法就不能生效
arguments是个类数组。
- 类数组:具备数组特性,通过索引取值,通过length确定长度。不具备数组方法(比如push,pop等),的集合,Array is Array()得到的是false
arguments内部还有有一个属性callee:
arguments.callee本意是指当前方法的集合。
✋实现多个数的相加:
/*
题中未说多少个数相加因此需要使用内部的arguments来实现
它是个类数组,有length属性
*/
function add(){
var sum=0;
for (var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
console.log(sum);
}
var a=10,b=3,c=1;
add(a,b,c);
**总结:**在ECMAScript函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,因为在function中接收实参的一直都是类数组arguments,从而获得传递给函数的每一个参数。
方法的重载:
方法名相同,但是方法的参数类型或个数不同,这时候同名的方法都会存在,这个情况,叫方法的重载。
js没有重载
function bb(a,b,c){
console.log(a+b+c);
}
function bb(a,b){
console.log(a+b);
}
bb(1,4,6);
//显示结果为5
从上面的代码可以看出来,js中没有重载,而是以后出现的function为准
:happy:因为js无法固定数据类型,也不能确定实参的个数,而其他语言的重载是根据数据个数和数据类型来实现的,所以js没有方法重载。
方法的返回值(return):
所有的方法都有返回值吗,return可以在任何地方,返回任意值
-
不需要返回值的function:return的值为默认的undefined;
-
需要返回值得function:
-
return可以返回一个值:
function a(a,b){ a=10; b=10; a+=b; return (a); } var b=a(); console.log(b); /* 得到的结果为20,即返回的是最后a的值。 */
-
return返回一个表达式:
function a(a,b){ a=10; b=10; return (a+b); } var b=a(); console.log(b); /* 最后输出结果为20,即接收到a+b的值。 */
-
return返回多个值:
function test(b,a){ b=5; a=6; return (a,b); } var c=test(); console.log(c); /* 以return的最后的值为准 最后结果为5,即b的值 */
-
return返回一个具体值:
function a(b,c){ b=20; c=11; return 5; } var b=a(); console.log(b); /* 最后结果为5,即return返回的5 */
-
-
-
方法返回值的用处:
-
结束方法。
function aa(){ console.log("1"); console.log("2"); return false; //return ; //如果只是为了结束方法,不给返回值。就直接给一个return即可 console.log("3"); } aa(); /* 输出结果为: 1 2 */
当一个方法遇到return后就结束方法,如果只是为了结束方法,不给返回值。就直接给一个return即可,需要返回值加返回值。
-
更容易的实现模块化(低耦合);
方法的调用者与方法的引用:
实现某个方法只能被另一个方法所调用:
function a(){
console.log("a");
}
function b(){
console.log("b")
}
function c(){
console.log("c");
}
-
通过方法的作用域来实现:
function b(){ console.log("b") function a(){ console.log("a"); } } function c(){ console.log("c"); } /* 将方法a定义到方法b中去 这种方法无法在外部直接调用,只有在function b(){ }内可以调用 */
-
通过内部属性caller来判断
-
与arguments相同,caller也是方法内置的隐藏属性,通过caller可以判断出是谁调用了这个方法,如果是全局调用,那么caller的值为null。
function a(){ console.log("a"); console.log(a.caller); } function b(){ console.log("b") console.log(b.caller); a(); } function c(){ console.log("c") console.log(c.caller); } b(); /* 这样的话 */
-
这样的话function a (){ }的caller会显示当前调用的方法的名字。
那么实现只允许某方法调用:
function a(){
if (a.caller!==b){
return ;
}
console.log("a");
console.log(a.caller);
}
function b(){
console.log("b")
console.log(b.caller);
a();
}
function c(){
console.log("c")
console.log(c.caller);
}
b();
通过在function a(){ }里用if去判断是不是方法b来调用的方法a。
方法的引用:
递归:
方法在自身里又调用自己
匿名函数:
匿名函数就可以避免自己的变量暴露在全局且可以隐藏掉自己的function名,避免后期在项目中,多人操作自己的方法(因为js
没有重载,后期存在被覆盖的风险) 。
简单的使用了回调函数
(function (){
//代码体
})();
+function (){
//代码体
}();
匿名函数的参数:
(function (a,b){
console.log(a+b);
})(1,3);
/*
后面的括号的1,3分别对应a,b
*/
+function (a,b){
console.log(a+b);
}(1,3);
回调函数:
把方法当成参数,传递到另一个方法里面,就是回调函数(回调方法);
本来正常情况下的变量的
上面的情况是回调函数的另一种应用,控制权转移(控制反转)反转变量。上面的红色框内,的控制权还是function buy(){ },但是到了紫色的框内,他的控制权就移交给别人。
从上面可以看出方法hong等于后面的代码块,因此,我们可以将函数的调用这样写:
利用匿名函数来实现回调
利用回调函数来遍历多维数组:
var arr = [
["张三", "李四", "王五", "赵六", ["杨标", "周洋"]],
[18, 19, 29], true,
"hello", ["创创", "万成", ["李昊", "高自忠"]]
];
function print(infos) {
var count = 0;
for (var i = 0; i < infos.length; i++) {
if (Array.isArray(infos[i])) {
count = count + print(infos[i]);
} else {
console.log(infos[i])
count++;
}
}
return count;
}
var a = print(arr);
在上面的代码中,在print(inos[i])
中使用回调函数,遍历数组,当进入到print
方法调用的时候,也会有一个count值,然后内层的count的值会return到上面一层的count、,然后通过count=count+print(infos[i]);来计算最后的count的值。最外层的count的值利用return来返回。
迭代方法:
可以理解为数组的遍历方法。
数组的迭代:
forEach方法:
数组可以使用第三方的方法来遍历里面的元素(for或for…in),也可以使用自己带的方法来进行遍历。
var arr=[1,4,7,9,2,8,3,6,11,54,55]
for (i in arr){
console.log(i);
//数组的索引值( 或者对象名)
console.log(arr[i]);
//数组的值
}
for```in是循环语句中,一种数组的正向遍历。
-
forEach方法:
一般用于数组长度不变的情况下去拿出数组内的元素。
该方法要做的事:
- 接收两个值,但是第二个值是可选的,所以我们必须要给forEach一个参数。
- 第一个参数是要在每一项上运行的函数(方法),将一个方法传到forEach中去
- 第二个参数是this,后面再去了解。
1. 做一个循环,从第一个开始(索引为0),到length-1结束
2. 每次循环的时候,拿出三个值给你:
第一个是当前我便利出来的元素。
第二个是我当前遍历的索引
第三个代表当前正在遍历的数组
3.当
forEach
将上面的三个值交出后,就不管其他的东西了
var arr=["a","b","c","d","e"];
// faor (var i in arr){
// console.log(arr[i]);
// }
// for (var i=0;i<arr.length;i++){
// console.log(arr[i]);
// }
// 用数组自带的方法forEach遍历
function m(item,index,a){
console.log (item,index);
console.log(a);
}
arr.forEach(m);
-
forEach是一个正向遍历的过程
-
不能使用break和continue(因为forEach控制循环部分系统无法用break去控制;
function m(item,index,a){ console.log (item,index); if (item=="c"){ break; } console.log(a); } arr.forEach(m); /* 这样的话会报语法错误 */
var count=0; function m(item,index,a){ count++; console.log (item,index); console.log(a); } arr.forEach(m); console.log(count); /* 在上面的代码中,count计数器的值为5,也就是说,forEach也是一个回调方法,每遍历数组的一个值,就会向方法传递三个值,直到数组遍历结束 */
function m(item,index,a){ if (item=="c"){ return ; } console.log (item,index); console.log(a); } arr.forEach(m); /* 因为是回调方法,所以碰到return就会结束当前方法,但是如仍然会继续下一次的回调方法。 */
如果想要逆向遍历就先对原数组revers反转后再遍历
结束forEach:
-
function m(item,index,a){ console.log (item,index); console.log(a); a.length=0; } arr.forEach(m); /* 将数组的长度改为0,那么forEach就会结束 */
-
function m(item,index,a){ if (index>=1){ return ; } console.log (item,index); console.log(a); } arr.forEach(m); /* 利用if控制function本身,让它每次都return结束方法; */
停止forEach且不会改变原数组
var arr=["a","b","c","d","e"]; arr.concat().forEach(function(item,index,a){ if (item=="c"){ a.length=0; } else{ console.log(item,index,a); } })
forEach
从一开始就确定他的遍历的次数,这个次数只能小于或等于它,不能大于它。var arr=["a","b","c","d","e"]; var count=0; arr.concat().forEach(function(item,index,a){ if (item=="c"){ a.push("f"); } else{ console.log(item,index,a); count++; } }) console.log(count);
map方法:
-
与forEach的遍历方式相同:也是接收一个方法的回调,回调方法也有三个参数。
var arr=[1,2,3,4,5,6]; //把上面的数除以二后存在新数组里 var newArray=[]; for (var i in arr){ newArray.push(arr[i]/2); } console.log (newArray); /* 用以前的方法 */
var newArray=[]; arr.forEach(function(item,index,a){ newArray.push(item/2); }) /* 用forEach */
map方法是可以接收回调方法通过return返回的值,并将每次返回的值放再一个新的数组里。
var newArr = arr.map(function (item, index, a) { return(item/2); }) /* map可以直接接收return的值,所以直接定义一个新数组来接收最后的值, */
var arr = ["a","b","c","a","a","b","d","e","f","c","g","d","e","g","a","c","a","h","a"]; //把上面的数除以二后存在新数组里 var newArr = arr.map(function (item,index,a){ if (a.indexOf(item)==index){ return item; } })
在上面的代码可以看到,去重之后,因为重复部分没有返回值,所以默认返回undefined。但是undefined不是我们想要的,因此我们引出filte方法:
filter方法:
var newArr = arr.filter(function (item,index,a){
if (a.indexOf(item)==index){
return item;
}
})
/*
*/
filter与map相同,但不同的是,filter会接收过滤后的结果。
比如代码中if{ }。
var arr = [1, 5, 7, 9, 0, 2, 3, 4];
//输出数组内的偶数
var newArr = arr.filter(function (item, index, a) {
if (item % 2 == 0) {
return item;
}
})
console.log(newArr);
//返回了一个明确的值
)
var arr = [1, 5, 7, 9, 0, 2, 3, 4];
//输出数组内的偶数
var newArr = arr.filter(function (item, index, a) {
return item%2==0;
})
console.log(newArr);
//返回了一个过滤条件
var arr = [1, 5, 7, 9, 0, 2, 3, 4];
//输出数组内的偶数
var newArr = arr.filter(function (item, index, a) {
return 1;
})
console.log(newArr);
**总结:**filter在有经过判断条件之后return的就是返回结果,没经过判断条件的return就是返回过滤条件(通过boolean转化)。
filter接收不了通过Boolean转化为false的值(0,false,undefined,NaN," ",null);
some方法:
这个遍历方法也有一个返回值,返回的是布尔类型。
var arr = [1, 5, 7, 9, 0, 2, 3, 4];
//判断数组中有没有一个元素大于4
var flag=false;
for (i=0;i<arr.length;i++){
if (arr[i]>4){
flag=true;
break;
}
}
var arr = [1, 5, 7, 9, 0, 2, 3, 4];
//判断数组中有没有一个元素大于4
var flag=arr.some(function(item,index,a){
return item>4;
});
some的运行方法为或(||)运算,一真即真,运行方法跟for循环相同,当得到true后,后面的代码将不运行。
every方法:
var arr = [1, 5, 7, 9, 0, 2, 3, 4];
//判断数组是不是所有元素都小于3
var flag=true;
for (i=0;i<arr.length;i++){
if (arr[i]>3){
flag=false
break;
}
}
var arr = [1, 5, 7, 9, 0, 2, 3, 4];
//判断数组是不是所有元素都小于3
var flag=arr.every(function(item,index,a){
return item<3;
});
every方法是执行且运算(&&),一假即假,得到flase后,后面也不执行。
归并方法:
也是迭代方法的一种,但不完全相同,他的回调方法有四个参数
var arr=["a","b","c","d","e"];
arr.reduce(function (prev,current,index,a){
console.log(prev,current,index);
});
var arr=["a","b","c","d","e"];
arr.reduce(function (prev,current,index,a){
console.log(prev,current,index);
return 1;
});
prev
当第一次遍历的时候,他代表第一个元素,current是第二个元素,之后代表上一次回调方法返回的值
current
代表当前的元素值
index
当前的索引
a
当前的数组
应用
var arr=[1,,8,15,,3,5,4,8,1,4,4]
var result= arr.reduce(function (prev,current,index,a){
return prev>current?prev:current;
});
/*
prev代表上次回调返回的值,所以,可以像上面那样去return最大值
*/
var arr=[1,,8,15,,3,5,4,8,1,4,4]
var result= arr.reduce(function (prev,current,index,a){
return prev+current;
});
/*
求和
*/
排序方法(sort):
sort方法默认升序排列,如果要进行其他方法的排序需要传递一个比较函数给它,它会调用数组的tostring方法进行排序,最后得到是是一个number类型的数组,因此在数组里都是数字的时候推荐使用,其他时候不推荐使用。
var arr = [1, 5, 7, 9, 0, 3, 2, 4, 8];
arr.sort();
//[0,1,2,3,4,5,6,7,8,9]
var arr = [1, 5, 7, 9, 0, 3, 2, 4, 8];
//我现在希望实现一个倒序
arr.sort(function (a,b) {
if(a>b){
return -1;
}
else if(a<b){
return 1;
}
else {
return 0;
}
});
// [9, 8, 7, 5, 4, 3, 2, 1, 0]
var arr = [1, 5, 7, 9, 0, 3, 2, 4, 8];
arr.sort();
arr.reverse();
/*
[9,8,7,6,5,4,3,2,1,0]
因为sort默认是升序排列,所以降序排列可以用sort后再使用reverse方法反向
*/
排序方法sort可以接受一个函数作为参数,这个函数接收两个参数,如果第一个参数应该位于第二个参数之前,返回一个负数;如果第一个参数位于第二个参数之后,则返回一个正数;如果相等,则返回0.