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+dom树型菜单类,希望朋友们一起进步
May 03 Javascript
javascript 面向对象继承
Nov 26 Javascript
JavaScript异步回调的Promise模式封装实例
Jun 07 Javascript
一个字符串反转函数可实现字符串倒序
Sep 15 Javascript
javascript实现复制与粘贴操作实例
Oct 16 Javascript
学习javascript面向对象 掌握创建对象的9种方式
Jan 04 Javascript
JS实现区分中英文并统计字符个数的方法示例
Jun 09 Javascript
node.js 微信开发之定时获取access_token
Feb 07 Javascript
Vue Cli3 打包配置并自动忽略console.log语句的方法
Apr 23 Javascript
vue props default Array或是Object的正确写法说明
Jul 30 Javascript
echarts实现晶体球面投影的实例教程
Oct 10 Javascript
一篇文章了解正则表达式的替换技巧
Feb 24 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
第十一节 重载 [11]
2006/10/09 PHP
小偷PHP+Html+缓存
2006/11/25 PHP
一个捕获函数输出的函数
2007/02/14 PHP
深入file_get_contents函数抓取内容失败的原因分析
2013/06/25 PHP
php 使用GD库为页面增加水印示例代码
2014/03/24 PHP
大家须知简单的php性能优化注意点
2016/01/04 PHP
浅谈PHP中的数据传输CURL
2016/09/06 PHP
PHP cookie与session会话基本用法实例分析
2019/11/18 PHP
php设计模式之职责链模式实例分析【星际争霸游戏案例】
2020/03/27 PHP
jQuery 学习 几种常用方法
2009/06/11 Javascript
EXTJS内使用ACTIVEX控件引起崩溃问题的解决方法
2010/03/31 Javascript
Javascript中arguments对象详解
2014/10/22 Javascript
Javascript中prototype属性实现给内置对象添加新的方法
2015/05/14 Javascript
js读取并解析JSON类型数据的方法
2015/11/14 Javascript
jQuery实现每隔几条元素增加1条线的方法
2016/06/27 Javascript
Javascript blur与click冲突解决办法
2017/01/09 Javascript
NodeJS配置HTTPS服务实例分享
2017/02/19 NodeJs
jQuery、layer实现弹出层的打开、关闭功能
2017/06/28 jQuery
vue将后台数据时间戳转换成日期格式
2019/07/31 Javascript
使用apifm-wxapi快速开发小程序过程详解
2019/08/05 Javascript
vue中defineProperty和Proxy的区别详解
2020/11/30 Vue.js
python dict remove数组删除(del,pop)
2013/03/24 Python
Python自动化测试工具Splinter简介和使用实例
2014/05/13 Python
python中argparse模块用法实例详解
2015/06/03 Python
Python中import机制详解
2017/11/14 Python
详解python中asyncio模块
2018/03/03 Python
详解Python3迁移接口变化采坑记
2019/10/11 Python
python3实现在二叉树中找出和为某一值的所有路径(推荐)
2019/12/26 Python
python opencv根据颜色进行目标检测的方法示例
2020/01/15 Python
python批量修改xml属性的实现方式
2020/03/05 Python
使用Keras中的ImageDataGenerator进行批次读图方式
2020/06/17 Python
毕业生的自我评价分享
2013/12/18 职场文书
中班上学期个人总结
2015/02/12 职场文书
导游词之上海豫园
2019/10/24 职场文书
php引用传递
2021/04/01 PHP
redis客户端实现高可用读写分离的方式详解
2021/07/04 Redis