深入理解JavaScript函数参数(推荐)


Posted in Javascript onJuly 26, 2016

前面的话

javascript函数的参数与大多数其他语言的函数的参数有所不同。函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数。

arguments

javascript中的函数定义并未指定函数形参的类型,函数调用也未对传入的实参值做任何类型检查。实际上,javascript函数调用甚至不检查传入形参的个数

function add(x){
return x+1;
}
console.log(add(1));//2
console.log(add('1'));//'11'
console.log(add());//NaN
console.log(add(1,2));//2

同名形参

在非严格模式下,函数中可以出现同名形参,且只能访问最后出现的该名称的形参

function add(x,x,x){
return x;
}
console.log(add(1,2,3));//3

而在严格模式下,出现同名形参会抛出语法错误

function add(x,x,x){
'use strict';
return x;
}
console.log(add(1,2,3));//SyntaxError: Duplicate parameter name not allowed in this context

参数个数

当实参比函数声明指定的形参个数要少,剩下的形参都将设置为undefined值

function add(x,y){
console.log(x,y);//1 undefined
}
add(1);

常常使用逻辑或运算符给省略的参数设置一个合理的默认值

function add(x,y){
y = y || 2;
console.log(x,y);//1 2
}
add(1);

[注意]实际上,使用y || 2是不严谨的,显式地设置假值(undefined、null、false、0、-0、''、NaN)也会得到相同的结果。所以应该根据实际场景进行合理设置

当实参比形参个数要多时,剩下的实参没有办法直接获得,需要使用即将提到的arguments对象

javascript中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。arguments对象并不是Array的实例,它是一个类数组对象,可以使用方括号语法访问它的每一个元素

function add(x){
console.log(arguments[0],arguments[1],arguments[2])//1 2 3
return x+1;
}
add(1,2,3);

arguments对象的length属性显示实参的个数,函数的length属性显示形参的个数

function add(x,y){
console.log(arguments.length)//3
return x+1;
}
add(1,2,3);
console.log(add.length);//2

形参只是提供便利,但不是必需的

function add(){
return arguments[0] + arguments[1];
}
console.log(add(1,2));//3

对象参数

当一个函数包含超过3个形参时,要记住调用函数中实参的正确顺序实在让人头疼

function arraycopy(/*array*/from,/*index*/form_start,/*array*/to,/*index*/to_start,/*integer*/length){
//todo
}

通过名/值对的形式来传入参数,这样参数的顺序就无关紧要了。定义函数的时候,传入的实参都写入一个单独的对象之中,在调用的时候传入一个对象,对象中的名/值对是真正需要的实参数据

function easycopy(args){
arraycopy(args.from,args.form_start || 0,args.to,args.to_start || 0, args.length);
}
var a = [1,2,3,4],b =[];
easycopy({form:a,to:b,length:4});

同步

当形参与实参的个数相同时,arguments对象的值和对应形参的值保持同步

function test(num1,num2){
console.log(num1,arguments[0]);//1 1
arguments[0] = 2;
console.log(num1,arguments[0]);//2 2
num1 = 10;
console.log(num1,arguments[0]);//10 10
}
test(1);

[注意]虽然命名参数和对应arguments对象的值相同,但并不是相同的命名空间。它们的命名空间是独立的,但值是同步的

但在严格模式下,arguments对象的值和形参的值是独立的

function test(num1,num2){
'use strict';
console.log(num1,arguments[0]);//1 1
arguments[0] = 2;
console.log(num1,arguments[0]);//1 2
num1 = 10;
console.log(num1,arguments[0]);//10 2
}
test(1);

当形参并没有对应的实参时,arguments对象的值与形参的值并不对应

function test(num1,num2){
console.log(num1,arguments[0]);//undefined,undefined
num1 = 10;
arguments[0] = 5;
console.log(num1,arguments[0]);//10,5
}
test();

内部属性

【callee】

arguments对象有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数

下面是经典的阶乘函数

function factorial(num){
if(num <=1){
return 1;
}else{
return num* factorial(num-1);
}
} 
console.log(factorial(5));//120

但是,上面这个函数的执行与函数名紧紧耦合在了一起,可以使用arguments.callee可以消除函数解耦

function factorial(num){
if(num <=1){
return 1;
}else{
return num* arguments.callee(num-1);
}
} 
console.log(factorial(5));//120

但在严格模式下,访问这个属性会抛出TypeError错误

function factorial(num){
'use strict';
if(num <=1){
return 1;
}else{
return num* arguments.callee(num-1);
}
} 
//TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
console.log(factorial(5));

这时,可以使用具名的函数表达式

var factorial = function fn(num){
if(num <=1){
return 1;
}else{
return num*fn(num-1);
}
}; 
console.log(factorial(5));//120

【caller】

实际上有两个caller属性

【1】函数的caller

函数的caller属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值是null

function outer(){
inner();
}
function inner(){
console.log(inner.caller);//outer(){inner();}
}
outer(); 
function inner(){
console.log(inner.caller);//null
}
inner();

在严格模式下,访问这个属性会抛出TypeError错误

function inner(){
'use strict';
//TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
console.log(inner.caller);
}
inner();

【2】arguments对象的caller

该属性始终是undefined,定义这个属性是为了分清arguments.caller和函数的caller属性

function inner(x){
console.log(arguments.caller);//undefined
}
inner(1);

同样地,在严格模式下,访问这个属性会抛出TypeError错误

function inner(x){
'use strict';
//TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context
console.log(arguments.caller);
}
inner(1);

函数重载

