函数
形参在函数中像局部变量一样工作。
函数使用它们实参的值来计算返回值,成为该函数调用表达式的值。除了实参之外。每次调用还会拥有另一值-本次调用的上下文-这就是this关键字的值
如果函数挂载在一个对象上,作为对象的一个属性,就称它为对象的方方法。当通过这个对象来调用函数时,该对象就是此次调用的上下文,也是改函数的this的值。用于初始化一个新创建的对象的函数称为构造函数
如果一个函数定义表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称,实际上函数的名称将成为函数内部的一个局部变量
function f(){ console.log(f); } f();//
function f(){
window.runnerWindow.proxyConsole.log(f);
}
var t=function test(){ console.log(t);//
function test(){
window.runnerWindow.proxyConsole.log(t);
window.runnerWindow.proxyConsole.log(test);
}
console.log(test);//
function test(){
window.runnerWindow.proxyConsole.log(t);
window.runnerWindow.proxyConsole.log(test);
}
};
t();
函数声明语句被提前到外部脚本或外部函数作用域的顶部,所以以这种方式声明的函数,可以被在它定义之前出现的代码所调用,以表达式定义的函数就另当别论了,为了调用一个函数,必须要能引用它,而要使用一个以表达式定义的函数之前,必须把它赋值给一个变量。变量的声明提前了,但是被变量赋值是不会提前的,所以,以表达式方式定义的函数在定义之前是无法调用的
有4种方法来调用javascript函数
作为函数
作为方法
作为构造函数
通过它们的call和apply方法间接调用
关键值this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用,其this的值指向调用它的对象。如果嵌套函数作为函数调用。其this是全局对象
var o={m:function(){ var self=this; console.log(this===o);//true f(); function f(){ console.log(this===o);//false console.log(self===o);//true } }}; o.m();
如果函数或者方法调用之前带有关键字new,它就构成构造函数调用
构造函数调用创建一个新的空对象,这个对象继承自构造函数的prototype属性,构造函数试图初始化这个新创建的对象,并将这个对象用做其调用上下文,因此构造函数可以使用this关键字来引用这个新创建的对象。注意,尽管构造函数看起来像一个方法调用,但它依然会使用这个新对象作为调用上下文。也就是说,在表达式new o.m()中,调用上下文并不是o。
构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显示返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值。然而如果构造函数显式的使用return语句返回一个对象,那么调用表达式的值就是这个对象。如果构造函数使用return语句但没有指定返回值或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果。
可选形参
function getPropertyNames(o,a){ a=a||[]; for(var property in o){ a.push(property); } return a; } var o={x:1,y:2}; var test=getPropertyNames(o); console.log(test);//["x", "y"]
function max(){ var result=Number.NEGATIVE_INFINITY; for(var i=0;i<arguments.length;i++){ if(arguments[i]>result){ result=arguments[i]; } } return result; } console.log(max(4,1,8));//8
当一个函数包含若干形参。实参对象的数组元素是函数形参所对应实参的别名,实参对象中以数字索引,并且形参名称可以认为是相同变量的不同命名。通过实参名字来修改实参值的话,通过arguments[]数组也可以获取到更改后的值
arguments.callee和arguments.caller 属性指代当且正在执行的函数
函数作用域的概念:在函数中声明的变量在整个函数体内都是可见的(包括在嵌套的函数中),在函数的外部是不可见的。不在任何函数内声明的变量是全局变量,在整个javascript程序中都是可见的
函数的执行依赖于变量作用域,这个作用域是函数定义是决定的,而不是函数调用是决定的
闭包
每次调用javascrip函数的时候,都会为之创建一个新的对象用来保存局部变量,把这个对象添加至作用域链中。当函数返回的时候,就从作用域链中将这个绑定变量的对象删除。如果不存在嵌套的函数,也没有其他引用指向这个绑定对象,它就会被当作垃圾回收掉。如果定义了嵌套的函数,每个嵌套的函数都各自对应一个作用域链,并且这个作用域链指向一个变量绑定对象。但如果这些嵌套的函数对象在外部函数中保存下来,那么它们也会和所指向的变量绑定对象一样当做垃圾回收。但是如果这个函数定义了嵌套的函数,并将它作为返回值返回或者存储在某处的属性里,这时就会有一个外部引用指向这个嵌套的函数。它就不会被当作垃圾回收,并且它所指向的变量绑定对象也不会被当作垃圾回收。
作用:保存私有变量
var uniqueInteger=(function(){ var counter=0; return function(){ return counter++; }; }()); console.log(uniqueInteger());//0 console.log(uniqueInteger());//1
function constfuncs(){ var funcs=[]; for(var i=0;i<10;i++){ funcs[i]=function(){ return i; }; } return funcs; } var funcs=constfuncs(); console.log(funcs[5]());//10
函数的属性:
length 形参的数量
prototype每个函数都包含一个prototype属性,这个属性是指向一个对象的引用,这个对象称为原型对象,每个函数都包含不同的原型对象,当将函数用作构造函数的时候,新创建的对象会从原型对象上继承属性。
call apply看作某个对象的方法,通过调用方法的形式来间接调用函数,它们的第一个实参是要调用函数的母对象,它是调用上下文,在函数体内通过this来获得对它的引用,传入apply的参数数组可以是类数组对象也可以是真实数组。实际上,可以将当前函数的arguments数组直接传入apply来调用另一个函数
bind 将函数绑定到某个对象
由ecma5 bind方法所返回的函数并不包含prototype属性(普通函数固有的prototype属性是无法删除的),并且将这些绑定的函数用作构造函数时所创建的对象从原始的未绑定的构造函数中继承继承prototype。同样,在使用instanceof运算符时,绑定构造函数和未绑定构造函数并无两样。
if(!Function.prototype.bind){ Function.prototype.bind=function(o){ var self=this,boundArgs=arguments; return function(){ var arg=[],i; for(i=1;i<boundArgs.length;i++){ arg.push(boundArgs[i]); } for( i=0;i<arguments.length;i++){ arg.push(arguments[i]); } return self.apply(o,arg); }; }; }
Function 关于这个构造函数非常重要的一点,就是它所创建的函数并不是使用词法作用域,想反,函数体代码的编译总是会在顶层函数执行
var scope="global"; function construction(){ var scope="local"; return new Function("return scope"); } var p=construction(); console.log(p());//global
将Function构造函数被认为是在全局作用域中执行eval
判断是否是函数
function isFunction(x){
return Object.prototype.toString.call(x)==="[object Function]";
}
if((typeof Array.prototype.map)!="function"){ Array.prototype.map=function(f){ var results=[]; var a=this; for(var i=0,len=a.length;i<len;i++){ if(i in a){ results[i]=f.call(null,a[i],i,a); } } }; } if(typeof (Array.prototype.reduce)!="function"){ Array.prototype.reduce=function(f,inital){ var a=this; var i=0,len=a.length,accumulator; if(arguments.length>1){ accumulator=inital; } else{ if(len===0) { throw TypeError(); } while(i<len){ if(i in a){ accumulator=a[i++]; break; } else i++; } if(i==len){ throw TypeError(); } } while(i<len){ if(i in a) { accumulator=f.call(undefined,accumulator,a[i],i,a); } i++; } }; } var sum=function(x,y){return x+y;}; var square=function(x){return x*x;}; var data=[1,1,3,5,5]; var mean=data.reduce(sum)/data.length; var deviations=data.map(function(x){return x-mean;}); console.log(deviations);//[-2, -2, 0, 2, 2] var stddev=Math.sqrt(deviations.map(square).reduce(sum)/(data.length-1)); console.log(stddev);[2]
高阶函数就是操作函数的函数
function not(f){ return function(){ console.log(this); var result=f.apply(this,arguments); return !result; }; } var even=function(x){ return x%2===0; }; var odd=not(even);//判断是否为奇数 [1,1,3,5,5].every(odd);//true
function compose(f,g){ return function(){ return f.call(this,g.apply(this,arguments)); }; } var square=function(x){return x*x;}; var sum=function(x,y){return x+y;}; var squareofsum=compose(square,sum); console.log(squareofsum(2,3));//25 先求和,再求平方
function compose(f,g){ return function(){ return f.call(this,g.apply(this,arguments)); }; } function array(a,n){ return Array.prototype.slice.call(a,n||0); } function partialLeft(f){ var args=arguments; return function(){ var a=array(args,1); a=a.concat(array(arguments)); return f.apply(this,a); }; } function partialRight(f) { var args=arguments; return function(){ var a=array(arguments); a=a.concat(array(args,1)); return f.apply(this,a); }; } function partial(f){ var args=arguments; return function(){ var a=array(args,1); var i=0,j=0; for(;i<a.length;i++){ if(a[i]===undefined){ a[i]=arguments[j++]; } } a=a.concat(array(arguments,j)); return f.apply(this,a); }; } var f=function(x,y,z){ return x*(y-z); };
console.log(partialLeft(f,2)(3,4));//-2 2*(3-4)
console.log(partialRight(f,2)(3,4));6 3*(4-2)
console.log(partial(f,undefined,2)(3,4));-6
function compose(f,g){ return function(){ return f.call(this,g.apply(this,arguments)); }; } function array(a,n){ return Array.prototype.slice.call(a,n||0); } function partialLeft(f){ var args=arguments; return function(){ var a=array(args,1); a=a.concat(array(arguments)); return f.apply(this,a); }; } function partialRight(f) { var args=arguments; return function(){ var a=array(arguments); a=a.concat(array(args,1)); return f.apply(this,a); }; } function partial(f){ var args=arguments; return function(){ var a=array(args,1); var i=0,j=0; for(;i<a.length;i++){ if(a[i]===undefined){ a[i]=arguments[j++]; } } a=a.concat(array(arguments,j)); return f.apply(this,a); }; } var f=function(x,y,z){ return x*(y-z); }; console.log(partialLeft(f,2)(3,4)); console.log(partialRight(f,2)(3,4)); console.log(partial(f,undefined,2)(3,4)); String.prototype.first=partial(String.prototype.charAt,0); String.prototype.last=partial(String.prototype.substr,-1); var text="javascript"; console.log(text.charAt(0)); console.log(text.first()); console.log(text.charAt(text.length-1)); console.log(text.last()); var not=partialLeft(compose,function(x){return !x;}); var even=function(x){return x%2===0;}; var odd=not(even); var isNumber=not(isNaN); console.log(odd(3)); console.log(isNumber("23123num"));