JavaScript函数详解


Posted in Javascript onFebruary 27, 2015

1、函数定义

 函数包含一组语句,它们是javascript的基础模块单元,用于代码复用、信息隐藏和组合调用。函数用于指定对象的行为

2、函数的四种调用模式及this的初始化

第一种:方法调用模式
    以下事例证明通过方法调用模式调用时,this绑定到拥有该方法的对象。如:

var person = {

    name: "defaultName",

    setName : function(name){

        this.name = name;

    }

};

person.setName("zhangsan");

alert(person.name);

第二种:函数调用模式
    以下事例证明通过函数调用模式调用时,this绑定到全局对象上。如:

var test = add(value1, value2);

var name = "defaultName";

var person = {

    name: "zhangsan", // person中定义的name

    getName : function(){

        // 通过此方法可以将test函数的this改变为person的this对象

        var that = this;  // 解决方案

        // getName中定义的name

        var name = "lisi";

        var test = function(){

            // 通过that来访问person中的对象

            // this指向Global对象

            // this.name = defaultName

            // that.name = zhangsan

            alert([this.name, that.name]);

        };

        test(); // 函数调用模式

    }

}

person.getName();

第三种:构造器调用模式

// 定义一个Person的构造器,在调用时一定要用new调用

var Person = function(name){

    this.name = name;

}

// 添加一个方法到Person

Person.prototype.getName = function(){

    return this.name;

};

// 构造一个Person对象

var person = new Person("zhangsan");

alert(person.getName()); // 调用getName获取person对象中name属性的值

第四种:Apply调用模式

<script type="text/javascript">

    // 定一个累加方法。如sum(1,2,3,4...)

    // 该方法位于window执行环境中。

    var displayName = function(){

        alert("sum的执行环境: " + typeof(this));

        alert("Name: " + this.name); // 取出当前执行环境中name属性

    }

    // 定一个Person对象

    var Person = {

        name: "zhangsan"

    };

    displayName.apply(Person);

</script>

3、Apply和call的区别

// 定一个对象,包含一个add方法,返回a、b的和

var Person = {

    'add' : function(a, b){

        return a + b;

    }

};

// 显示a、b的和

function showInfo(a, b){

    alert(this.add(a, b));

}

// 通过apply方法改变showInfo方法的this指向

//showInfo(1, 3); // 对象不支持次对象

showInfo.apply(Person, [1, 3]);

showInfo.call(Person, 1, 3);

// 从上面可以看出,apply和call的区别是apply接受一个数组作为被调函数的参数,

// 而call是通过将被调函数的所有参数以逗号分隔的形式展开

4、函数参数(arguments)
    arguments并不是一个数组,只是与数组相似。arguments除了拥有length属性,数组的所有属性和方法都不具备。用arguments来实现一个累加的函数。

function sum(){

    var total = 0;

    for(var i=0; i<arguments.length; i++){ // arguments.length返回sum函数调用时传递参数的个数

        total += arguments[i];

    }

    return total;

}

alert("sum: " + sum(1, 3, 2, 4));

5、函数返回值(return)
    当一个函数被调用,通常会从函数的{开始执行到}结束。如果想提前结束该函数的执行可以使用return语句,此时,return语句后面的所有语句将永远不会执行。如:

function test(){

    alert("first");

    return;

    alert("second"); // 该语句永远被不会执行

}

test();

// 一个函数总是会返回值,如果没有使用return返回值,默认返回undefined。如:

function test(){

    alert("first"); 

}

alert(test()); // 输出:undefined

// 如果函数前使用new方式调用,且返回值不是一个对象,则返回this(新对象)。如:

function test(){

    alert("first");

}

var t = new test(); 

alert(typeof t); // 输出:‘object'

alert(t instanceof test); // 输出:true

6、异常(exception)

    异常是干扰程序正常流程的非正常事故(可能人为有意的)。当检查出这样的事故,应当抛出异常。如:

