javascript中的函数(进阶1)

253 阅读6分钟

javascript中的函数(进阶1)

函数进阶包含内容

找工作必备之函数进阶,需要掌握以下内容:

  • 深入理解函数
  • 深入理解作用域
  • 深入理解闭包
  • this指向深入
  • 面向对象编程

深入理解函数

1.函数声明的三种方式

方式一

函数的声明语句

function fn(a,b){
 //执行代码
    return a+b;
}
fn(1,2);//3

声明语句不是可执行语句,不用;结束

方式二

函数的表达式:将一个匿名函数(没有名字的)赋值给一个新的变量,变量可以作为函数使用

var fn = function(a,b){
//执行代码
    return a+b;
};
fn(1,2);//3

函数表达式是可执行语句,需要用;结束

特殊写法

将一个有名字的函数赋值给一个新的变量,此时要注意在外部使用时只能用这个变量,如果使用函数名字就会报错,函数名字只能在函数内部使用,而变量名称既可在外部也可在函数内部使用,可以将变量看作实参,函数名看作形参来理解。

var hello = function hel(a,b){
    return a+b;
    console.log(hello===hel);//true
};
console.log(hel());//报错
console.log(hello(1,2));//3

方式三

Function 构造函数,不推荐使用

var fn = new Function('a','b','return a+b;');
fn(1,2);//3
//等价于
var fn=function(a,b){
	return a+b;
};
fn(1,2);//3

2.函数的返回值

没有返回值时,调用函数的结果是一个undefined

function fn(a,b){
	a+b;
}
fn(1,2);//undefined

return语句用于返回函数调用后的结果

function fn(a,b){
	return a+b;
}
fn(1,2);//3

return 可以返回任意数据类型

注意:并不是所有return语句后的语句都不执行。

如下:

 function testFianlly(){
 	try{
 		//正常时执行
		return 2;
	}catch(e){
		//出错时执行
		//e 表示抛出的异常
		return 1;
	}finally{
		//始终执行
		return 0;
	}
 }

一个函数可以有多个return语句

如果一个函数在调用时使用了new 关键字,且返回值不是一个对象,或者没有返回值时,则返回该函 数对象(this)。

如下:

function fn(){
    console.log(this);//fn{}
	return 1;
}
fn();//1
var f = new fn();
console.log(f);//fn{},函数对象

return返回了一个对象,此时this又是什么?

function fn(){
console.log(this);
	return {
		name:'john',
	}
}
var f = new fn();
console.log(f);//该对象

return 还可以返回一个**;**此时函数直接被终止,但是return 之前的语句还会执行

function fn(){
	return;
	alert('不会执行');
}
fn();

3.函数调用方式

方式一

函数调用模式

function add(x,y){
	console.log(this);//在非严格模式下,window object
    //'use strict';
    //console.log(this);//在严格模式下,undefined
	return x+y;
}
console.log(this);//window object
add(3,4);//7

在非严格模式下就有可能修改this中的变量(重写)

function fn(){
	this.a=1;
	return this.a
}
fn();//1
this.a=2;
console.log(this.a);//2

注意:小心避免全局的属性重写带来的问题

方式二

方法调用模式

var obj = {
    a:1,
    //fn 称为对象的方法
	fn:function(){
    	console.log(this);//obj object
	    console.log("hello");	
	},
    fn2:function(){
        this.a = 2; 
    }
};
obj.fn();//hello
obj.fn2();
console.log(obj.a);//2

方式三

构造函数调用模式

function fn(){
	this.a = 1;
}
var f = new fn();//可简写为 var f = new fn;
console.log(f);

构造函数不常用了解即可

方式四

间接调用模式

var obj = {
    name:'john',
};
function sum(x,y){
    console.log(this);// obj object
	return x+y;
}
sum.call(obj,1,2);//3
sum.apply(obj,[1,2]);//3

可以发现call 和apply 方法会改变this的指向,关于this指向见this指向深入

4.函数参数

函数参数可以是任意类型,任意个数。

同名形参

在非严格模式下,可以出现同名形参,但 只会访问最后一个。

function fn(x,x,x){
    //'use strict';
	return x;
}
fn(1,2,3);//3

参数个数

形参个数大于实参个数时,剩余形参都将为undefined

function fn(x,y){
	console.log(x,y);
}
fn(1);//1 undefined

实参个数大于形参个数时,考虑arguments对象,arguments对象是js的一个内置对象,是一个包含了实参的类数组(不是一个数组,所以不具备数组的方法)。

