一、函数概述
函数是一块JavaScript代码,被定义一次,但可执行和调用多次.JS中的函数也是对象,所以js函数可以像其它对象那样操作和传递,所以我们也常叫JS中的函数为函数对象
不同的调用方式
直接调用:foo();
对象方法:o.method();
构造器:new Foo();
call/apply/bind:func.call(o);
二、函数声明和表达式
1.函数声明 vs 函数表达式
函数声明:function add(a,b){....}
函数表达式:
var add=function(a,b){...};
(function(){...})();//定义匿名函数并立即调用
return function(){...};
var add=function foo(a,b){...};
2.变量&函数的声明前置
函数声明可以在调用之后,而函数表达式必须在调用之前
3.命名函数表达式(NFE)
var func=function nfe(){};
alert(func===nfe);//在IE6,7,8弹出false,在IE9+会报错:"nfe" is undefined
//递归调用
var func=function nfe(){/*do sth*/nfe();}
4.Function构造器
可以含有n个参数,前面n-1个参数表示形参,最好一个表示函数体里面的代码
var func=new Function("a","b","console.log(a+b);");
func(1,2);//3
var func=Function("a","b","console.log(a+b);");
func(1,2);//3
Function构造器的作用域
//case1
Function('var localVal="local";console.log(localVal);')();//localVal仍为局部变量
console.log(typeof localVal);
//result:local,undefined
//case2
var globalVal="global";
(function(){
var localVal="local";
Function("console.log(typeof localVal,typeof globalVal);")();//local不可访问,全局变量global可以访问
})();
//result:undefined,string
在浏览器中,全局的this指window
console.log(this.document===document);//true
console.log(this===window);//true
this.a=37;
console.log(window.a);//37
2.一般函数的this(浏览器)
function f1(){
return this;
}
f1()===window;//true,在浏览器中this指window,在node.js中指global对象
function f2(){
"use strict";//see strict mode
return this;
}
f2()==="undefined";//true,严格模式下,this指向undefined
3.作为对象方法的函数的this
函数作为对象属性的值的时候常常叫做对象方法
var o={
prop:37,
f:function(){
return this.prop;
}
};
console.log(o.f());//37
var o={prop:37};
function independent(){
return this.prop;
}
independent();//此时this指window
o.f=independent;//此时this指o
console.log(o.f());//37
4.对象原型链上的this
var o={f:function(){return this.a+this.b;}};
var p=Object.create(o);//p为空的对象,并且p的原型指向o
p.a=1;
p.b=4;
console.log(p.f());//5
5.get/set方法与this
function modulus(){
return Math.sqrt(this.re*this.re+this.im*this.im);
}
var o={
re:1,
im:-1,
get phase(){
return Math.agan2(this.im,this.re);
}
};
Object.defineProperty(o,"modulus",{
get:modulus,enumerable,configurable:true
});
console.log(o.phase,o.modulus);
6.构造器中的this
function MyClass(){
this.a=37;
}//函数没有return语句,会返回this
var o=new MyClass();//o的原型指向MyClass.prototype
console.log(o.a);//37
function C2(){
this.a=37;
return {a:38};
}
o=new C2();
console.log(o.a);//38
7.call/apply方法与this
function add(c,d){
return this.a+this.b+c+d;
}
var o={a:1,b:3};
add.call(o,5,7);//1+3+5+7=16
add.apply(o,[10,20]);//1+3+!0+20=34
call和apply基本一致,只是传参有所不同,call是将参数一个一个传进去,而apply是将参数作为一个数组传进去
function bar(){
console.log(Object.prototype.toString.call(this));
}
bar.call(7);//"[object Number]"
8.bind方法与this
function f(){
return this.a;
}
var g=f.bind({a:"test"});
console.log(g());//test
var o={a:37,f:f,g:g};
console.log(o.f(),o.g());//37,test
foo.name:函数名
foo.length:形参个数
arguments.length:实参个数
function foo(x,y,z){
arguments.length;//2,实际参数1,2
arguments[0];//1
arguments[0]=10;
x;//change to 10;与上面的arguments[0]为绑定关系
arguments[2]=100;
z;//still undefined!!!,注意:未传参数失去绑定关系
arguments.callee===foo;//true
}
foo(1,2);
foo.length;//3,x,y,z三个形参
foo.name;//"foo",函数名
严格模式:
function foo(x,y,z){
"use strict";
arguments.length;//2,实际参数1,2
arguments[0];//1
arguments[0]=10;
x;//严格模式下仍然是1
arguments[2]=100;
z;//still undefined!!!,注意:未传参数失去绑定关系
//严格模式下不能使用callee
}
foo(1,2);
foo.length;//3,x,y,z三个形参
foo.name;//"foo",函数名
2.apply/call方法(浏览器)
function foo(x,y){
console.log(x,y,this);
}
foo.call(100,1,2);//1,2,Number(100)
foo.apply(true,[3,4]);//3,4,Boolean(true)
foo.apply(null);//undefined,undefined,window
foo.apply(undefined);//undefined,undefined,window
严格模式下:
function foo(x,y){
"use strict"
console.log(x,y,this);
}
foo.apply(null);//undefined,undefined,null
foo.apply(undefined);//undefined,undefined,undefined
3.bind方法
this.x=9;
var module={
x:81,
getX:function(){return this.x;}
};
module.getX();//81
var getX=module.getX;
getX();//9
var boundGetX=getX.bind(module);//通过bind方法可以改变函数运行时里面对应得this,此时this指向module
boundGetX();//81
4.bind与currying
bind除了改变函数里的this外,还有科里化的功能,函数科里化就是将函数拆成多个单元
function add(a,b,c){
return a+b+c;
}
var func=add.bind(undefined,100);//相当于100固定赋值给第一个参数a
func(1,2);//103,此时1给b,2给c
var func2=func.bind(undefined,200);//200绑定给b,100已经绑定给a
func2(10);//310
实例:
function getConfig(colors,size,otherOptions){
console.log(colors,size,otherOptions);
}
var defaultConfig=getConfig.bind(null,"#cc0000","1024*768");
defaultConfig("123");//#cc0000 1024*768 123
defaultConfig("456");//#cc0000 1024*768 456
5.bind与new
function foo(){
this.b=100;
return this.a;
}
var func=foo.bind({a:1});
func();//1
new func();//{b:100},使用new调用时,bind的作用会被忽略掉
注意:使用new时,除非是对象,否则会把this作为返回值,并且this会被初始化为一个空对象,这个对象的原型是foo.prototype
6.bind方法模拟(如何在老的ie浏览器中实现bind方法)
bind方法的两个功能:绑定this和科里化(把函数拆成不同的子函数)
if(!Function.prototype.bind){
Function.prototype.bind=function(oThis){
if(typeof this!=="function"){
//closet thing possible to the ECMAScript 5
//internal IsCallable function
throw new TypeError("What is trying to be bound is not callable")
}
var aArgs=Array.prototype.slice.call(arguments,1),
fToBind=this,
fNOP=function(){},
fBound=function(){
return fToBind.apply(this.instanceof fNOP?this:oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype=this.prototype;
fBound.prototype=new fNOP();
return fBound;
}
}