function add(a, b){ // 定义一个加法函数

    // 如果传递的参数不是数字类型,则抛出一个异常信息

    if(typeof a != 'number' || typeof b != 'number'){

        throw {

            'name'  : "typeError", // 属性是自定义的,名字可以任意取

            'message': "add方法必须使用数字作为参数"

        };

    }

    return a + b;

}

(function(){

    // 捕获add方法可能产生的异常

    try{

        add(10, "");

    } catch(e){ 

        // 一个try语句只有一个catch语句,如果要处理多个异常,则通过异常的name属性来区别

        // 判断异常的类型

        if(e.name === "typeError"){

            alert(e.message);

        }

    }

})();

7、给类型添加方法
    javascript中允许给基本类型添加方法。如:boolean、string、Number
    实例:在Function中添加一个method函数,该函数为Function添加其他自定义的函数(避免使用prototype),然后利用method函数想Function中添加一个add函数,最后测试add函数在Function中确实存在。该方法将func函数添加到Function中,以name命名。然后,返回Function的对象

Function.prototype.method = function(name, func){

    // 避免覆盖已有的方法

    if(!this.prototype[name]){

        this.prototype[name] = func;

    }

    return this;

};

// 通过Function.method方法添加一个加法函数到Function,该函数的名称为“add”

Function.method("add", function(a, b){

    if(typeof a != 'number' || typeof b != 'number'){

        throw {

            'name'  : "typeError",

            'message' : "add方法必须传入数字"

        };

    }

    return a + b;

});

// 调用Function的add方法是否存在

(function(){

    try{

        alert(Function.add(1, 3)); // 输出:4

    } catch(e){

        if(e.name === 'typeError'){

            alert(e.message);

        }

    }

})();

// 去除字符串两端的空白

String.method("trim", function(){

    return this.replace(/^\s+|\s+$/g, '');

});

alert('|' + "   hello world     ".trim() + '|'); // 输出: '|hello world|'

// 添加数字的取整函数

Number.method("integer", function(){

    // 可以通过此种方式调用函数,如:Math.random() == Math['random']() == Math["random"]()

    return Math[this < 0 ? 'ceil' : 'floor'](this);

});

alert((-10 / 3).integer()); // 输出:-3

8、递归调用(arguments.callee)
    递归调用就是自己调用自己。调用分为:直接调用和间接调用下面展示使用递归调用来计算指定值的斐波那契数列。

// 求i的阶乘

function factorial(i){

    if(i < 2){

        return 1;

    }

    return i*factorial(i-1); // 递归调用

}

alert(factorial(5)); // 求5的阶乘

// 以上方式存在一个问题?如下:

var factorial = function(i){

    if(i < 2){

        return 1;

    }

    return i*factorial(i-1); // factorial还能被调用吗?不能

}

var test = factorial; 

factorial = null;  

alert(test(2));

// 解决方案:

var factorial = function(i){

    if(i < 2){

        return 1;

    }

    return i*arguments.callee(i-1); // arguments.callee返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文

}

var test = factorial;

factorial = null;

alert(test(5));

9、作用域

// 在程序中,作用域控制着变量的可见性和生命周期。

var name = "default"; // 全局作用域

function getName(){

    var name = "getName"; // getName作用域下

    for(var i=0; i<2; i++){

        var inName = "inName";

    }

    alert(i + "," + inName); // 2,inName 注意:在js中没有块级作用域,及if、for、while中声明的变量是放在块所在的作用域下

    return name;

}

alert(getName()); // getName 注意:js存在函数作用域,所以在函数内部定义的变量在外部是不可见的

alert(name); // default

注意:在现代的很多语言中,推荐将变量尽可能的延迟声明。如:java而在js中,却不推荐这样做,因为js不支持块级作用域。推荐在函数的开始就将所有用到的变量进行声明。

10、闭包
    函数能够访问它被创建时环境的上下文称为闭包。作用域的好处是,内部函数可以访问外部函数的所有变量(除this和arguments)。