function f1(a){
	console.log(arguments[0],arguments[1],arguments[2]);//1 2 3
}
f1(1,2,3);

函数重载

什么是重载?

定义了相同的函数名,传入不同的参数。函数会匹配同样的类型去执行

function fn(a){
	return a;
}
function fn(a,b){
	return a+b;
}
//如果存在重载时
//fn(1);//1
//fn(1,2);//3
//但是js中函数不存在重载,后定义的覆盖前者
fn(1);//NaN

模拟重载

function do(){
	if (arguments.legth==0){
		return 0;
	}else if(arguments.length==1){
		return arguments[0];
	}else if (arguments.length==2){
		return arguments[0]+arguments[1];
	}
}
do();//0
do(1);//1
do(1,2);//3

出现多个if...else语句时可以考虑使用switch语句,所以以上代码可以表示为:

function do(){
	switch(arguments.lebgth){
		case 0:
			return 0;
			break;
		case 1:
			return arguments[0];
			break;
		case 2:
			return arguments[0]+arguments[1];
			break;
	}
}
do();//0
do(1);//1
do(1,2);//3

参数传递(难点)

1.基本数据类型

在向参数传递基本数据类型的值时,被传递的会被复制到一个局部变量

function fn(num){
	num = num+10;
    //num = count+10;
	return num;
}
var count = 20;
var res = fn(count);//30

2.引用数据类型

在向参数传递引用数据类型的值时,会把这个值在内存中的地址赋值给局部变量

function setName(obj){
	obj.name = 'john';
}
var person = new Object();
setName(person);
console.log(person.name);//john

5.函数中的属性和方法

函数属性

length属性

arguments对象中的length属性表示实参个数,而函数参数length属性表示的是形参的个数

function fn(x,y){
	console.log(arguments.length);//4
	console.log(fn.length);//2
}
fn(1,2,3,4);
name属性

name指的是当前函数的名字

function fn(){};
console.log(fn.name);//fn
var fn2 = function (){};
console.log(fn2.name);//fn2
var fn3 = function abc(){};
console.log(fn3.name);//abc

prototype属性

每一个函数都有一个prototype属性(详细见面向对象)

function fn(){}
console.log(fn.prototype);//是一个对象
//由于是一个对象,我们可以自定义他的属性
fn.prototype.a = 1;
console.log(fn.prototype);

函数方法

每个函数都包含两个非继承来的方法,自身的

apply( )&call()

apply与call方法的作用是一样的,可以改变this的指向,两者唯一区别就是传参的不同。

call({},1,2);
apply({},[1,2]);
window.color = 'red';
console.log(window);
var obj = {
    color:'blue';
};
function sayColor(){
    console.log(this.color);
}
sayColor();//red
sayColor.call(this);//red
sayColor.call(window);//red
sayColor.call(obj);//blue

**注意:**在非严格模式下,如果我们使用call()或apply()方法,传入一个null或undefined会被转换成一个window对象。在严格模式下,只会指向当前所绑定的对象。

var color = 'red';
function sayColor(){
   // 'use strict';
   console.log(this.color);
}
//sayColor.call(null);//red
sayColor.call(null);//报错
bind()

es5新增的方法,主要作用:将函数绑定到某个对象上,并且有返回值,是一个函数

function fn(y){
	return this.x+y;
}
var obj = {
	x:1
};
var gn = fn.bind(obj);
console.log(gn(3));//4

方法的应用

1.找出数组的最大元素
var arr = [1,3,4,6,2];
var arrMax=Math.max.apply(null,arr);
console.log(arrMax);//6
2.将类数组转换成真正的数组
function add(x,y){
	//console.log(arguments);
	//return x+y;
    var arr = Array.prototype.slice.apply(arguments);
    console.log(arr);//[1,2]
}
add(1,2)
3.数组追加
var arr = [];
Array.prototype.push.apply(arr,[1,2,3,4]);
console.log(arr);//[1,2,3,4]
4.利用call和apply作继承
//中国人
function Chinese(name,age){
	this.name = name;
    //c.name = 'john';
    this.age = age;
    //c.age = 18;
    this.sayAge = function(){
        console.log(this.age);
    }
}
//河南人
function Henan(name,age){
    //继承了Chinese
    Chinese.call(this,name,age);//把this指向了c
}
var c = new Henan('john',18);
c.sayAge();//18
//东北人
function Norther(name,age){
    Chinese.apply(this,arguments);
}
var d = new Norther("jack",20);
console.log(d.name);//jack 
5.使用log代替console.log()
function log(){
	console.log.apply(console,arguments)
}
log(d);