JavaScript函数详解_javascript技巧

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/>");
}

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

时间: 2024-09-27 20:10:41

JavaScript函数详解_javascript技巧的相关文章

JavaScript构造函数详解_javascript技巧

构造函数就是初始化一个实例对象,对象的prototype属性是继承一个实例对象.   构造函数注意事项: 1.默认函数首字母大写 2.构造函数并没有显示返回任何东西.new 操作符会自动创建给定的类型并返回他们,当调用构造函数时,new会自动创建this对象,且类型就是构造函数类型. 3.也可以在构造函数中显示调用return.如果返回的值是一个对象,它会代替新创建的对象实例返回.如果返回的值是一个原始类型,它会被忽略,新创建的实例会被返回.     function Person( name)

javascript中Array()数组函数详解_javascript技巧

在程序语言中数组的重要性不言而喻,JavaScript中数组也是最常使用的对象之一,数组是值的有序集合,由于弱类型的原因,JavaScript中数组十分灵活.强大,不像是Java等强类型高级语言数组只能存放同一类型或其子类型元素,JavaScript在同一个数组中可以存放多种类型的元素,而且是长度也是可以动态调整的,可以随着数据增加或减少自动对数组长度做更改. Array()是一个用来构建数组的内建构造器函数.数组主要由如下三种创建方式: array = new Array() array =

JS高级调试技巧:捕获和分析 JavaScript Error详解_javascript技巧

反正只要 JavaScript 出错后刷新不复现,那用户就可以通过刷新解决问题,浏览器不会崩溃,当没有发生过好了.这种假设在 Single Page App 流行之前还是成立的.现在的 Single Page App 运行一段时间后状态复杂无比,用户可能进行了若干输入操作才来到这里的,说刷新就刷新啊?之前的操作岂不要完全重做?所以我们还是有必要捕获和分析这些异常信息的,然后我们就可以修改代码避免影响用户体验. 捕获异常的方式 我们自己写的 throw new Error() 想要捕获当然可以捕获

js运动事件函数详解_javascript技巧

本文实例为大家分享了js运动事件函数,供大家参考,具体内容如下 HTML <div id="breedsdog"> <h2 class="title">The Dog</h2> <p class="describe">Split between cat,belong to the cat family,cat,cat,is the world's more widely<br> in t

在Node.js中使用Javascript Generators详解_javascript技巧

Generators是Javascript的一种协同程序( coroutine 简称:协程)风格,是指那些可以在执行时暂停然后又恢复的函数,该函数是在functi配以星号符号形式如function* ,函数内有些特征关键词如yield 和yield*. function* generatorFn () { console.log('look ma I was suspended') } var generator = generatorFn() // [1] setTimeout(functio

javascript 闭包详解_javascript技巧

看了一下网上闭包的概念及文章,对于这个问题,自己做一个梳理吧. 问:闭包是什么? 答:闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后. 这个是我自身第一次碰到闭包的问题 <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"/> <title>闭包循环问题</title> <

javascript控制台详解_javascript技巧

一.显示信息的命令 console.log(); //控制台输入 网页中不会输出 console.info(); //一般信息 console.debug(); //除错信息 console.warn(); //警告提示 console.error(); //错误提示 "console.log();" 可以用来取代 "alert();" 或 "document.write();" 比如,在网页中写入 "console.log("

JavaScript多线程详解_javascript技巧

虽然有越来越多的网站在应用AJAX技术进行开发,但是构建一个复杂的AJAX应用仍然是一个难题. 造成这些困难的主要原因是什么呢?是与服务器的异步通信问题?还是GUI程序设计问题呢?通常这两项工作都是由桌面程序来完成的,那究竟为何开发一个可以实现同样功能的AJAX应用就这么困难呢? 大家都知道javascript是单线程执行的,但是又可以通过setTimeout或者setInterval定时执行一个方法,通过Ajax可以在向服务器端发送请求没有收到回应可以继续执行主逻辑.这些是如何做到的呢,下面就

javascript字母大小写转换的4个函数详解_javascript技巧

js中实现字母大小写转换主要用到了四个js函数: 1.toLocaleUpperCase2.toUpperCase3.toLocaleLowerCase4.toLowerCase 下面就这四个实现大小写转换的js函数逐一做简单的分析. 1.toLocaleUpperCase 将字符串中所有的字母字符都将被转换为大写的,同时适应宿主环境的当前区域设置. 2.toUpperCase 将字符串中的所有字母都被转化为大写字母. 3.toLocaleLowerCase 将字符串所有的字母字符都被转换为小写