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 相关文章推荐
浅谈javascript中的作用域
Apr 07 Javascript
重构Javascript代码示例(重构前后对比)
Jan 23 Javascript
javascript制作坦克大战全纪录(2)
Nov 27 Javascript
jquery移动端TAB触屏切换实现效果
Dec 22 Javascript
jQuery使用animate实现ul列表项相互飘动效果示例
Sep 16 Javascript
谈谈JS中常遇到的浏览器兼容问题和解决方法
Dec 17 Javascript
React Native中NavigatorIOS组件的简单使用详解
Jan 27 Javascript
vue实现验证码按钮倒计时功能
Apr 10 Javascript
详解ES7 Decorator 入门解析
Feb 18 Javascript
Vue插件之滑动验证码用法详解
Apr 05 Javascript
在vue中通过render函数给子组件设置ref操作
Nov 17 Vue.js
react中useState使用:如何实现在当前表格直接更改数据
Aug 05 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+MySql实现登录系统与输出浏览者信息功能
2016/07/01 PHP
php获取手机端的号码以及ip地址实例代码
2018/09/12 PHP
js获取对象为null的解决方法
2013/11/21 Javascript
解析img图片没找到onerror事件 Stack overflow at line: 0
2013/12/23 Javascript
JavaScript类型系统之基本数据类型与包装类型
2016/01/06 Javascript
Nodejs中使用captchapng模块生成图片验证码
2017/05/18 NodeJs
谈谈VUE种methods watch和compute的区别和联系
2017/08/01 Javascript
react路由配置方式详解
2017/08/07 Javascript
react-router4 配合webpack require.ensure 实现异步加载的示例
2018/01/18 Javascript
基于Vue实现可以拖拽的树形表格实例详解
2018/10/18 Javascript
从零开始在NPM上发布一个Vue组件的方法步骤
2018/12/20 Javascript
vue从后台渲染文章列表以及根据id跳转文章详情详解
2020/12/14 Vue.js
梳理一下vue中的生命周期
2020/12/30 Vue.js
python函数缺省值与引用学习笔记分享
2013/02/10 Python
Python常用随机数与随机字符串方法实例
2015/04/09 Python
PHP网页抓取之抓取百度贴吧邮箱数据代码分享
2016/04/13 Python
python实现域名系统(DNS)正向查询的方法
2016/04/19 Python
python+matplotlib绘制简单的海豚(顶点和节点的操作)
2018/01/02 Python
Python+OpenCV实现旋转文本校正方式
2020/01/09 Python
关于pycharm 切换 python3.9 报错 ‘HTMLParser‘ object has no attribute ‘unescape‘ 的问题
2020/11/24 Python
anaconda升级sklearn版本的实现方法
2021/02/22 Python
Photobook澳大利亚:制作相片书,婚礼卡,旅行相簿
2017/01/12 全球购物
美国领先的奢侈手表在线零售商:WatchMaxx
2017/12/17 全球购物
澳洲网红粉泥面膜:Sand & Sky
2019/08/13 全球购物
俄罗斯一家时尚女装商店:Charuel
2019/12/04 全球购物
荷兰家电购物网站:Expert.nl
2020/01/18 全球购物
2013年大学生的自我鉴定
2013/10/24 职场文书
主题酒店策划书
2014/01/28 职场文书
创先争优个人承诺书
2014/08/30 职场文书
高中生第一学年自我鉴定2015
2014/09/28 职场文书
财产保全担保书
2015/01/20 职场文书
给医院的感谢信
2015/01/21 职场文书
幼儿园老师个人总结
2015/02/28 职场文书
导游词之桂林山水
2019/09/20 职场文书
实例讲解Python中sys.argv[]的用法
2021/06/03 Python
Mysql查询时间区间日期列表,不会由于数据表数据影响
2022/04/19 MySQL