JavaScript函数的调用方式和传参方式
来源:互联网 发布:思科通过mac地址查询ip 编辑:程序博客网 时间:2024/06/10 05:21
JavaScript函数的调用方式和传参方式
了解函数的调用过程有助于深入学习与分析JavaScript代码。
在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C#或其他描述性语言那样仅仅作为一个模块来使用。函数有四种调用模式,分别是:函数调用形式、方法调用形式、构造器形式以及apply和call调用形式。这里所有模式中,最主要的区别在于关键字this的意义,下面分别介绍这几种调用形式。(注以下代码都是运行在浏览器环境中)
本文的主要内容:
- 分析函数的四种调用形式
- 弄清楚函数中this的意义
- 明确构造函数对象的过程
- 学会使用上下文调用函数
一、函数调用形式
函数调用形式是最常见的形式,也是最好理解的形式。所谓函数形式就是一般声明函数后直接调用即时。例如:
function foo(){ console.log(this);}foo();//Window
单独独立调用的,就是函数调用模式,即函数名(参数),不能加任何其他东西,对象o.fun()就不是了。在函数调用模式中,this表示全局对象window。
任何自调用函数都是函数模式。
二、方法调用模式
函数调用模式很简单,是最基本的调用方式。但是同样的是函数,将其赋值给一个对象的成员以后,就不一样了。将函数赋值给对象的成员后,那么这个就不再称为函数,而应该称为方法。
所谓方法调用,就是对象的方法调用。方法是什么,方法本身就是函数,但是,方法不是单独独立的,而是要通过一个对象引导来调用。就是说方法对象一定要有宿主对象。
即对象.方法(参数)。
this表示引导方法的对象,就是宿主对象。
对比函数调用模式:
- 方法调用模式不是独立的,需要宿主,而函数调用模式是独立的;
- 方法调用模式方式:obj.fun();函数调用模式方式:fun();
- 方法调用模式中,this指宿主;而函数调用模式中this指全局对象window。
//定义一个函数function foo(){ console.log(this);}//将其赋值给一个对象var o = {};o.fn = foo;//注意这里不要加括号//调用o.fn();//o
函数调用中,this专指全局对象window,而在方法中this专指当前对象,即o.fn中的this指的就是对象o。
美团的一道面试题:
var length = 10;function fn(){ console.log(this.length);}var obj = { length:5, method:function(fn){ fn();//10 前面没有引导对象,函数调用模式 arguments[0]();//2 //arguments是一个伪数组对象,这里调用相当于通过数组的索引来调用 //这里的this就是指的这个伪数组,所以this.length为2 }};obj.method(fn,1);//打印10和2obj.method(fn,1,2,3);//打印10和4
解析:
- fn()前面没有引导对象,是函数调用模式, this是全局对象,输出 10;
- arguments0,arguments是一个伪数组对象, 这里调用相当于通过数组的索引来调用。所以,执行时,this就是指arguments,由于传了两个参数,所以 输出为arguments.length就是2。
这里引导对象即宿主就是 arguments对象。
三、构造器调用模式
同样是函数,在单纯的函数模式下,this表示window;在对象方法模式下,this值的是当前对象。除了这两种情况,JavaScript中函数还可以是构造器。将函数作为构造器来使用的语法就是在函数调用前面加上一个new关键字。例如:
//定义一个构造函数var Person = function(){ this.name = '程序员'; this.sayHello = function(){ console.log('Hello'); };};//调用构造器,创建对象var p = new Person();//使用对象p.sayHello();//Hello
上面的案例首先创建一个构造函数Person,然后使用构造函数创建对象p。这里使用new语法。然后再使用对象调用sayHello()方法。从案例可以看到,此时this指的是对象本身。此外,函数作为构造器还有几个变化,分别为:
- 所有需要由对象使用的属性,必须使用this引导;
- 函数的return语句意义被改写,如果返回非对象,就返回this。
分享一道面试题
请问顺序执行以下代码,会怎样输出
function Foo(){ getName = function(){ console.log(1); } return this;}Foo.getName = function(){ console.log(2);}Foo.prototype.getName = function(){ console.log(3);}var getName = function(){ console.log(4);}function getName(){ console.log(5);}Foo.getName();//输出2.//调用Foo函数作为对象动态添加的属性方法 getName//Foo.getName = function(){console.log(2);}getName();//输出4.//这里Foo函数还没有执行,getName还没有被覆盖//所以这里还是最上面的getName=function(){console.log(4);}Foo().getName();//输出1//Foo()执行,先覆盖全局的getName,再返回this//this是window,Foo().getName()就是调用window.getName//此时全局的getName已被覆盖成function(){console.log(1);}//所以输出为1//从这里开始window.getName已被覆盖为function(){console.log(1);}getName();//输出1//window.getName(),输出1new Foo.getName();//输出2//new 就是找构造函数(),由构造函数结合性,这里即使Foo无参,也不能省略(),所以不是Foo().getName()//所以Foo.getName为一个整体,等价于new (Foo.getName)();//而 Foo.getName其实就是函数function(){console.log(2);}的引用//那么new (Foo.getName)(),就是在以Foo.getName为构造函数,实例化对象。//就类似于 new Person();Person是一个构造函数new Foo().getName();//输出3//new就是找构造函数(),等价于(new Foo() ).getName();//执行new Foo() => 以Foo为构造函数,实例化一个对象//(new Foo() ).getName;访问这个实例化对象的getName属性//实例对象自己并没有getName属性,构造的时候也没有添加,找不到,就到原型中找//发现Foo.prototype.getName = fucntion(){console.log(3);}//原型中有,找到了,所以执行(new Foo() ).getName()结果为3new new Foo().getName();//输出为3//new就是找构造函数(),等价于new ( (new Foo() ).getName ) ()//先看里面的(new Foo() ).getName//new Foo() 以Foo为构造函数,实例化对象//new Foo().getName 找实例对象的 getName 属性,自己没有,就去原型中找//发现 Foo.prototype.getName = function() {console.log(3);}//所以里层(new Foo() ).getName就是以Foo为构造函数实例出的对象的一个原型属性//属性值为一个函数function(){console.log(3);}的引用//所以外层new ( (new Foo() ).getName )()在以函数function(){console.log(3);}为构造函数,构造实例//构造过程中执行了console.log(3),输出3
构造器中的this
分析创建对象的过程,理解this的意义。
var Person = function(){ this.name = '程序员';};var p = new Person();
这里首先定义了函数Person,下面分析一下整个执行:
- 程序在执行到这一句的时候,不会执行函数体,因此JavaScript的解释器并不知道这个函数的内容。
- 接下来执行new关键字,创建对象,解释器开辟内存,得到对象的引用,将新对象的引用交给函数。
- 紧接着执行函数,将传过来的对象引用交给this。也就是说,在构造方法中,this就是刚刚被new创建出来的对象。
- 然后为this添加成员,也就是为对象添加成员。
- 最后函数结束,返回this,将this交给左边的变量。
分析过构造函数的执行以后,可以得到,构造函数中的this就是当前对象。
构造器中的return
在构造函数中return的意义发生了变化,首先如果在构造函数中,如果返回的是一个对象,那么就保留原意。如果返回的是非对相,比如数字、布尔值和字符串,那么就返回this,如果没有return语句,那么也返回this,例如:
//返回一个对象的returnvar foo = function(){ this.name = '张三'; return { name:'李四' };};//创建对象var p = new foo();//访问name属性console.log(p.name);//李四
执行代码,这里输出的结果是“李四”。因为构造方法中返回的是一个对象,那么保留return的意义,返回内容为return后面的对象,再看如下代码:
//定义返回非对象数据的构造器var foo = fucntion() { this.name = '张三'; return '李四';}//创建对象var p = new foo();console.log(p.name);//张三
执行代码,这里输出结果为“张三”,因为这里return的是一个字符串,属于基本类型,那么这里的return语句无效,返回的是this对象。
四、上下文调用模式
就是环境调用模式 => 在不同环境下的不同调用模式
简单说就是统一一种格式,可以实现函数模式与方法模式
语法
- call形式,函数名.call(…)
- apply形式,函数名.apply(…)
这两种形式功能完全一样,唯一不同的是参数的形式。先学习apply,再来看call形式
apply方法调用形式
存在上下文调用的目的就是为了实现方法借用,且不会污染对象。
如果需要让函数以函数的形式调用,可以使用
foo.apply(null);//上下文为window如果希望它是方法调用模式,注意需要提供一个宿主对象
foo.apply(obj);//上下文为传入的obj对象
function foo(num1,num2){ console.log(this); return num1+num2;}//函数调用模式var res1 = foo(123,567);//方法调用var o = { name: 'chenshsh' };o.func = foo;var res2 = o.func(123,567);
使用apply进行调用,如果函数是带有参数的。apply的第一个参数要么是null要么是对象。
- 如果是null,就是函数调用
- 如果是对象就是方法调用,该对象就是宿主对象,后面紧跟一个数组参数,将函数所有的参数依次放在数组中
//函数模式foo(123,567);foo.apply(null,[123,567]);//以window为上下文执行apply//方法模式o.func(123,567);var o = { name:'chenshsh' };foo.apply(o,[123,567]);//以o为上下文执行apply
call方法调用
在使用apply调用的时候,函数参数必须以数组的形式存在。但是有些时候数组封装比较复杂,所以引入call调用,call调用与apply完全相同,唯一不同是参数不需要使用数组。
foo(123,456);foo.apply(null,[123,456]);foo.call(null,123,456);
- 函数调用:函数名.call(null,参数1,参数2,参数3…);
- 方法调用:函数名.call(obj,参数1,参数2,参数3…);
不传递参数时,apply和call完全一样
- JavaScript函数的调用方式和传参方式
- JavaScript函数调用方式
- javascript函数调用方式
- JavaScript函数分类、定义方式和调用方式
- 关于javascript 匿名函数的调用方式
- php调用javascript函数的方式
- JavaScript之调用函数的方式
- javascript的四种函数调用方式
- javascript调用匿名函数的多种方式
- Javascript中四种函数调用方式
- Javascript 调用Javascript函数的四种方式
- 函数调用的两种方式PASCAL调用方式和C调用方式
- JavaScript函数的常用的两种调用方式
- Javascript中匿名函数的多种调用方式
- Javascript中匿名函数的多种调用方式总结
- JavaScript中七种函数调用方式及对应 this 的含义
- JavaScript函数定义的三种方式及其调用
- JavaScript中七种函数调用方式及对应 this 的含义
- AssetBundle.cs
- 使用ffmpeg3.x进行YUV420P->H.264的简单转码
- Python之ReportLab绘图
- crond和crontab调研
- HDU-1014 Uniform Generator
- JavaScript函数的调用方式和传参方式
- 每日一学(一)android图形验证码的实现
- Python之ReportLab绘制条形码和二维码
- 水池数目
- 2017年6月9日,周结(十六),一些简单的算法题(二)
- JS实用命令积累
- Qt中截图功能的实现
- 费科长的首秀
- HDU 4873 题解