var myObject = {

    value   : 0,

    increment : function(inc){

        this.value = typeof inc === 'number' ? inc : 1;

    },

    getValue  : function(){

        return this.value;

    }

};

myObject.increment(10);

alert(myObject.value);

alert(myObject.getValue());

// 上面使用字面常量方式定义了一个myObject对象。但是value变量可以被外部对象访问

var myObject = function(){

    var value = 0;

    return {

        increment: function(inc){

            value += typeof inc === 'number' ? inc : 1;

        },

        getValue : function(){

            return value;

        }

    };

}();

myObject.increment(10);

alert(myObject.value); // 不能被外部对象访问

alert(myObject.getValue()); // 10

// 渐变body的背景色(黄色到白色)

var fade = function(node){

    var level = 1;

    var step = function(){

        var hex = level.toString(16);

        node.style.backgroundColor = '#FFFF' + hex + hex;

        if(level < 15){

            level += 1;

            setTimeout(step, 500); // 如果level小于15,则内部函数自我调用

        }

    };

    setTimeout(step, 1); // 调用内部函数

};

fade(document.body);

// 下面是一个很糟糕的例子

<a href="#" name="test">点击我...</a><br> // 点击时显示3

<a href="#" name="test">点击我...</a><br> // 点击时显示3

<a href="#" name="test">点击我...</a><br> // 点击时显示3

var add_the_handlers = function(nodes){

    var i;

    for(i = 0; i < nodes.length; i += 1) {

        nodes[i].onclick = function(e){ // 函数构造时的:i

            alert(i);

        };

    }

};

var objs = document.getElementsByName("test");

add_the_handlers(objs);

// 造成上面的原因是:a标签的事件函数绑定了变量i,则不是函数在构造时的i值。

// 解决方案如下:

var add_the_handlers = function(nodes){

    var i;

    for(i = 0; i < nodes.length; i += 1) {

        nodes[i].onclick = function(i){

            return function(e){

                alert(i); // 输出的i是构造函数传递进来的i,不是事件处理绑定的i。

            };

        }(i);

    }

};

var objs = document.getElementsByName("test");

add_the_handlers(objs);

11、回调(callbacks)

// data表示参数,而call_function则表示回调函数

function sendRequest(data, call_function){

    // setTimeout来模仿客户端请求服务端中传输数据的时间。

    // 当3秒钟后就调用回调函数(有客户端实现回调函数)

    setTimeout(function(){

        call_function(data); // 调用回调函数

    }, 3000);

}

// 测试sendRequest函数

sendRequest("参数", function(context){

    alert("context=" + context);

});

12、模块
    模块是一个提供接口而隐藏状态和实现的函数或对象。
    一般形式:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把他们保存到一个可以被访问到的地方。

Function.prototype.method = function(name,func){

    this.prototype[name] = func;

    return this;

};

String.method("deentityify",function(){

    var entity = {

        quot : '"',

        lt   : '<',

        gt   : '>'

    };

    return function(){

        return this.replace(/&([^&;]+);/g, function(a, b){ // 怎样知道a、b的值,了解正则表达式

            var r = entity[b];

            return typeof r === "string" ? r : a;

        });

    };

}());

