深入理解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自定义startWith()和endWith()的两种方法
Nov 11 Javascript
PHP PDO操作总结
Nov 17 Javascript
js的window.showModalDialog及window.open用法实例分析
Jan 29 Javascript
HTML Table 空白单元格补全的简单实现
Oct 13 Javascript
JS中SetTimeout和SetInterval使用初探
Mar 23 Javascript
Angular2开发环境搭建教程之VS Code
Dec 15 Javascript
webpack中的热刷新与热加载的区别
Apr 09 Javascript
解决vuex刷新状态初始化的方法实现
Aug 15 Javascript
JS原形与原型链深入详解
May 09 Javascript
微信小程序实现上拉加载功能示例【加载更多数据/触底加载/点击加载更多数据】
May 29 Javascript
echarts饼图各个板块之间的空隙如何实现
Dec 01 Javascript
javascript实现放大镜功能
Dec 09 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
利用PHP创建动态图像
2006/10/09 PHP
PHP IN_ARRAY 函数使用注意事项
2010/07/24 PHP
php操纵mysqli数据库的实现方法
2016/09/18 PHP
toggle一个div显示或隐藏且可扩展成自定义下拉框
2013/09/12 Javascript
js中的this关键字详解
2013/09/25 Javascript
JavaScript网页定位详解
2014/01/13 Javascript
jquery ajax跨域解决方法(json方式)
2014/02/04 Javascript
纯JS实现动态时间显示代码
2014/02/08 Javascript
一个js过滤空格的小函数
2014/10/10 Javascript
JavaScritp添加url参数并将参数加入到url中及更改url参数的方法
2015/10/26 Javascript
jQuery实现鼠标滑过链接控制图片的滑动展开与隐藏效果
2015/10/28 Javascript
JavaScript学习小结之被嫌弃的eval函数和with语句实例详解
2016/08/01 Javascript
Jquery获取当前城市的天气信息
2016/08/05 Javascript
Javascript设计模式之装饰者模式详解篇
2017/01/17 Javascript
jQuery插件HighCharts绘制2D半圆环图效果示例【附demo源码下载】
2017/03/09 Javascript
使用 vue-i18n 切换中英文效果
2018/05/23 Javascript
vue中如何让子组件修改父组件数据
2018/06/14 Javascript
[02:54]DOTA2英雄基础教程 暗影牧师戴泽
2013/12/05 DOTA
[42:36]DOTA2上海特级锦标赛B组败者赛 VG VS Spirit第二局
2016/02/26 DOTA
在Python中关于中文编码问题的处理建议
2015/04/08 Python
详解Python网络爬虫功能的基本写法
2016/01/28 Python
python制作爬虫爬取京东商品评论教程
2016/12/16 Python
Python学习小技巧之利用字典的默认行为
2017/05/20 Python
python使用正则表达式的search()函数实现指定位置搜索功能
2017/11/10 Python
python+pyqt5实现图片批量缩放工具
2019/03/18 Python
浅谈python编译pyc工程--导包问题解决
2019/03/20 Python
Python定义函数功能与用法实例详解
2019/04/08 Python
python实现ftp文件传输功能
2020/03/20 Python
Python之字符串的遍历的4种方式
2020/12/08 Python
Html5 Canvas 实现一个“刮刮乐”游戏
2019/09/05 HTML / CSS
优秀企业获奖感言
2014/02/01 职场文书
语文教学感言
2014/02/06 职场文书
毕业生就业协议书
2014/04/11 职场文书
工作失误检讨书
2015/01/26 职场文书
创业计划书之废品回收
2019/09/26 职场文书
Python djanjo之csrf防跨站攻击实验过程
2021/05/14 Python