深入理解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 相关文章推荐
js 创建书签小工具之理论
Feb 25 Javascript
jquery中eq和get的区别与使用方法
Apr 14 Javascript
基于JQuery和CSS3实现仿Apple TV海报背景视觉差特效源码分享
Sep 21 Javascript
jQuery遍历DOM节点操作之filter()方法详解
Apr 14 Javascript
Bootstrap基本组件学习笔记之分页(12)
Dec 08 Javascript
jquery 正整数数字校验正则表达式
Jan 10 Javascript
js 函数式编程学习笔记
Mar 25 Javascript
将angular.js项目整合到.net mvc中的方法详解
Jun 29 Javascript
jQuery easyui datagird编辑行删除行功能的实现代码
Sep 20 jQuery
详解使用uni-app开发微信小程序之登录模块
May 09 Javascript
Layui数据表格 前后端json数据接收的方法
Sep 19 Javascript
vue2.0 解决抽取公用js的问题
Jul 31 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
深入了解php4(1)--回到未来
2006/10/09 PHP
php制作中间带自己定义图片二维码的方法
2014/01/27 PHP
PHP利用header跳转失效的解决方法
2014/10/24 PHP
php文件压缩之PHPZip类用法实例
2015/06/18 PHP
Gambit vs CL BO3 第三场 2.13
2021/03/10 DOTA
JavaScript脚本性能优化注意事项
2008/11/18 Javascript
JS setCapture 区域外事件捕捉
2010/03/18 Javascript
js里的prototype使用示例
2010/11/19 Javascript
图片上传插件jquery.uploadify详解
2013/11/15 Javascript
JavaScript中Math对象方法使用概述
2014/01/02 Javascript
js中使用replace方法完成某个字符的转换
2014/08/20 Javascript
javascript制作网页图片上实现下雨效果
2015/02/26 Javascript
js实现的鼠标滚轮滚动切换页面效果(类似360默认页面滚动切换效果)
2016/01/27 Javascript
node.js插件nodeclipse安装图文教程
2020/10/19 Javascript
全面解析Javascript无限添加QQ好友原理
2016/06/15 Javascript
用JS实现图片轮播效果代码(一)
2016/06/26 Javascript
javascript实现的左右无缝滚动效果
2016/09/19 Javascript
vue使用watch 观察路由变化,重新获取内容
2017/03/08 Javascript
windows下vue-cli导入bootstrap样式
2017/04/25 Javascript
详解angularjs 关于ui-router分层使用
2017/06/12 Javascript
详解在vue-cli项目中安装node-sass
2017/06/21 Javascript
深入理解基于vue-cli的vuex配置
2017/07/24 Javascript
AngularJS点击添加样式、点击变色设置的实例代码
2017/07/27 Javascript
微信小程序开发问题之wx.previewImage
2018/12/25 Javascript
vue+高德地图写地图选址组件的方法
2019/05/18 Javascript
开发Node CLI构建微信小程序脚手架的示例
2020/03/27 Javascript
python查找目录下指定扩展名的文件实例
2015/04/01 Python
wx.CheckBox创建复选框控件并响应鼠标点击事件
2018/04/25 Python
Python自然语言处理 NLTK 库用法入门教程【经典】
2018/06/26 Python
PyQt5基本控件使用之消息弹出、用户输入、文件对话框的使用方法
2019/08/06 Python
美国婴童服装市场上的领先品牌:Carter’s
2018/02/08 全球购物
一套软件测试笔试题
2014/07/25 面试题
内刊编辑求职自荐书范文
2014/02/19 职场文书
客服部班长工作责任制
2014/02/25 职场文书
保密工作目标责任书
2014/07/28 职场文书
浅谈如何写好演讲稿?
2019/06/12 职场文书