深入理解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函数、方法、对象代码
Oct 29 Javascript
关于jQuery中的end()使用方法
Jul 10 Javascript
JavaScript 代码压缩工具小结
Feb 27 Javascript
禁止空格提交表单的js代码
Nov 17 Javascript
用javascript对一个json数组深度赋值示例
Jul 27 Javascript
jQuery实现提示密码强度的代码
Jul 15 Javascript
10行原生JS实现文字无缝滚动(超简单)
Jan 02 Javascript
Vue组件化开发思考
Feb 02 Javascript
Vue 父子组件数据传递的四种方式( inheritAttrs + $attrs + $listeners)
May 04 Javascript
React Navigation 使用中遇到的问题小结
May 08 Javascript
让webpack+vue-cil项目不再自动打开浏览器的方法
Sep 27 Javascript
ES6基础之展开语法(Spread syntax)
Feb 21 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 header Content-Type类型小结
2011/07/03 PHP
PHP简洁函数小结
2011/08/12 PHP
php中将汉字转换成拼音的函数代码
2012/09/08 PHP
PHP读取PPT文件的方法
2015/12/10 PHP
全新Mac配置PHP开发环境教程
2016/02/03 PHP
Ubuntu 16.04下安装PHP 7过程详解
2017/03/28 PHP
PHP7 整型处理机制修改
2021/03/09 PHP
浅谈javascript 面向对象编程
2009/10/28 Javascript
Ext.MessageBox工具类简介
2009/12/10 Javascript
js下用eval生成JSON对象
2010/09/17 Javascript
JavaScript 垃圾回收机制分析
2013/10/10 Javascript
删除javascript中注释语句的正则表达式
2014/06/11 Javascript
使用正则表达式的格式化与高亮显示json字符串
2014/12/03 Javascript
jQuery中大家不太了解的几个方法
2015/03/04 Javascript
一个php+js实时显示时间问题
2015/10/12 Javascript
微信小程序 swiper组件详解及实例代码
2016/10/25 Javascript
完美解决浏览器跨域的几种方法(汇总)
2017/05/08 Javascript
jQuery实现选中行变色效果(实例讲解)
2017/07/06 jQuery
jQuery实现的简单日历组件定义与用法示例
2018/12/24 jQuery
详解微信小程序-扫一扫 wx.scanCode() 扫码大变身
2019/04/30 Javascript
element el-tree组件的动态加载、新增、更新节点的实现
2020/02/27 Javascript
python连接mysql实例分享
2016/10/09 Python
python机器学习理论与实战(一)K近邻法
2021/01/28 Python
解决使用pycharm提交代码时冲突之后文件丢失找回的方法
2018/08/05 Python
python 爬虫 实现增量去重和定时爬取实例
2020/02/28 Python
python numpy实现rolling滚动案例
2020/06/08 Python
Python3爬虫中关于Ajax分析方法的总结
2020/07/10 Python
python openpyxl模块的使用详解
2021/02/25 Python
Expedia加拿大官方网站:加拿大最大的在线旅游提供商
2017/12/31 全球购物
船餐厅和泰晤士河餐饮游轮:Bateaux London
2018/03/19 全球购物
阿尔卡特(中国)的面试题目
2014/08/20 面试题
SQL Server里面什么样的视图才能创建索引
2015/04/17 面试题
介绍一下mysql的日期和时间函数
2013/03/28 面试题
多媒体专业自我鉴定
2014/02/28 职场文书
校长四风对照检查材料
2014/09/27 职场文书
《哪吒之魔童降世》观后感:世上哪有随随便便的成功
2019/11/08 职场文书