函数初识
函数的5种声明方式
具名函数
function f(x,y){
return x+y
}
f.name // 'f'
- 具名函数的函数名也是变量名
- 函数里面如果只写return和return undefined是一个意思,另外函数返回0、 ' '、 NaN 、undefined、 null都等同于返回false
-
function x(val1,val2){return}
等同于function x(val1,val2){return undfined}
匿名函数
- 不能单独使用,必须先声明一个变量,把他赋值给变量
var f
f = function(x,y){
return x+y
}
f.name // 'f'
具名函数赋值
- 采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只
var f
f = function f2(x,y){ return x+y }
f.name // 'f2'
console.log(f2) //f2 is not defined
window.Function
var f = new Function('x','y','return x+y')
- 如果你用new Function声明一个函数,那么这个函数没有名字的,也就是匿名函数
箭头函数
var f = (x,y) => {
return x+y
}
f(1,2)//调用方法函数名(参数1,参数2) 3
//如果函数体内只有一条语句,大括号和return可以省略,也就是说在不写大括号的情况下,它默认就return
var sum = (x,y) => x+y
//如果函数只有一个参数,小括号可以省略
var n2 = n => n*n
函数的执行
var z;
function f2(){
console.log(z)
}
z=1;
f2.call()//1
上面的代码函数调用后打印出来的z是1,而不是undefined,是因为在函数f2声明的时候并未获取和打印出z这个变量,而是在f2调用的时候才去获取的,就相当于只要在函数调用的时候打印的z都是与函数调用这段代码最近的那个z。也就是说一个函数里面获取一个变量,是在函数调用之后才去获取的(获取的变量是与函数调用这个代码前面的最新一次的变量值),而不是函数声明的时候就获取。
上面的代码其实你也可以看成:
var z;
z=1;
function f2(){
console.log(z)
}
f2.call()//1
将执行的函数赋值给一个变量
- 将一个执行的函数赋值给一个变量,它会先执行一下函数内部的语句,然后将返回值赋值给变量,之后执行这个变量操作的时候,这个变量的值都等于函数的返回值,而不会再去执行函数内部的语句。
function fn(){
console.log('aaa');
return 'zzz'
}
var result = fn() //'aaa'
result //'zzz'
- 上面的代码将执行的fn函数赋值给result,fn函数先执行了它内部的代码,打印出了'aaa',然后将返回值'zzz'赋值给了result,所以此时var result = 'zzz',之后获取result变量得到的值都是'zzz'
如何调用函数
函数到底是什么?
- 函数就是一段可以反复调用的代码块。
- 函数是一个对象,这个对象可以执行一段代码。
- 可以执行代码的对象就是函数。
var obj = {
//这里的test就是一个方法,也可以说是函数
test: function(){
console.log('zzz')
}
}
//方法(函数)调用
obj.test()
函数传参
- 函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。这意味着,在函数体内修改参数值,不会影响到函数外部。
var p = 2;
function f(p) {
p = 3;
}
f(p);
p // 2
上面代码中,变量p是一个原始类型的值,传入函数f的方式是传值传递。因此,在函数内部,p的值是原始值的拷贝,无论怎么修改,都不会影响到原始值。
函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。
var obj = { p: 1 };
function f(o) {
o.p = 2;
}
f(obj);
obj.p // 2
上面代码中,传入函数f的是参数对象obj的地址。因此,在函数内部修改obj的属性p,会影响到原始值。
function fn(obj){
obj.name = 'ff'
obj = new Object()
obj.name = 'man'
}
var person = new Object()
fn(person)
console.log(person.name) //'ff'
1.上面代码因为是将一个引用类型作为参数,所以传入的是一个引用地址(ADDR 26),也就是形参obj的值实际上就是person的引用地址
2.然后对这个ADDR 26添加一个name属性obj.name = 'ff'
3.重新对形参obj进行赋值,指向另一个内存地址(ADDR 34)
4.在ADDR 34上添加name属性obj.name='man'
5.所以最后person.name也就是person的引用地址(ADDR 26)里的name,也就是‘ff’
f()与f.call()
function f(x,y){return x+y};
f.call(undefined,1,2) //3
//相当于 f(1,2)
f 与 f.call()区别:
f 指的是这个对象
f.call() 是执行这个对象的函数体
this和arguments
function f(){
'use strict'
console.log(this)
console.log(arguments)
return undefined
}
f.call(1,2,3) // this 为 1,arguments 为 [2,3]
- call 的第一个参数可以用this得到
-
call 后面的参数可以用arguments得到
上面的函数f里面写入了打印this和arguments两条语句;当执行f.call(undefined,1,2)命令时,得到this是window(chorme浏览器显示的大写的Window其实是小写的window),arguments(实参)是一个伪数组,里面有1和2两个值,这里注意为什么this不是undefined,因为默认情况下this都会变成window,所以要想得到undefined就要开启严格模式
上面的代码使用了 ‘use strict’ 会变成严格模式。开启严格模式就不会乱改 this ,那么 this 原本的值就会打印出来。这样再给 undefined 真的是 undefined ,不会变成window。
f.call(1) //1
f.call('sss') // sss
this 就是 call 的第一个参数
伪数组:如果你的proto(原型链中)没有Array.prototype这一环,就是伪数组。同时你的原型链的原型链也不是Array.prototype,整个原型链中跟Array.prototype没有一点 关系,就是完完全全的伪数组。
arguments 的proto 直接指向了 object.prototype ,没有Array.prototype这一环,所以是伪数组。
- call 的第一个参数是this,call 后面的参数是arguments。
- 实际上可以用 arguments 全包起来,但是为了满足长得像 java 这个需求,所以设计者当初设计的时候临时加了一个 this来表示第一个参数 。
对象中的函数
- 属性方法里面直接调用一个和属性方法同名的函数,这个函数肯定不是这个属性方法,而是变量
var init=function(){
console.log('aaa')
}
var obj = {
init: function(){
console.log('zzz');
//这里的init是上面的变量init,而不是属性init,因为对象里的属性必须对象.属性,而不能直接写属性
init()
}
}
obj.init()
//zzz
//aaa
同名参数
如果有同名的参数,则取最后出现的那个值。
function f(a, a) {
console.log(a);
}
f(1, 2) // 2
上面代码中,函数f有两个参数,且参数名都是a。取值的时候,以后面的a为准,即使后面的a没有值或被省略,也是以其为准。
function f(a, a) {
console.log(a);
}
f(1) // undefined
调用函数f的时候,没有提供第二个参数,a的取值就变成了undefined。这时,如果要获得第一个a的值,可以使用arguments对象。
function f(a, a) {
console.log(arguments[0]);
}
f(1) // 1
var a = (1,2)
a //2
上面的代码a的值是2,多个值用小括号括起来,但取值的时候总是以后面的为准
设置默认参数值
function b(obj,str='z'){
return{
obj,str
}
}
上面的函数b里面有两个参数,其中第二个参数str,写成了str='z'意思是如果你不传第二个参数,它默认就相当于你传了一个'z'
//调用b函数,不传入第二个参数,str就是默认的'z'
b({name:'ff'})
//当调用函数时传入第二个参数,那个str就会是你传入的值
b({name:'ff'},'fangfang')
另外如果函数表达式中需要传参,而你调用的时候没有传,那么这个参数就是undefined,可以通过参数是否等于undefined来判断有没有传参数