函数表达式和函数声明式:node
//命名函数表达式
var demo=funtion d(){
console.log("Hello World!");
};
//函数表达式
var demo=function(){
console.log("Hello World!");
};
//函数声明式
function demo(){
console.log("Hello World!")
}
复制代码
函数表达式又分为命令函数表达式和函数表达式(匿名函数),而且函数表达式结尾须要加上分号,而函数声明式则不须要。浏览器
函数声明式会进行变量提高,而函数表达式则不会。缓存
//全局函数 function foo(){ alert('global foo'); }安全
function bar(){ alert('global bar') }闭包
function hoistMe(){ console.log(typeof foo); //输出 "function" console.log(typeof bar); //输出 "undefined"app
foo();//输出"local foo"
bar();//输出TypeError: bar is not function
//函数声明
//变量'foo'以及其实现着2被提高
function foo(){
alert('local foo');
}
//函数表达式
//仅变量 'bar' 提高
//函数实现并未提高
var bar = function () {
alert('local bar')
}
复制代码
}函数
函数都是对象,函数能够被当作参数传递给其余函数,当一个函数被当作参数传递给另外一个函数,且执行,这叫回调。测试
Code:优化
function writeCode(callback){
//do something
callback();
}
function introduce(){
// do something
}
//传递函数,执行回调
writeCode(introduce);
复制代码
若是传递的对象不是一个函数,而是一个对象的方法,而且这个方法用this来引用所属的对象,那可能会致使意想不到的后果。this
假设一个方法paint(),它是一个名为myapp的对象的方法:
var myapp={};
myapp.color="red";
myapp.paint=function(node){
node.style.color=this.color;
};
复制代码
函数findNodes()执行如下语句:
var findNodes=function(callback){
// ...
if(typeof callback === "function"){
callback(found);
}
};
复制代码
若是执行findNodes(myapp.paint)并不会获得如期的结果,由于此时this的引用的对象发生了改变,变成了findNodes,而findNodes并无color这个属性。那咱们怎么获得预期的结果呢?
一个解决方法,咱们传递回调函数,而且也传递回调函数所属的对象:
var findNodes = function(callback,callback_obj){
// ...
if(typeof callback === "function"){
callback(callback_obj,found);
}
};
复制代码
那咱们的写法将变成findNodes(myapp.paint,myapp),咱们也能够将第一个参数写成字符串的形式,这样无需写两遍对象名, findNodes("paint",myapp),Code:
var findNodes = function(callback,callback_obj){
// ...
if(typeof callback === "string" ){
callback = callback_obj[callback]
}
if(typeof callback === "function"){
callback.call(callback_obj,found)
}
};
复制代码
函数也是对象,所以它们也能够用做为返回值。 下面有个demo:
var setup=function(){
alert(1);
return function(){
alert(2);
};
};
var my=setup(); //alert 1
my(); // alert 2
复制代码
因为setup()包装了返回函数,它建立了一个闭包(粗俗的理解就是函数中的函数),能够用这个闭包存储一些私有数据,这些数据仅能够被该返回函数访问,但外部代码却没法访问。 Code:
var setup=function(){
var count = 0;
return function(){
return (count+=1);
};
};
var next=setup();
next(); //result is 1
next(); //result is 2
next(); //result is 3
复制代码
若是建立了一个新函数而且将其分配给保存另外函数的同一个变量,那么就以一个新函数覆盖了旧函数。在某种程度上,回收了旧函数指针以指向一个新函数。而这一切发生在旧函数体的内部。在这种状况下,该函数以一个新的实现覆盖并从新定义了自身,这可能听起来比实际上更复杂,Code:
var scareMe=function(){
alert("Boo!");
scareMe=function(){
alert("Double boo!");
};
};
scareMe(); //输出 Boo
scareMe(); //输出 Double boo!
复制代码
当函数有且执行一次时,这种模式很是有效。 可是这种模式有个缺点,就是定义在自身时,已添加的属性和方法都会丢失,此外,若是该函数使用了不一样的名称,好比从新分配给了另一个变量或者以对象的方法来使用,那么重定义的部分将永远不会发生。而且将会执行原函数体。 Code:
//1 添加一个新属性
scareMe.property="property";
//2 赋值给另外一个不一样名称的变量
var prank=scareMe;
//3 做为一个方法使用
var spooky = {
boo:scareMe
};
//
prank(); //输出 "bool"
prank(); //输出 "bool"
console.log(prank.property) //输出 "property"
spooky.boo(); //输出 "bool"
spooky.boo(); //输出 "bool"
console.log(spooky.boo.property); //输出 "property"
scareMe(); //输出 "Double boo!"
scareMe(); //输出 "Double boo!"
console.log(scareMe.property); // 输出 "undefined"
复制代码
当咱们以新名称和做为一个方法使用时,scareMe指针一直被重写,至此scareMe函数自定义的部分一直没有执行。直到当咱们直接执行scareMe时,指针没有被重写,自定义部分才得以执行。
即时函数模式,该模式有一下几部分组成:
这种模式的好处就是造成了做用域沙箱。避免了全局污染,而且能够执行一些一次新函数,而没必要去建立复用函数。
(function(){
var days=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
today=new Date(),
msg='Today is ' + days[today.getDay()] + ', ' + today.getDate();
alert(msg);
}())
复制代码
即时函数一样能够传递参数,Code:
;(function(who,when){
console.log("I met " + who + " on " + when)
})("Nei",new Date());
复制代码
固然即时函数一样能够返回值,Code:
var result=(function(){
return 2+2;
})();
复制代码
赋值给一个变量时,即时函数能够不须要最外层括号,Code:
var result=function(){
return 2+2;
}();
复制代码
即时函数不仅仅能够返回整数值,一样还能够返回其余类型,例如返回函数。造成一个闭包,在一个函数做用域中保留变量。Code:
var getResult=(function(){
var res = 2 + 2;
return function(){
return res;
};
})();
复制代码
因而咱们能够借用即时函数,在建立对象时,能够将对象的属性永久保留在内存中。Code:
var o = {
message: (function(){
var who = "me",
what = "call";
return who + " " + what;
})(),
getMsg:function(){
return this.message;
}
};
复制代码
初始化时分支时一种优化模式,当知道某个条件在整个程序生命周期内都不会发生改变的时候,仅对该条件测试一次时颇有意义的。浏览器嗅探(功能检测)就是一个典型的例子。Code:
var utils = {
addListener:function(el,type,fn){
if(typeof window.addEvenListener === "function"){
el.addEventListener(type,fn,false);
}else if(typeof document.attachEvent === "function"){
el.attachEven('on'+type,fn);
}
},
removeListener:function(el,type,fn){
//......
}
};
// 当咱们每次调用utils.addListener 或 utils.removeListener时,都会重复执行检测代码。可是对于这样不会变的特性时,咱们能够执行一次性检查代码。
复制代码
一次性检查Code:
var utils = {
addListener:null,
removeListener:null
};
if(typeof window.addEventListener==="function"{
utils.addListener = function(el,type,fn){
el.addEventListener(type,fn,false);
};
utils.removeListener = function (el,type,fn) {
el.removeEventListener(type,fn,false);
}
}else if(){
// ie......
}else{
// 更早浏览器兼容模式
}
复制代码
函数也是对象,咱们能够添加属性上去,例如咱们在大量计算的时候,就能够将结果缓存在函数对象上,Code:
var myFunc = function (param){
if(!myFunc.cache[param]){
var result = {};
//...大计算
myFunc.cache[param]=result;
}
return myFunc.cache[param]
};
myFunc.cache={};
复制代码
固然咱们的参数通常都不仅一个,这时怎么办呢?看Code:
var myFunc = function (){
var cachekey = JSON.stringfy(Array.prototype.slice.call(arguments)),
result;
if(!myFunc.cache[cachekey]){
result = {};
//...大计算
myFunc.cache[cachekey]=result;
}
return myFunc.cache[cachekey]
};
myFunc.cache={};
复制代码
调用函数,传入参数时,一种传参方式是多少个参数传递多少个参数,Code:
Demo(one,two,three,four,five,...);
复制代码
可是这样传参,参数一旦不少,函数将变得难看和难以维护。 因此咱们传参时,将参数写进一个对象中,这样传参就是只有传递一个对象。
var conf={
username:'Nei',
first:'N',
last:'de'
};
Demo(conf)
复制代码
配置对象的优势在于:
而缺点在于: