原文http://pij.robinqu.me/

通过call和apply间接调用函数(改变this)

call 和 apply带有多个参数,call和apply把当前函数的this指向第一个参数给定的函数或对象中,并传递其余所有的参数作为当前函数的参数。

  1.  
  1. var O = function () {
  2. this.foo = 'hello';
  3. this.hello = function () {
  4. return 'world';
  5. }
  6. };
  7.  
  8. var fn = function () {
  9. console.log('call', this);
  10. };
  11.  
  12. var o = new O();
  13. fn.call(o);//此时fn的this指向o
  1. callapply的不同之处,在于call传递的参数是作为arguments依次传入的,例如

fn.call(o, 1, 2, 3);
而apply传递的参数是以一个数组的方式传入的,例如
fn.apply(o, [1, 2, 3]);

参数

当传入参数少于函数声明的参数时,留空的参数的值是undefined

JavaScript允许传入参数的个数大于声明时制定的参数个数。可以用arguments来访问这些参数

  1.  
  1. function f(){
  2. var i;
  3. for( i = 0; i < arguments.length ; i++) {
  4. console.log(arguments[i]);
  5. }
  6. }
  7.  
  8. f(1,2,3,4,5,6);
  1. 函数通过取得arguments的长度得到传入参数的个数,使用一个循环获取每一个参数。

arguments还有两个属性,calleecaller
callee表示正在执行的function对象,
caller表示调用当前function的function

例如

  1. function f(){
  2. console.log(arguments.callee);//[Function: f]
  3. console.log(arguments.callee.caller);[Function: g]
  4. var i;
  5. for( i = 0; i < arguments.length ; i++) {
  6. console.log(arguments[i]);
  7. }
  8. }
  9.  
  10. function g(){
  11. f(1,2,3,4,5,6);
  12. }
  13.  
  14. g();
  15. callee 的重要用法之一是在匿名函数中实现递归
  16. var result = function (x) {
  17. if (x <= 1) return 1;
  18. return x * arguments.callee(x - 1);
  19. }(3);
  20.  
  21. console.log(result);

上例使用了一个匿名函数和callee实现了一个阶乘。

作为值的函数

javascript中的函数可以作为值来传递,这种赋值方式可以认为是传内存地址

  1. function square(x) {
  2. return x * x;
  3. }
  4. var s = square;
  5. s(4);

作为命名空间的函数

  1. (function() {
  2. }());

闭包

JavaScript函数对象的内部状态不仅包含着函数的代码逻辑,还引用当前的作用域链。函数对象通过作用域链相互关联起来,函数体内部变量包含在函数作用域内,这就叫闭包。

例如

  1.  
  1. var scope = 'global scope';
  2. function checkscope() {
  3. var scope = 'local scope';
  4. function f() {
  5. return scope;
  6. }
  7. return f;
  8. }
  9.  
  10. checkscope()();
  1. 这段checkscope声明了一个局部变量,定义了一个函数f,函数f返回了这个局部变量的值,最后返回了这个函数f。在定义函数f的作用域外调用f,得到的返回仍然是函数f创建时所在的作用域的局部变量scope

又例如

  1. var counter = (function() {
  2. var count = 0;
  3. return function () {
  4. return count++ ;
  5. }
  6. }());

代码定义了一个立即执行函数并返回给counter,这个函数定义了一个局部变量count,返回了一个子函数,该子函数每次调用,都会吧count加一并返回。

闭包的注意事项

观察下面的示例:

  1.  
  1. var add_the_handlers = function (nodes) {
  2. var i;
  3. for (i = 0; i < nodes.length; i += 1) {
  4. nodes[i].onclick = function (e) {
  5. alert(i);
  6. };
  7. }
  8. };
  1. 这个函数期望的结果,是在运行的时候为每个nodeonclick的时候alert出各自的序号,但是实际运行的结果却不同:所有的node在单击的时候alert出来的数字总是同一个。

这是因为alert所在的匿名函数的闭包中存放的i是第一行的i,而不是在循环中获得的i的当前值。

所以如果希望达到预期结果,应该在循环中创建多个闭包,在闭包中存放当前循环的i的值:

  1.  
  1. var add_the_handlers = function (nodes) {
  2. var i;
  3. for (i = 0; i < nodes.length; i += 1) {
  4. nodes[i].onclick = function (i) {
  5. return function(e){
  6. alert(e);
  7. };
  8. }(i);
  9. }
  10. };

这里使用一个立即执行函数并传递当前的i的值,返回一个新生成的函数。在这个新生成的函数的闭包中就保存了当前的i的值。

函数中的this对象

在一个对象中的this始终引用当前对象,但是在函数中,特别是在闭包中,this有一些特殊的行为。

函数中的this对象始终绑定在函数运行时的上下文环境上。所以在普通模式下调用一个全局函数,this始终指向window(客户端),在严格模式下调用一个全局函数,this始终是undefined

示例

  1.  
  1. var name = "The Window";
  2. var object = {
  3. name: "My Object",
  4. getNameFunc: function () {
  5. return function () {
  6. return this.name;
  7. };
  8. },
  9. getName : function () {
  10. return this.name;
  11. }
  12. };
  13.  
  14. console.log(object.getNameFunc()());
  15. console.log(object.getName());
  1.  
  1. getNameFunction()返回了一个匿名函数,这个匿名函数在调用的时候,上下文是window(浏览器中),所以在浏览器中输出的是the Window

而getName()调用的时候上下文是object,所以成功输出object的name

其实以上代码中
object.getNameFunc()()
等效于
var fnc = object.getNameFunc();//这时候的fnc已经脱离了object对象
fnc();

所以如果想要getNameFunction()正确返回Object的Name,需要在返回的匿名函数的闭包中保存在函数声明时的this,

  1.  
  1. getNameFunc: function () {
  2. var that = this;
  3. return function () {
  4. return that.name;
  5. };
  6. },
  1. 这样就可以了。。

函数柯里化

函数柯里化是指,把接受多个参数的函数转换成接受一个单一参数的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

示例

  1. var add1 = add.curry(1);
  2. console.log(add1(2));

其中,add是接受两个参数的函数,add调用了curry返回一个只接受一个参数的新函数,之后调用add1便等效于调用add(1, 2);

javascript并不原生支持curry,可以用prototype来模拟

  1.  
  1. Function.prototype.curry = function () {
  2. var slice = Array.prototype.slice,
  3. args = slice.apply(arguments),
  4. that = this;
  5. return function () {
  6. return that.apply(null, args.concat(slice.apply(arguments)));
  7. };
  8. };
  9.  
  10. function add(n1, n2) {
  11. return n1 + n2;
  12. }
  13.  
  14. var add1 = add.curry(1);
  15. console.log(add1(2));
  1. curry创建了一个新函数,在新函数的闭包中保存了原先传递的参数。

函数的属性和方法

  length 函数的length表示函数实参的数量,是只读的

  prototype 指向一个该函数的原型对象的引用

  toString 返回一个字符串

