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);