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 相关文章推荐
jquery $(document).ready() 与window.onload的区别
Dec 28 Javascript
JQuery魔力之$(&quot;tagName&quot;)与selector
Mar 05 Javascript
从面试题学习Javascript 面向对象(创建对象)
Mar 30 Javascript
JQuery实现绚丽的横向下拉菜单
Dec 19 Javascript
使用focus方法让光标默认停留在INPUT框
Jul 29 Javascript
jQuery拖拽插件gridster使用指南
Apr 21 Javascript
简介JavaScript中的italics()方法的使用
Jun 08 Javascript
基于jQuery+Cookie实现的防止刷新的在线考试倒计时
Jun 19 Javascript
JS中Swiper的使用和轮播图效果
Aug 11 Javascript
Vue仿支付宝支付功能
May 25 Javascript
jQuery实现的简单手风琴效果示例
Aug 29 jQuery
简单的React SSR服务器渲染实现
Dec 11 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
PHP网站基础优化方法小结
2008/09/29 PHP
模板引擎正则表达式调试小技巧
2011/07/20 PHP
PHP正则表达式之定界符和原子介绍
2012/10/05 PHP
ThinkPHP的MVC开发机制实例解析
2014/08/23 PHP
如何使用纯PHP实现定时器任务(Timer)
2015/07/31 PHP
PHP页面间传递值和保持值的方法
2016/08/24 PHP
用函数式编程技术编写优美的 JavaScript
2006/11/25 Javascript
早该知道的7个JavaScript技巧
2013/03/27 Javascript
选择复选框按钮置灰否则按钮可用
2014/05/22 Javascript
javascript获取隐藏元素(display:none)的高度和宽度的方法
2014/06/06 Javascript
浅谈javascript回调函数
2014/12/07 Javascript
jQuery动画效果图片轮播特效
2016/01/12 Javascript
详解JavaScript实现设计模式中的适配器模式的方法
2016/05/18 Javascript
JavaScript中的事件委托及好处
2016/07/12 Javascript
JavaScript组合模式学习要点
2016/08/26 Javascript
Jquery针对tr td的一些实用操作方法(必看篇)
2016/10/05 Javascript
详解如何让InstantClick兼容MathJax、百度统计等
2017/09/12 Javascript
js实现随机点名系统(实例讲解)
2017/10/18 Javascript
微信小程序实现点击按钮修改view标签背景颜色功能示例【附demo源码下载】
2017/12/06 Javascript
使用vue-cli导入Element UI组件的方法
2018/05/16 Javascript
Vue 组件复用多次自定义参数操作
2020/07/27 Javascript
vue 数据遍历筛选 过滤 排序的应用操作
2020/11/17 Javascript
python 时间信息“2018-02-04 18:23:35“ 解析成字典形式的结果代码详解
2018/04/19 Python
解决Python 中英文混输格式对齐的问题
2018/07/16 Python
对Python3之进程池与回调函数的实例详解
2019/01/22 Python
python修改FTP服务器上的文件名
2019/09/11 Python
使用Pyhton 分析酒店针孔摄像头
2020/03/04 Python
将python字符串转化成长表达式的函数eval实例
2020/05/11 Python
5行Python代码实现图像分割的步骤详解
2020/05/25 Python
pycharm 2020.2.4 pip install Flask 报错 Error:Non-zero exit code的问题
2020/12/04 Python
美国最大的网上冲印店:Shutterfly
2017/01/01 全球购物
预备党员学习十八届三中全会精神思想汇报
2014/09/13 职场文书
湖南省党的群众路线教育实践活动总结会议新闻稿
2014/10/21 职场文书
机关工会工作总结2015
2015/05/26 职场文书
六一儿童节主持开场白
2015/05/28 职场文书
入党积极分子党小组意见
2015/06/02 职场文书