javascript基础(五)函数的更多相关文章

  1. JavaScript 基础(五) 函数 变量和作用域

    函数定义和调用 定义函数,在JavaScript中,定义函数的方式如下: function abs(x){ if(x >=0){ return x; }else{ return -x; } } ...

  2. (Frontend Newbie)JavaScript基础之函数

    函数可以说是任何一门编程语言的核心概念.要能熟练掌握JavaScript,对于函数及其相关概念的学习是非常重要的一步.本篇从函数的基本知识.执行环境与作用域.闭包.this关键字等方面简单介绍Java ...

  3. JavaScript基础学习-函数及作用域

    函数和作用域是JavaScript的重要组成部分,我们在使用JavaScript编写程序的过程中经常要用到这两部分内容,作为初学者,我经常有困惑,借助写此博文来巩固下之前学习的内容. (一)JavaS ...

  4. JavaScript 基础回顾——函数

    在JavaScript中,函数也是一种数据类型,属于 function 类型,所以使用Function关键字标识函数名.函数可以在大括号内编写代码并且被调用,作为其他函数的参数或者对象的属性值. 1. ...

  5. Javascript 基础--JS函数(三)

    一.基本概念:未完成某一个功能的代码(语句,指令)的集合. 二.函数的调用方式: 2.1.函数名(传递参数1,传递参数2)   基本语法 function 函数名(参数列表){ //代码; retur ...

  6. javascript基础知识-函数

    1.javascript中函数有两种定义方式: 函数语句定义和表达式定义 //函数有定义 function test(){ console.log("This is a function&q ...

  7. JavaScript基础——创建函数

    JavaScript的最重要的一个部分是制作其他代码可以重用的代码.要做到这一点,你可以把代码组织成执行特定任务的函数.函数是结合在一个单一的块中,并给予一个名称的一系列代码语句.然后,你就可以通过引 ...

  8. JavaScript基础之函数与数组

     函数    函数的基本概念 为完成某一功能的程序指令(语句)的集合,称为函数.有的程序员把函数称为方法,希望大家不要被这两个名词搞晕了. 函数分为:自定义函数.系统函数(经常查看js帮助手册). j ...

  9. javascript基础知识--函数定义

    函数声明式 function funname( 参数 ){ ...执行的代码 } 声明式的函数并不会马上执行,需要我们调用才会执行:funname(); * 分号是用来分隔可执行JavaScript语 ...

随机推荐

  1. call 与 apply的区别

    1.方法定义 call方法: 语法:call([thisObj[,arg1[, arg2[,   [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象. 说明: call ...

  2. python——爬虫

    网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模拟程序或者蠕 ...

  3. Winsock - 1 - Winsock API

    Winsock Winsock API Winsock是网络编程接口,而不是协议. 网络原理和协议 建立Winsock规范的主要目的是提供一个与协议无关的传送接口. Winsock将网络编程接口与具体 ...

  4. 解决 spring mvc 3.+ 结合 hibernate3.+ 使用<tx:annotation-driven>声明式事务无法提交的问题

    spring mvc使用注解方式:service使用@service注解 事务使用@Transactional 事务配置使用 <tx:annotation-driven transaction- ...

  5. Unity3DGUI:GUILayout

    显示效果,注意GUILayout控件默认垂直布局,且在水平布局模块里控件大小默认按控件内容来显示,因此对于水平滑块HorizontalSlider来说需要自定义大小避免变形

  6. String与StringBuild、StringBuffer的区别

    String与StringBuild.StringBuffer的区别相信困扰了好多新入门的JAVA程序员,而这也是笔试和面试的一道常见题型,如何全面的回答该问题,变得尤为重要. 首先我们需要清楚一点, ...

  7. 《C++反汇编与逆向分析技术揭秘》——函数的工作原理

    各种调用方式的考察 示例: cdecl方式是调用者清空堆栈: 如果执行的是fastcall: 借助两个寄存器传递参数: 参数1和2借助局部变量来存储: 返回值 如果返回值是结构体: 返回值存放在eax ...

  8. inux按照CPU、内存、磁盘IO、网络性能监测

    http://my.oschina.net/chape/blog/159640 系统优化是一项复杂.繁琐.长期的工作,优化前需要监测.采集.测试.评估,优化后也需要测试.采集.评估.监测,而且是一个长 ...

  9. magento中对获取的数据在前台进行分页显示

    1.数据加载类class Bf170_Bf170Logistics_Block_Inquiry_Index extends Mage_Core_Block_Template {        publ ...

  10. 《Intel汇编第5版》 Mov指令

    一.Mov用于数据传送,用法如下: 二.当传送的数据和目标数据位宽不一致的时候,需要使用MOVZX.MOVSX扩展.MOVZX使用0填充高位,MOVSX使用源操作数最高位填充 下面是汇编代码演示: I ...