JS和jquery的几个令人迷惑的问题之四-函数
2014-06-17 21:59
363 查看
1.函数
1.1.函数定义
1.1.1.definition expression vs. declaration statement
函数定义有两种方式
函数定义表达式和函数声明语句
definition expression vs. declaration statement
两者看起来差不多,程序员经常随意使用。其实还是有微妙的差别。
//函数表达式-有时候定义后立即调用
var tensqured=(function(x){return x*x;}(10));
一共发现三个区别.先说第一个,不一样的提前。我们知道js里面变量声明提前的说法。两种方式都创建了新的函数对象, 但函数声明语句的函数名是一个变量名, 变量指向函数对象, 和通过一般的通过var声明了一个变量一样。前者函数定义表达式, 只有变量声明提前了,变量初始化代码仍然在原来的位置,而用函数声明语句创建的函数, 函数名称和函数体均被提前,所以我们可以在声明它之前就使用它。可以使用下面的一段代码去验证这种区别:
再说第二个区别-funcname是否必须
定义表达式一般没有省略了函数名字,有人说fnx不就是名字了吗?我说的是下列格式中的名字funcname
function funcname(parameters)
{
statements;
}
在声明语句中funcname是必须的,而且在上下文范围内都是可见的。
在定义表达式中funcname是可选的,就算提供了funcname,也仅仅只是存在于函数体中,并指代该函数对象本身,这个名称在递归中很有用。
第三个区别-适用场合
就是定义表达式只适用于它作为一个大的表达式的一部分,比如在赋值和调用过程中定义函数。
定义表达式一般来说为了紧凑不提供funcname。
定义表达式特别适合用来定义那些只会用到一次的函数,例子如下:
而对于函数声明语句来说,其实他不算真正的语句。可以出现在全局代码里面,或者内嵌在其他函数中,但是它们不能出现在循环、条件判断、或者try/catch/finally以及with语句中。(EMCASCRIPT规范)1.2.函数调用函数调用 普通的函数调用 调用表达式可以进行普通的函数调用,也可以进行方法调用。方法调用 方法链,方法返回对象自己this,就可以使用方法链。构造函数调用
这个类似于java类的构造函数间接调用 call apply 两个方法都运行显色指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调动,哪怕这个函数不是那个对象的方法。1.2.1.this关键字this是关键字,不是变量,也不是属性名。不允许给this赋值。this没有作用域的限制。嵌套函数中的this如果是作为方法调用,this就是对象。如果嵌套函数作为普通函数调用,其this值不是全局对象就是undefined(strict
mode)。
1.3.函数的形参和实参
1.3.1.可选形参
可选形参放在实参列表的最后。最佳实践是使用/*optional*/来强调形参是可选的。
1.3.2.实参对象-可变长的实参列表
arguments表示实参对象的引用,是类数组对象。
callee和caller
实参对象还有两个属性:callee和caller
callee表示函数自己,caller指代当前正在执行的函数的函数。
通过caller属性可以访问调用栈。
callee属性有些时候很有用,比如匿名函数中通过callee来递归地调用自身。
1.3.3.将对象属性用做实参
使用对象作为实参,而不是很多个实参,这样的好处是不用记住这些好多个实参的次序。
1.3.4.实参类型
js是一种非常灵活的弱类型语言。可以接受任意数量的实参,并可以递归的处理实参是数组的情况。
1.4.作为值的函数
1.4.1.作为值的函数
函数不仅仅是一种语法,也是值。可以将函数赋值给变量,存储在对象的属性或者数组的元素中,作为参数传入另外一个函数。
函数作为一种特殊的对象,也就是说函数可以拥有属性。
当函数需要一个“静态”变量的时候,我们就可以定义这样的函数的属性。
使用“静态”变量的好处是不会污染全局命名空间。
下面是这样做的两个例子:
1.5.作为命名空间的函数
现在开始进入魔术时刻
据说是为了简化写法,他的等价写法如下:
下面的代码才是魔术代码,相信你同意这一点。
他的目的是当然不仅仅是为了展示命名空间技术。它定义了一个返回entend()函数的匿名函数,这就是在例6-2的时候提到的更加健壮的extend版本。
下面匿名函数里面的代码,检测了一个IE bug,如果出现了这个bug,就返回一个带补丁的函数版本,匿名函数命名空间用来隐藏了一组属性名。
另外,匿名函数立即执行,返回的实际上是一个函数对象。
我不知道js里面到底有多少潜力,或者说是有多少坑在前方潜伏。
我需要细细学习js基础知识,才敢于说自己在使用js的时候少犯错,另外看得懂别人的代码,或者说我看到的高手的代码都是这样子的。
今天终于看懂了,有收获,真高兴。
下面还有呢!!!
1.6.闭包 closures
函数对象和作用域关联,嵌套的函数对象和一个作用域链关联,变量和作用域的绑定,这种特性在计算机科学文献里面被称为闭包closures.
1.6.1.
闭包的某个强大特性:他们可以捕捉到局部变量(和参数),并一直保存下来。
作用域链描述为一个对象链表,而不是绑定的栈。
理解了闭包和垃圾回收的关系,有利于理解闭包的这个特性。
返回的结果还是local scope
1.6.2.闭包的应用-私有化函数对象的“静态”属性
不是说lamda表达式类似于java中的匿名内部类吗?难道闭包也是有类似的作用?
私有变量不是只能用在一个单独的闭包内,还可以定义多个嵌套函数访问他。
下面是利用闭包实现的私有属性存取器方法
1.7.1.函数属性:
length
prototype
函数方法:
call()
apply()
下面演示了monkey-patching,能够将指定的方法替换为一个新方法,新方法其实是包裹了原有方法,这种动态修改已有方法的做法就是monkey-patching.
1.7.3.bind()方法
主要作用是将某个函数绑定到某个对象。
function.bind(o,args,...)
bind函数还可以绑定this,bind第二个参数后面的参数也会被用到。
1.7.4.toString()方法
1.7.5.Function()构造函数
实际编程中很少使用
1.7.6.可调用的对象
callable object
所有的函数都是可调用的,但并非所有可调用对象都是函数。
IE web(<=ie8)中的客户端方法(如Window.alert()和Document.getElementById()),使用了可调用的宿主对象,而不是内置函数对象。
本质上不是Function对象,IE9将他们实现为真正的真正的函数,因此这种可调用对象将会越来越少了。
下面这个函数可以用来检测x是否真正的函数对象。
js并非lisp,haskell那样的函数式编程语言。
但是在js中可以像操控对象那样操控函数,所以可以使用函数式编程技术,比如数组方法中的map()和reduce()就是非常适合函数式编程风格。
其实已经有了函数式javascript库了。
1.8.1.使用函数处理数组
求数组元素的平均值和标准差,下面一段代码是传统的写法:
下面是使用map,reduce函数式编程风格
1.8.2.高阶函数(higher-order function)
所谓高阶函数,就是把函数作为参数和返回值的函数。
接受一个或者多个函数作为函数的参数,并且返回一个新函数。
mapper可以将一个一般的函数映射为一个操作数组的函数。
而map本身就是操作数组的函数了。
下面是一个常见的高阶函数例子-计算f(g(...))
1.8.3.不完全函数(partial function)
partial application不完全调用
函数变换技巧:
将一个完整的函数调用拆分成多个函数调用,每次传入的都是完整实参的一部分,每次拆分开的函数叫做不完全函数,每次调用叫做不完全调用。
这种函数变换的特点是,每次不完全调用都返回一个函数,直到得到最终结果为止。
1.8.4.记忆
缓存
不完全函数和记忆估计暂时用不到细细研究,以后再说吧。
有时候不完全函数又叫做curring
http://www.cnblogs.com/rubylouvre/archive/2009/11/09/1598761.html
1.1.函数定义
1.1.1.definition expression vs. declaration statement
函数定义有两种方式
函数定义表达式和函数声明语句
definition expression vs. declaration statement
两者看起来差不多,程序员经常随意使用。其实还是有微妙的差别。
// 函数定义表达式-在赋值表达式中使用 var fnx=function(str) { console.log(str+ ' from fnx'); }; //函数定义表达式-调用过程中使用定义表达式,<pre name="code" class="javascript">//这里的函数作为一个参数data.sort(function(a,b){return a-b;})
//函数表达式-有时候定义后立即调用
var tensqured=(function(x){return x*x;}(10));
// 函数声明语句 function fn(str) { console.log(str); };
一共发现三个区别.先说第一个,不一样的提前。我们知道js里面变量声明提前的说法。两种方式都创建了新的函数对象, 但函数声明语句的函数名是一个变量名, 变量指向函数对象, 和通过一般的通过var声明了一个变量一样。前者函数定义表达式, 只有变量声明提前了,变量初始化代码仍然在原来的位置,而用函数声明语句创建的函数, 函数名称和函数体均被提前,所以我们可以在声明它之前就使用它。可以使用下面的一段代码去验证这种区别:
console.log(typeof (fn)); // function fn("abc"); // abc console.log(typeof (fnx)); // undefined if (fnx) fnx("abc"); // will not execute else console.log("fnx is undefined"); // fnx is undefined // 函数声明语句 function fn(str) { console.log(str); }; // 函数定义表达式 var fnx = function(str) { console.log(str + " from fnx"); };
再说第二个区别-funcname是否必须
定义表达式一般没有省略了函数名字,有人说fnx不就是名字了吗?我说的是下列格式中的名字funcname
function funcname(parameters)
{
statements;
}
在声明语句中funcname是必须的,而且在上下文范围内都是可见的。
在定义表达式中funcname是可选的,就算提供了funcname,也仅仅只是存在于函数体中,并指代该函数对象本身,这个名称在递归中很有用。
第三个区别-适用场合
就是定义表达式只适用于它作为一个大的表达式的一部分,比如在赋值和调用过程中定义函数。
定义表达式一般来说为了紧凑不提供funcname。
定义表达式特别适合用来定义那些只会用到一次的函数,例子如下:
<pre name="code" class="javascript">//函数定义表达式-调用过程中使用定义表达式,//这里的函数作为一个参数data.sort(function(a,b){return a-b;})//函数表达式-有时候定义后立即调用var tensqured=(function(x){return x*x;}(10));
而对于函数声明语句来说,其实他不算真正的语句。可以出现在全局代码里面,或者内嵌在其他函数中,但是它们不能出现在循环、条件判断、或者try/catch/finally以及with语句中。(EMCASCRIPT规范)1.2.函数调用函数调用 普通的函数调用 调用表达式可以进行普通的函数调用,也可以进行方法调用。方法调用 方法链,方法返回对象自己this,就可以使用方法链。构造函数调用
这个类似于java类的构造函数间接调用 call apply 两个方法都运行显色指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调动,哪怕这个函数不是那个对象的方法。1.2.1.this关键字this是关键字,不是变量,也不是属性名。不允许给this赋值。this没有作用域的限制。嵌套函数中的this如果是作为方法调用,this就是对象。如果嵌套函数作为普通函数调用,其this值不是全局对象就是undefined(strict
mode)。
var o = { // An object o. m: function() { // Method m of the object. var self = this; // Save the this value in a variable. console.log(this === o); // Prints "true": this is the object o. f(); // Now call the helper function f(). function f() { // A nested function f console.log(this === o); // "false": this is global or undefined console.log(self === o); // "true": self is the outer this value. } } }; o.m(); // Invoke the method m on the object o.
1.3.函数的形参和实参
1.3.1.可选形参
可选形参放在实参列表的最后。最佳实践是使用/*optional*/来强调形参是可选的。
// Append the names of the enumerable properties of object o to the // array a, and return a. If a is omitted, create and return a new array. function getPropertyNames(o, /* optional */ a) { if (a === undefined) a = []; // If undefined, use a new array for(var property in o) a.push(property); return a; } // This function can be invoked with 1 or 2 arguments: var a = getPropertyNames(o); // Get o's properties into a new array getPropertyNames(p,a); // append p's properties to that array
1.3.2.实参对象-可变长的实参列表
arguments表示实参对象的引用,是类数组对象。
function max(/* ... */) { var max = Number.NEGATIVE_INFINITY; // Loop through the arguments, looking for, and remembering, the biggest. for(var i = 0; i < arguments.length; i++) if (arguments[i] > max) max = arguments[i]; // Return the biggest return max; } var largest = max(1, 10, 100, 2, 3, 1000, 4, 5, 10000, 6); // => 10000
callee和caller
实参对象还有两个属性:callee和caller
callee表示函数自己,caller指代当前正在执行的函数的函数。
通过caller属性可以访问调用栈。
callee属性有些时候很有用,比如匿名函数中通过callee来递归地调用自身。
1.3.3.将对象属性用做实参
使用对象作为实参,而不是很多个实参,这样的好处是不用记住这些好多个实参的次序。
// Copy length elements of the array from to the array to. // Begin copying with element from_start in the from array // and copy that element to to_start in the to array. // It is hard to remember the order of the arguments. function arraycopy(/* array */ from, /* index */ from_start, /* array */ to, /* index */ to_start, /* integer */ length) { // code goes here } // This version is a little less efficient, but you don't have to // remember the order of the arguments, and from_start and to_start // default to 0. function easycopy(args) { arraycopy(args.from, args.from_start || 0, // Note default value provided args.to, args.to_start || 0, args.length); } // Here is how you might invoke easycopy(): var a = [1,2,3,4], b = []; easycopy({from: a, to: b, length: 4});
1.3.4.实参类型
js是一种非常灵活的弱类型语言。可以接受任意数量的实参,并可以递归的处理实参是数组的情况。
// Return the sum of the elements of array (or array-like object) a. // The elements of a must all be numbers or null and undefined are ignored. function sum(a) { if (isArrayLike(a)) { var total = 0; for(var i = 0; i < a.length; i++) { // Loop though all elements var element = a[i]; if (element == null) continue; // Skip null and undefined if (isFinite(element)) total += element; else throw new Error("sum(): elements must be finite numbers"); }//end of for loop return total; } else throw new Error("sum(): argument must be array-like"); }
1.4.作为值的函数
1.4.1.作为值的函数
函数不仅仅是一种语法,也是值。可以将函数赋值给变量,存储在对象的属性或者数组的元素中,作为参数传入另外一个函数。
Example 8-2. Using functions as data // We define some simple functions here function add(x,y) { return x + y; } function subtract(x,y) { return x - y; } function multiply(x,y) { return x * y; } function divide(x,y) { return x / y; } // Here's a function that takes one of the above functions // as an argument and invokes it on two operands function operate(operator, operand1, operand2) { return operator(operand1, operand2); } // We could invoke this function like this to compute the value (2+3) + (4*5): var i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5)); // For the sake of the example, we implement the simple functions again, // this time using function literals within an object literal; var operators = { add: function(x,y) { return x+y; }, subtract: function(x,y) { return x-y; }, multiply: function(x,y) { return x*y; }, divide: function(x,y) { return x/y; }, pow: Math.pow // Works for predefined functions too }; // This function takes the name of an operator, looks up that operator // in the object, and then invokes it on the supplied operands. Note // the syntax used to invoke the operator function. function operate2(operation, operand1, operand2) { if (typeof operators[operation] === "function") return operators[operation](operand1, operand2); else throw "unknown operator"; } // Compute the value ("hello" + " " + "world") like this: var j = operate2("add", "hello", operate2("add", " ", "world")); // Using the predefined Math.pow() function: var k = operate2("pow", 10, 2);1.4.2.自定义函数属性
函数作为一种特殊的对象,也就是说函数可以拥有属性。
当函数需要一个“静态”变量的时候,我们就可以定义这样的函数的属性。
使用“静态”变量的好处是不会污染全局命名空间。
下面是这样做的两个例子:
// Initialize the counter property of the function object. // Function declarations are hoisted so we really can // do this assignment before the function declaration. uniqueInteger.counter = 0; // This function returns a different integer each time it is called. // It uses a property of itself to remember the next value to be returned. function uniqueInteger() { return uniqueInteger.counter++; // Increment and return counter property } // Compute factorials and cache results as properties of the function itself. function factorial(n) { if (isFinite(n) && n>0 && n==Math.round(n)) { // Finite, positive ints only if (!(n in factorial)) // If no cached result factorial = n * factorial(n-1); // Compute and cache it return factorial ; // Return the cached result } else return NaN; // If input was bad } factorial[1] = 1; // Initialize the cache to hold this base case.
1.5.作为命名空间的函数
现在开始进入魔术时刻
(function() { // mymodule function rewritten as an unnamed expression // Module code goes here. }()); // end the function literal and invoke it now.匿名函数的定义表达式后面立即跟上(),就是说立即调用,另外这个函数定义表达式外面还加上一对(),为什么要这么写呢?
据说是为了简化写法,他的等价写法如下:
function mymodule() { // Module code goes here. // Any variables used by the module are local to this function // instead of cluttering up the global namespace. } mymodule(); // But don't forget to invoke the function!这样做的目的,是为了模块化,定义一个函数用做临时的命名空间,在这个命名空间内定义的变量都不会污染到全局命名空间。
下面的代码才是魔术代码,相信你同意这一点。
他的目的是当然不仅仅是为了展示命名空间技术。它定义了一个返回entend()函数的匿名函数,这就是在例6-2的时候提到的更加健壮的extend版本。
下面匿名函数里面的代码,检测了一个IE bug,如果出现了这个bug,就返回一个带补丁的函数版本,匿名函数命名空间用来隐藏了一组属性名。
另外,匿名函数立即执行,返回的实际上是一个函数对象。
Example 8-3. The extend() function, patched if necessary // Define an extend function that copies the properties of its second and // subsequent arguments onto its first argument. // We work around an IE bug here: in many versions of IE, the for/in loop // won't enumerate an enumerable property of o if the prototype of o has // a nonenumerable property by the same name. This means that properties // like toString are not handled correctly unless we explicitly check for them. var extend = (function() { // Assign the return value of this function // First check for the presence of the bug before patching it. for(var p in {toString:null}) { // If we get here, then the for/in loop works correctly and we return // a simple version of the extend() function return function extend(o) { for(var i = 1; i < arguments.length; i++) { var source = arguments[i]; for(var prop in source) o[prop] = source[prop]; } return o; }; } // If we get here, it means that the for/in loop did not enumerate // the toString property of the test object. So return a version // of the extend() function that explicitly tests for the nonenumerable // properties of Object.prototype. return function patched_extend(o) { for(var i = 1; i < arguments.length; i++) { var source = arguments[i]; // Copy all the enumerable properties for(var prop in source) o[prop] = source[prop]; // And now check the special-case properties for(var j = 0; j < protoprops.length; j++) { prop = protoprops[j]; if (source.hasOwnProperty(prop)) o[prop] = source[prop]; } } return o; }; // This is the list of special-case properties we check for var protoprops = ["toString", "valueOf", "constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable","toLocaleString"]; }());其实这也是我现在细细学习js基础知识的原因,经常看到这种魔术代码,影响我的自信心。
我不知道js里面到底有多少潜力,或者说是有多少坑在前方潜伏。
我需要细细学习js基础知识,才敢于说自己在使用js的时候少犯错,另外看得懂别人的代码,或者说我看到的高手的代码都是这样子的。
今天终于看懂了,有收获,真高兴。
下面还有呢!!!
1.6.闭包 closures
函数对象和作用域关联,嵌套的函数对象和一个作用域链关联,变量和作用域的绑定,这种特性在计算机科学文献里面被称为闭包closures.
1.6.1.
闭包的某个强大特性:他们可以捕捉到局部变量(和参数),并一直保存下来。
作用域链描述为一个对象链表,而不是绑定的栈。
理解了闭包和垃圾回收的关系,有利于理解闭包的这个特性。
var scope = "global scope"; // A global variable function checkscope() { var scope = "local scope"; // A local variable function f() { return scope; } // Return the value in scope here return f(); } checkscope() // => "local scope"下面这段代码,就说明了闭包的这个特性:
var scope = "global scope"; // A global variable function checkscope() { var scope = "local scope"; // A local variable function f() { return scope; } // Return the value in scope here return f; } checkscope()() // What does this return?这里返回的是f()这个函数对象,而不是f函数的执行结果。
返回的结果还是local scope
1.6.2.闭包的应用-私有化函数对象的“静态”属性
var uniqueInteger = (function() { // Define and invoke 定义匿名函数并且立即调用,执行得到的是一个拥有私有状态的函数对象 var counter = 0; // Private state of function below 函数的私有状态 return function() { return counter++; }; }());如果你属性java的话,是不是觉得这个有点类似于java中的匿名内部类?
不是说lamda表达式类似于java中的匿名内部类吗?难道闭包也是有类似的作用?
私有变量不是只能用在一个单独的闭包内,还可以定义多个嵌套函数访问他。
function counter() { var n = 0; return { count: function() { return n++; }, reset: function() { n = 0; } }; } var c = counter(), d = counter(); // Create two counters c.count() // => 0 d.count() // => 0: they count independently c.reset() // reset() and count() methods share state c.count() // => 0: because we reset c d.count() // => 1: d was not reset 我们看到,每次调用counter()都会创建一个新的作用域和一个新的私有变量。这有些类似于java中的私有静态变量,但是又不完全是,因为静态变量在不同的类实例之间的共享的,也就是说他们是互相影响的。
下面是利用闭包实现的私有属性存取器方法
Example 8-4. Private property accessor methods using closures // This function adds property accessor methods for a property with // the specified name to the object o. The methods are named get<name> // and set<name>. If a predicate function is supplied, the setter // method uses it to test its argument for validity before storing it. // If the predicate returns false, the setter method throws an exception. // // The unusual thing about this function is that the property value // that is manipulated by the getter and setter methods is not stored in // the object o. Instead, the value is stored only in a local variable // in this function. The getter and setter methods are also defined // locally to this function and therefore have access to this local variable. // This means that the value is private to the two accessor methods, and it // cannot be set or modified except through the setter method. function addPrivateProperty(o, name, predicate) { var value; // This is the property value // The getter method simply returns the value. o["get" + name] = function() { return value; }; // The setter method stores the value or throws an exception if // the predicate rejects the value. o["set" + name] = function(v) { if (predicate && !predicate(v)) throw Error("set" + name + ": invalid value " + v); else value = v; }; } // The following code demonstrates the addPrivateProperty() method. var o = {}; // Here is an empty object // Add property accessor methods getName and setName() // Ensure that only string values are allowed addPrivateProperty(o, "Name", function(x) { return typeof x == "string"; }); o.setName("Frank"); // Set the property value console.log(o.getName()); // Get the property value o.setName(0); // Try to set a value of the wrong type1.7.函数属性、方法和构造函数
1.7.1.函数属性:
length
prototype
// This function uses arguments.callee, so it won't work in strict mode. function check(args) { var actual = args.length; // The actual number of arguments var expected = args.callee.length; // The expected number of arguments if (actual !== expected) // Throw an exception if they differ. throw Error("Expected " + expected + "args; got " + actual); } function f(x, y, z) { check(arguments); // Check that the actual # of args matches expected #. return x + y + z; // Now do the rest of the function normally. }1.7.2.
函数方法:
call()
apply()
下面演示了monkey-patching,能够将指定的方法替换为一个新方法,新方法其实是包裹了原有方法,这种动态修改已有方法的做法就是monkey-patching.
// Replace the method named m of the object o with a version that logs // messages before and after invoking the original method. function trace(o, m) { var original = o[m]; // Remember original method in the closure. o[m] = function() { // Now define the new method. console.log(new Date(), "Entering:", m); // Log message. var result = original.apply(this, arguments); // Invoke original. console.log(new Date(), "Exiting:", m); // Log message. return result; // Return result. }; }apply是call的数组版本(后面的参数是用了数组)
1.7.3.bind()方法
主要作用是将某个函数绑定到某个对象。
function f(y) { return this.x + y; } // This function needs to be bound var o = { x : 1 }; // An object we'll bind to var g = f.bind(o); // Calling g(x) invokes o.f(x) g(2) // => 3
function.bind(o,args,...)
bind函数还可以绑定this,bind第二个参数后面的参数也会被用到。
var sum = function(x,y) { return x + y }; // Return the sum of 2 args // Create a new function like sum, but with the this value bound to null // and the 1st argument bound to 1. This new function expects just one arg. //创建一个新函数类似于sum,但是this的值绑定到null, <pre name="code" class="javascript">//第一个参数x绑定到1,所以新函数只需要一个实参了(对应于sum中的形参y) var succ = sum.bind(null, 1); succ(2) // => 3: x is bound to 1, and we pass 2 for the y argument function f(y,z) { return this.x + y + z }; // Another function that adds var g = f.bind({x:1}, 2); // Bind this and y g(3) // => 6: this.x is bound to 1, y is bound to 2 and z is 3
1.7.4.toString()方法
1.7.5.Function()构造函数
实际编程中很少使用
1.7.6.可调用的对象
callable object
所有的函数都是可调用的,但并非所有可调用对象都是函数。
IE web(<=ie8)中的客户端方法(如Window.alert()和Document.getElementById()),使用了可调用的宿主对象,而不是内置函数对象。
本质上不是Function对象,IE9将他们实现为真正的真正的函数,因此这种可调用对象将会越来越少了。
下面这个函数可以用来检测x是否真正的函数对象。
function isFunction(x) { return Object.prototype.toString.call(x) === "[object Function]"; }1.8.函数式编程
js并非lisp,haskell那样的函数式编程语言。
但是在js中可以像操控对象那样操控函数,所以可以使用函数式编程技术,比如数组方法中的map()和reduce()就是非常适合函数式编程风格。
其实已经有了函数式javascript库了。
1.8.1.使用函数处理数组
求数组元素的平均值和标准差,下面一段代码是传统的写法:
var data = [1,1,3,5,5]; // This is our array of numbers // The mean is the sum of the elements divided by the number of elements var total = 0; for(var i = 0; i < data.length; i++) total += data[i]; var mean = total/data.length; // The mean of our data is 3 // To compute the standard deviation, we first sum the squares of // the deviation of each element from the mean. total = 0; for(var i = 0; i < data.length; i++) { var deviation = data[i] - mean; total += deviation * deviation; } var stddev = Math.sqrt(total/(data.length-1)); // The standard deviation is 2
下面是使用map,reduce函数式编程风格
// First, define two simple functions var sum = function(x,y) { return x+y; }; var square = function(x) { return x*x; }; // Then use those functions with Array methods to compute mean and stddev var data = [1,1,3,5,5]; var mean = data.reduce(sum)/data.length; var deviations = data.map(function(x) {return x-mean;}); var stddev = Math.sqrt(deviations.map(square).reduce(sum)/(data.length-1));EMCASCRIPT5引入了map,reduce等等函数。下面是EMCASCRIPT3下面对这两个函数的模拟实现。
// Call the function f for each element of array a and return // an array of the results. Use Array.prototype.map if it is defined. var map = Array.prototype.map? function(a, f) { return a.map(f); } // Use map method if it exists : function(a,f) { // Otherwise, implement our own var results = []; for(var i = 0, len = a.length; i < len; i++) { if (i in a) results[i] = f.call(null, a[i], i, a); } return results; }; // Reduce the array a to a single value using the function f and // optional initial value. Use Array.prototype.reduce if it is defined. var reduce = Array.prototype.reduce? function(a, f, initial) { // If the reduce() method exists.如果reduce函数已经存在 if (arguments.length > 2) return a.reduce(f, initial); // If an initial value was passed.如果传入一个初始值 else return a.reduce(f); // Otherwise, no initial value. } : function(a, f, initial) { // This algorithm from the ES5 specification var i = 0, len = a.length, accumulator; // Start with the specified initial value, or the first value in a if (arguments.length > 2) accumulator = initial; else { // Find the first defined index in the array if (len == 0) throw TypeError(); while(i < len) { if (i in a) { accumulator = a[i++]; break; } else i++; } if (i == len) throw TypeError(); } // Now call f for each remaining element in the array while(i < len) { if (i in a) accumulator = f.call(undefined, accumulator, a[i], i, a); i++; } return accumulator; }; //下面是使用上面定义的全局函数map和reduce来计算数组的平均值和标准差 var data = [1,1,3,5,5]; var sum = function(x,y) { return x+y; }; var square = function(x) { return x*x; }; var mean = reduce(data, sum)/data.length; var deviations = map(data, function(x) {return x-mean;}); var stddev = Math.sqrt(reduce(map(deviations, square), sum)/(data.length-1));
1.8.2.高阶函数(higher-order function)
所谓高阶函数,就是把函数作为参数和返回值的函数。
接受一个或者多个函数作为函数的参数,并且返回一个新函数。
// This higher-order function returns a new function that passes its // arguments to f and returns the logical negation of f's return value; //高阶函数返回一个新函数,这个新函数把实参传递给f,将f的结果做逻辑非返回 function not(f) { return function() { // Return a new function var result = f.apply(this, arguments); // that calls f return !result; // and negates its result. }; } var even = function(x) { // A function to determine if a number is even return x % 2 === 0; }; var odd = not(even); // A new function that does the opposite [1,1,3,5,5].every(odd); // => true: every element of the array is odd
// Return a function that expects an array argument and applies f to // each element, returning the array of return values. // Contrast this with the map() function from earlier. function mapper(f) { return function(a) { return map(a, f); }; } var increment = function(x) { return x+1; }; var incrementer = mapper(increment); incrementer([1,2,3]) // => [2,3,4]mapper和先前的map有和不同?
mapper可以将一个一般的函数映射为一个操作数组的函数。
而map本身就是操作数组的函数了。
下面是一个常见的高阶函数例子-计算f(g(...))
// Return a new function that computes f(g(...)).返回一个新函数,计算f(g(...)) // The returned function h passes all of its arguments to g, and then passes // the return value of g to f, and then returns the return value of f. // Both f and g are invoked with the same this value as h was invoked with. function compose(f,g) { return function() { // We use call for f because we're passing a single value and // apply for g because we're passing an array of values. return f.call(this, g.apply(this, arguments)); }; } var square = function(x) { return x*x; }; var sum = function(x,y) { return x+y; }; var squareofsum = compose(square, sum); squareofsum(2,3) // => 25
1.8.3.不完全函数(partial function)
partial application不完全调用
函数变换技巧:
将一个完整的函数调用拆分成多个函数调用,每次传入的都是完整实参的一部分,每次拆分开的函数叫做不完全函数,每次调用叫做不完全调用。
这种函数变换的特点是,每次不完全调用都返回一个函数,直到得到最终结果为止。
1.8.4.记忆
缓存
不完全函数和记忆估计暂时用不到细细研究,以后再说吧。
有时候不完全函数又叫做curring
http://www.cnblogs.com/rubylouvre/archive/2009/11/09/1598761.html
相关文章推荐
- JS和jquery的几个令人迷惑的问题之三-数组
- JS和jquery的几个令人迷惑的问题之一-类型、值、变量、运算符和表达式
- JS和jquery的几个令人迷惑的问题之二(语句和对象)
- JS和jquery的几个令人迷惑的问题之五-类和模块
- js jquery ajax 获取函数返回值问题
- 几个优秀的js框架的license问题,付费,版权,侵权(extj,jquery,YUI,coolite)
- CSS盒模型里面的几个宽度问题:offsetWidth,clientWidth,scrollWidth以及jquery的.width()函数
- VC++令人迷惑的几个问题。。。
- 页面中使用已经引入的jquery插件,结果却提示JS报错找不到函数【问题解决】
- 几个优秀的js框架的license问题,付费,版权,侵权(extj,jquery,YUI,coolite)
- 几个优秀的js框架的license问题,付费,版权,侵权(extj,jquery,YUI,coolite)
- 使用prototype.js 的时候应该特别注意的几个问题.
- ajax提交中文编码问题(同时给出几个js与php编码方式)
- 常用的几个JS验证函数
- 干了几天的QA工作,迷惑的几个问题和建议
- jQuery 插件form.js在gb2312中的乱码问题
- ajax提交中文编码问题(同时给出几个js与php编码方式)
- BS中开发过程中的几个常用js函数
- PJBLOG中用到的ajaxjs.几个简单的函数
- MFC DDE开发中令人迷惑的问题?