js模式第四章

第四章 函数

函数表达式和函数声明式: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)
复制代码

配置对象的优势在于:

  • 不须要记住众多的参数以及其顺序。
  • 能够安全忽略可选参数。
  • 更加易于阅读和维护。
  • 更加易于添加和删除参数。

而缺点在于:

  • 须要记住参数名称。
  • 属性名称没法被压缩。?
相关文章
相关标签/搜索