alert("<">".deentityify()); // 测试:<">

注:模块模式通常结合单例模式使用,JavaScript的单例模式就是用对象字面量方式创建的对象,对象的属性值可以是数值或函数,并且属性值在该对象的生命周期中不会发生变化。

13、级联(链式操作)
    对于一些不返回值的方法,我们返回this,而不是undefined,那么我们就可以启动以级联(链式)去操作该对象。如下:

var $ = function(id){

    var obj = document.getElementById(id);

    obj.setColor = function(color){

        this.style.color = color;

        return this;

    };

    obj.setBgColor = function(color){

        this.style.backgroundColor = color;

        return this; // 返回this对象,启动级联

    };

    obj.setFontSize = function(size){

        this.style.fontSize = size;

        return this;

    };

    return obj;

};

$("test").setColor("red")

         .setFontSize("30px")

         .setBgColor("blue");

// 改进后的代码:

(function(id){

    var _$ = function(id){

        this.element = document.getElementById(id);

    };

    _$.prototype = {

        setColor : function(color){

            this.element.style.color = color;

            return this;

        },

        setBgColor : function(color){

            this.element.style.backgroundColor = color;

            return this;

        },

        setFontSize : function(size){

            this.element.style.fontSize = size;

            return this;

        }

    };

     

    // 添加到window原型链中

    window.$ = function(id){

        return new _$(id);

    };

})();

$("test").setColor("red")

         .setFontSize("30px")

         .setBgColor("blue");

14、套用
    所谓套用就是将函数与传递给它的参数相结合,产生一个新的函数。如:下面代码中定义一个add()函数,该函数能够返回一个新的函数,并把参数值传递给这个新函数,从而实现连加操作。

// 第一种方式:

var add = function(a){

    return function(b){

        return a + b;

    }

};

alert(add(1)(2)); // 3

// 第二种方式:用arguments实现

var add = function(){

    var arg = arguments;

    return function(){

        var sum = 0;

        for(var i=0; i<arg.length; i++){

            sum += arg[i];

        }

        for(i=0; i<arguments.length; i++){

            sum += arguments[i];

        }

        return sum;

    }

};

alert(add(1,2,3)(4,5,6)); // 21

// 第三种方式:通过一个套用方法(curry)实现

var add = function(){

    var sum = 0;

    for(var i=0; i<arguments.length; i++){

        sum += arguments[i];

    }

    return sum;

};

// 添加方法到Function的原型链上

Function.prototype.method = function(name, func){

    this.prototype[name] = func;

    return this;

};

// 套用方法

Function.method('curry', function(){

    // 通过数组Array的slice方法,使得arguments也具有concat方法

    var slice = Array.prototype.slice,

        args = slice.apply(arguments), that = this;

    return function(){

        return that.apply(null, args.concat(slice.apply(arguments)));

    };

});

alert(add.curry(1,2)(3,4)); // 10

15、记忆
    函数可以用对象去记住先前操作的结果,从而能避免无谓的运算。这种优化被称为记忆。

var fibonacci = function(){

    var mome = [0,1]; // 存放计算后的数据

    var fib = function(n){

        var result = mome[n];

        // 如果不存在被计算过的数据,则直接计算。然后在将计算结果缓存

        if(typeof result !== 'number'){

            result = fib(n-1) + fib(n-2);

            mome[n] = result;

        }

        return result;

    };

    return fib;

}();

for(var i=0; i<=10; i++){

    document.writeln("// " + i + ": " + fibonacci(i) + "<br/>");

}

//==========================

// 创建一个具有记忆的函数

//==========================

var memoizer = function(memo, fundamental){

    var shell = function(n){

        var result = memo[n];

        if(typeof result !== "number"){

            result = fundamental(shell, n);

            memo[n] = result;

        }

        return result;

    };

    return shell;

};

// 通过记忆函数memoizer完成斐波那契数列

var fibonacci = memoizer([0,1], function(shell, n){

    return shell(n-1) + shell(n-2);

});

// 通过记忆函数memoizer完成阶乘

var factorial = memoizer([1,1], function(shell, n){

    return n * shell(n-1);

});

for(var i=0; i<=15; i++){

    document.writeln("// " + i + ": " + factorial(i) + "<br/>");

}

小伙伴们看明白了没,非常实用吧,如有遗漏的地方,还请大神们指点下,共同进步

Javascript 相关文章推荐
select 控制网页内容隐藏于显示的实现代码
May 25 Javascript
js根据给定的日期计算当月有多少天实现思路及代码
Feb 25 Javascript
类似天猫商品详情随浏览器移动的示例代码
Feb 27 Javascript
JavaScript闭包实例讲解
Apr 22 Javascript
jquery制作漂亮的弹出层提示消息特效
Dec 23 Javascript
javascript函数特点实例分析
May 14 Javascript
js支持键盘控制的左右切换立体式图片轮播效果代码分享
Aug 26 Javascript
JQuery在循环中绑定事件的问题详解
Jun 02 Javascript
在Vue中使用highCharts绘制3d饼图的方法
Feb 08 Javascript
JSON字符串操作移除空串更改key/value的介绍
Jan 05 Javascript
基于 jQuery 实现键盘事件监听控件
Apr 04 jQuery
vue-router的两种模式的区别
May 30 Javascript
浅谈JavaScript的事件
Feb 27 #Javascript
JS实现图片放大镜效果的方法
Feb 27 #Javascript
浅谈javascript中的instanceof和typeof
Feb 27 #Javascript
js实现有时间限制消失的图片方法
Feb 27 #Javascript
js用拖动滑块来控制图片大小的方法
Feb 27 #Javascript
javascript中局部变量和全局变量的区别详解
Feb 27 #Javascript
对比分析AngularJS中的$http.post与jQuery.post的区别
Feb 27 #Javascript
You might like
BBS(php &amp; mysql)完整版(二)
2006/10/09 PHP
使用PHP实现密保卡功能实现代码&amp;lt;打包下载直接运行&amp;gt;
2011/10/09 PHP
初识php MVC
2014/09/10 PHP
PHP实现数组的笛卡尔积运算示例
2017/12/15 PHP
初学js 新节点的创建 删除 的步骤
2011/07/04 Javascript
25个非常棒的jQuery滑块插件和教程小结
2011/09/02 Javascript
jQuery类选择器用法实例
2014/12/23 Javascript
Javascript之String对象详解
2016/06/08 Javascript
AngularJS 中文API参考手册
2016/07/28 Javascript
jQuery实现表格文本框淡入更改值后淡出效果
2016/09/27 Javascript
浅谈js在html中的加载执行顺序,多个jquery ready执行顺序
2016/11/26 Javascript
关于javascript获取内联样式与嵌入式样式的实例
2017/06/01 Javascript
想用好React的你必须要知道的一些事情
2017/07/24 Javascript
详解redis在nodejs中的应用
2018/05/02 NodeJs
Vue组件的使用及个人理解与介绍
2019/02/09 Javascript
jquery获取并修改触发事件的DOM元素示例【基于target 属性】
2019/10/10 jQuery
vuex实现像调用模板方法一样调用Mutations方法
2019/11/06 Javascript
[40:05]DOTA2上海特级锦标赛A组小组赛#1 EHOME VS MVP.Phx第一局
2016/02/25 DOTA
python3+PyQt5实现使用剪贴板做复制与粘帖示例
2017/01/24 Python
新手常见6种的python报错及解决方法
2018/03/09 Python
python基础教程项目四之新闻聚合
2018/04/02 Python
判断python对象是否可调用的三种方式及其区别详解
2019/01/31 Python
python 列表输出重复值以及对应的角标方法
2019/06/11 Python
pytorch中如何使用DataLoader对数据集进行批处理的方法
2019/08/06 Python
python中利用matplotlib读取灰度图的例子
2019/12/07 Python
python中pop()函数的语法与实例
2020/12/01 Python
Charlotte Tilbury英国官网:英国彩妆品牌
2017/05/26 全球购物
法国面料和小百货在线商店:Mondial Tissus
2019/03/23 全球购物
Linux面试经常问的文件系统操作命令
2015/11/05 面试题
工商技校毕业生自荐信
2013/11/15 职场文书
经销商培训邀请函
2014/01/21 职场文书
设备动力科岗位职责范本
2014/02/23 职场文书
农业局学习党的群众路线教育实践活动心得体会
2014/03/07 职场文书
2014年企业党建工作总结
2014/12/18 职场文书
销区经理年终述职报告模板
2019/11/28 职场文书
德劲DE1107指针试高灵敏度全波段收音机机评
2022/04/05 无线电