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 相关文章推荐
firefox中用javascript实现鼠标位置的定位
Jun 17 Javascript
表单元素的submit()方法和onsubmit事件应用概述
Feb 01 Javascript
JS 屏蔽按键效果与改变按键效果的示例代码
Dec 24 Javascript
JavaScript实现找出数组中最长的连续数字序列
Sep 03 Javascript
JS实现简单的二维矩阵乘积运算
Jan 26 Javascript
jQuery实现的简单拖动层示例
Feb 22 Javascript
ES6实现的遍历目录函数示例
Apr 07 Javascript
node.js基础知识小结
Feb 26 Javascript
Javascript Promise用法详解
May 10 Javascript
在vue中使用express-mock搭建mock服务的方法
Nov 07 Javascript
JS实现的杨辉三角【帕斯卡三角形】算法示例
Feb 26 Javascript
vue-cli3.x配置全局的scss的时候报错问题及解决
Apr 30 Vue.js
浅谈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安装攻略:常见问题解答(二)
2006/10/09 PHP
php从完整文件路径中分离文件目录和文件名的方法
2015/03/13 PHP
如何使用PHP给图片加水印
2016/10/12 PHP
PHP 根据key 给二维数组分组
2016/12/09 PHP
jQuery下通过$.browser来判断浏览器.
2011/04/05 Javascript
jquery中ajax学习笔记一
2011/10/16 Javascript
js中更短的 Array 类型转换
2011/10/30 Javascript
基于JS实现EOS隐藏错误提示层代码
2016/04/25 Javascript
jquery mobile 实现自定义confirm确认框效果的简单实例
2016/06/17 Javascript
详解Vue的常用指令v-if, v-for, v-show,v-else, v-bind, v-on
2018/10/12 Javascript
javascript 关于赋值、浅拷贝、深拷贝的个人理解
2019/11/01 Javascript
[02:28]DOTA2亚洲邀请赛附加赛 RECAP赛事回顾
2015/01/29 DOTA
[01:13:17]Secret vs NB 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
Python过滤列表用法实例分析
2016/04/29 Python
VTK与Python实现机械臂三维模型可视化详解
2017/12/13 Python
使用实现XlsxWriter创建Excel文件并编辑
2018/05/04 Python
python开发之anaconda以及win7下安装gensim的方法
2019/07/05 Python
python爬虫 urllib模块url编码处理详解
2019/08/20 Python
详解centos7+django+python3+mysql+阿里云部署项目全流程
2019/11/15 Python
Python + Requests + Unittest接口自动化测试实例分析
2019/12/12 Python
AmazeUI的下载配置与Helloworld的实现
2020/08/19 HTML / CSS
英国最大最好的无人机商店:Drones Direct
2019/07/12 全球购物
高中毕业生自我鉴定
2013/11/03 职场文书
销售行政专员岗位职责
2014/06/10 职场文书
学习计划书怎么写
2014/09/15 职场文书
销售代理协议书
2014/09/30 职场文书
简单租房协议书(范本)
2014/10/13 职场文书
党的群众路线教育实践活动领导班子整改措施
2014/10/28 职场文书
2015年民主生活会发言材料
2014/12/15 职场文书
小学五年级语文上册教学计划
2015/01/22 职场文书
青岛导游词
2015/02/12 职场文书
2015年端午节国旗下演讲稿
2015/03/19 职场文书
小学数学国培研修日志
2015/11/13 职场文书
2016大学生优秀志愿者事迹材料
2016/02/25 职场文书
常用的Python代码调试工具总结
2021/06/23 Python
让JavaScript代码更加精简的方法技巧
2022/06/01 Javascript