javascript函数不能像传统意义上那样实现重载。而在其他语言中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可

javascript函数没有签名,因为其参数是由包含0或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的

//后面的声明覆盖了前面的声明
function addSomeNumber(num){
return num + 100;
}
function addSomeNumber(num){
return num + 200;
}
var result = addSomeNumber(100);//300

只能通过检查传入函数中参数的类型和数量并作出不同的反应,来模仿方法的重载

function doAdd(){
if(arguments.length == 1){
alert(arguments[0] + 10);
}else if(arguments.length == 2){
alert(arguments[0] + arguments[1]);
}
}
doAdd(10);//20
doAdd(30,20);//50

参数传递

javascript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制到函数内部的参数,就和把值从一个变量复制到另一个变量一样

【1】基本类型值

在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(命名参数或arguments对象的一个元素)

function addTen(num){
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
console.log(count);//20,没有变化
console.log(result);//30

【2】引用类型值

在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部

function setName(obj){
obj.name = 'test';
}
var person = new Object();
setName(person);
console.log(person.name);//'test'

当在函数内部重写引用类型的形参时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁

function setName(obj){
obj.name = 'test';
console.log(person.name);//'test'
obj = new Object();
obj.name = 'white';
console.log(person.name);//'test'
}
var person = new Object();
setName(person);

以上所述是小编给大家介绍的深入理解JavaScript函数参数(推荐),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
javascript 表格排序和表头浮动效果(扩展SortTable)
Apr 07 Javascript
Jquery实现简单的动画效果代码
Mar 18 Javascript
.net,js捕捉文本框回车键事件的小例子(兼容多浏览器)
Mar 11 Javascript
jquery为页面增加快捷键示例
Jan 31 Javascript
不用一句js代码初始化组件
Jan 27 Javascript
jquery的ajax提交form表单的两种方法小结(推荐)
May 25 Javascript
Jquery中attr与prop的区别详解
May 27 jQuery
JS/jQuery实现DIV延时几秒后消失或显示的方法
Feb 12 jQuery
图片文字识别(OCR)插件Ocrad.js教程
Nov 26 Javascript
layer弹出层自适应高度,垂直水平居中的实现
Sep 16 Javascript
Vue常用的全选/反选的示例代码
Feb 19 Javascript
Vue使用自定义指令实现拖拽行为实例分析
Jun 06 Javascript
JavaScript从0开始构思表情插件
Jul 26 #Javascript
只需五句话搞定JavaScript作用域(经典)
Jul 26 #Javascript
第一次接触神奇的Bootstrap基础排版
Jul 26 #Javascript
js获取html的span标签的值方法(超简单)
Jul 26 #Javascript
js无法获取到html标签的属性的解决方法
Jul 26 #Javascript
jquery判断对象是否为空并遍历对象的简单实例
Jul 26 #Javascript
浅谈Javascript数据属性与访问器属性
Jul 26 #Javascript
You might like
Yii2 GridView实现列表页直接修改数据的方法
2016/05/16 PHP
ThinkPHP防止重复提交表单的方法实例分析
2018/05/10 PHP
PHP利用DWZ.CN服务生成短网址
2019/08/11 PHP
php设计模式之适配器模式实例分析【星际争霸游戏案例】
2020/04/07 PHP
理解Javascript_15_作用域分配与变量访问规则,再送个闭包
2010/10/20 Javascript
ASP中Sub和Function的区别说明
2020/08/30 Javascript
jQuery调用WebService的实现代码
2011/06/19 Javascript
工作需要写的一个js拖拽组件
2011/07/28 Javascript
js简单实现让文本框内容逐个字的显示出来
2013/10/22 Javascript
new Date()问题在ie8下面的处理方法
2014/07/31 Javascript
利用原生JavaScript获取元素样式只是获取而已
2014/10/08 Javascript
Jquery实现由下向上展开效果的例子
2014/12/08 Javascript
javascript实现带节日和农历的日历特效
2015/02/01 Javascript
第十篇BootStrap轮播插件使用详解
2016/06/21 Javascript
javascript 使用正则test( )第一次是 true,第二次是false
2017/02/22 Javascript
Vuex利用state保存新闻数据实例
2017/06/28 Javascript
javascript ES6 新增了let命令使用介绍
2017/07/07 Javascript
js动态引入的四种方法
2018/05/05 Javascript
微信小程序基于picker实现级联菜单
2019/02/15 Javascript
今天,小程序正式支持 SVG
2019/04/20 Javascript
vue项目中常见问题及解决方案(推荐)
2019/10/21 Javascript
django的ORM操作 增加和查询
2019/07/26 Python
Python如何使用k-means方法将列表中相似的句子归类
2019/08/08 Python
5个HTML5的常用本地存储方式详解与介绍
2021/03/27 HTML / CSS
会计专业大学生职业生涯规划范文
2014/01/11 职场文书
公司拓展活动方案
2014/02/13 职场文书
环保倡议书400字
2014/05/15 职场文书
群众路线教育实践活动心得体会(教师)
2014/10/31 职场文书
2014企业年终工作总结
2014/12/23 职场文书
人事局接收函
2015/01/31 职场文书
审查起诉阶段律师意见书
2015/05/19 职场文书
企业年会祝酒词
2015/08/11 职场文书
感谢信的技巧及范例
2019/05/15 职场文书
启迪人心的励志语录:脾气永远不要大于本事
2020/01/02 职场文书
Python机器学习之底层实现KNN
2021/06/20 Python
MySQL 外连接语法之 OUTER JOIN
2022/04/09 MySQL