JavaScript系列之类数组对象arguments

以前在《JavaScript系列之this是什么》这篇文章中,咱们曾谈过"this"这个关键字,本章将会谈到另外一个JavaScript中的关键字,叫作"arguments"。javascript

那arguments究竟是什么呢?下面我将尽量地用简洁明了的言语来描述 arguments。前端

什么是参数(parameters)

在了解arguments以前,咱们必需要先了解一下什么是参数(parameter)。参数其实就是咱们会带入函数的变量,如下面例子来讲,"house"、"car"、"money",就是咱们在执行函式的时候能够任意填入的参数。java

function MyFavorite(house, car, money) {
    console.log(house);
    console.log(car);
    console.log(money);
}

MyFavorite();
复制代码

首先,当我创建好这样的函数,我能够不带任何参数值就去执行这个函数,只要输入MyFavorite()这样就能够了!git

通常若是有参数却又没有给它参数值,函数的执行上每每会有错误!但在JavaScript中不太同样的地方在于,即便你没有给它任何参数值就加以执行,也不会报错,而是会返回undefinedgithub

为何会获得"undefined"呢?面试

之因此会这样是由于当JavaScript在执行这个函数的时候,因为提高机制,它会先把咱们的参数(house, car, money)存到内存中了,而且赋予它的值是undefinedexpress

参数值会由左至右读取,若是我依序执行这样的代码就理解了:编程

MyFavorite();
MyFavorite("别墅");
MyFavorite("别墅", "法拉利");
MyFavorite("别墅", "法拉利", "一亿元");
复制代码

会分别读到如下的结果,表示JavaScript会由左至右来读取参数值,并且即便某些参数值有缺值的状况,JavaScript仍是能够正常执行。数组

设置函数中参数的预设值

因为目前的几款浏览器使用的JavaScript版本都尚不支持直接在参数的地方设置预设值(ES6的将能够),因此不少的框架都还不会用这种方式设置预设值。浏览器

方法一:在ES6的JavaScript中,能够直接经过这种方式设置参数预设值:

function MyFavorite(house, car, money = '一亿元') {
    console.log(house);
    console.log(car);
    console.log(money);
    console.log("----------------");
}
复制代码

方法二:利用强制转换的概念设置预设值

因为版本兼容的差别,现今多数的编程都是使用这种方式设置参数的预设值,利用简单的"="和"||"就能够达到参数预设值的效果:

function MyFavorite(house, car, money) {
    
    money = money || '一亿元';
    console.log(house);
    console.log(car);
    console.log(money);
    console.log("----------------");
}
复制代码

这时候即便在没有给值的状况下,money同样能够获得预设值为"一亿元":

类数组对象

了解了parameters的概念后,让咱们回来谈谈arguments,MDN将它叫作类数组对象,那么什么是类数组对象呢?

所谓的类数组对象:

就是拥有一个 length 属性和若干索引属性的对象

举个例子:

var array = ['house', 'car', 'money'];

var arrayLike = {
    0: 'house',
    1: 'car',
    2: 'money',
    length: 3
}

//读取
console.log(array[0]); // house
console.log(arrayLike[0]); // house

array[0] = 'new house';
arrayLike[0] = 'new house';

//长度
console.log(array.length); // 3
console.log(arrayLike.length); // 3

//遍历
for(var i = 0, len = array.length; i < len; i++) {
   ……
}
for(var i = 0, len = arrayLike.length; i < len; i++) {
    ……
}
复制代码

上面咱们能够看得出来,类数组对象与数组在读取、获取长度、遍历三个方面同样,都能取到,那为何还叫作类数组对象呢?

由于类数组对象不可使用数组的方法,好比:

arrayLike.push('name');
复制代码

然而上述代码会报错: arrayLike.push is not a function,因此终归仍是类数组呐……

调用数组方法

若是类数组想用数组的方法怎么办呢?

直接调用是不可取的,那咱们能够经过 Function.call 的方法进行间接调用:

var arrayLike = {0: 'house', 1: 'car', 2: 'money', length: 3 }

console.log(Array.prototype.join.call(arrayLike, '&')); // house&car&money

console.log(Array.prototype.slice.call(arrayLike, 0)); // ["house", "car", "money"] 
// slice能够作到类数组转数组

Array.prototype.map.call(arrayLike, function(item){
    return item.toUpperCase();
}); 
// ["HOUSE", "CAR", "MONEY"]
复制代码

在上面已经提到了一种类数组转数组的方法,再补充三个:

var arrayLike = {0: 'house', 1: 'car', 2: 'money', length: 3 }
// 1. slice
console.log(Array.prototype.slice.call(arrayLike)); // ["house", "car", "money"] 
// 2. splice
console.log(Array.prototype.splice.call(arrayLike, 0)); // ["house", "car", "money"] 
// 3. ES6 Array.from
console.log(Array.from(arrayLike)); // ["house", "car", "money"] 
// 4. apply
console.log(Array.prototype.concat.apply([], arrayLike));
复制代码

接下来重点讲讲 Arguments 这个类数组对象。

arguments对象

arguments比起this来讲,要容易理解的多,arguments对象只定义在函数体中,包括了函数的参数和其余属性。

一样地经过上面的例子加以理解,咱们直接在函数中去打印出"arguments"这个关键字:

function MyFavorite(house, car, money) {
    
    money = money || '一亿元';
    
    console.log(arguments);
    console.log("----------------");
}

MyFavorite();
MyFavorite("别墅");
MyFavorite("别墅", "法拉利");
MyFavorite("别墅", "法拉利", "一亿元");
复制代码

打印结果以下:

咱们能够看到除了类数组的索引属性和length属性以外,还有一个callee属性,并且咱们能够看到arguments对象的__ proto __是指向object的,这也说明了他是个类数组对象,而不是一个数组。下面咱们进行一一介绍。

length属性

arguments.length为函数实参个数,举个例子:

function foo(house, car, money){
    console.log("实参的长度为:" + arguments.length)
}

console.log("形参的长度为:" + foo.length)

foo(1)

// 形参的长度为:3
// 实参的长度为:1
复制代码

callee属性

每一个参数实例都有一个callee属性,经过它能够调用函数自身。 ES5的严格模式不容许访问arguments.callee

讲个闭包经典面试题使用 callee 的解决方法:

var data = [];

for (var i = 0; i < 3; i++) {
    (data[i] = function () {
       console.log(arguments.callee.i) 
    }).i = i;
}

data[0]();
data[1]();
data[2]();

// 0
// 1
// 2
复制代码

最后来看看展开运算符spread(...)

除了arguments这个关键字,在新版ES6的JavaScript中另外提供了一个展开运算符(spread),它就是「...」三个点,这个...有什么用呢?

根据MDN对于展开运算符spread的描述以下:

The spread operator allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) or multiple variables (for destructuring assignment) are expected.

简单来讲,就是它能够把函数中许多的参数(arguments)或数组中许多的元素(elements)造成一个新的变量。

举例来讲:

在函数的部分,在参数的地方咱们用"...other",other是你想要储存成的数组变量名称,能够本身取。

在执行函数的地方,本来咱们只有三个参数(house, car, money),也只能填写三个参数;但使用了展开运算符"..."后,咱们在执行函数的地方就能够带入不仅三个参数(例如,我在最后面又加了"老婆"和"孩子"),这些多的参数值最后都会被放到other这个数组当中。

function MyFavorite(house, car, money, ...other){

    console.log(other);
    console.log('What my favorite are' + house + ',' + car + ',' + money + ',' + other);

}

MyFavorite("别墅", "法拉利", "一亿");
MyFavorite("别墅", "法拉利", "一亿", "老婆", "孩子");
复制代码

结果就会长的像这样子:

展开运算符还有其余的使用方式,像是把数组元素作链接等等,使用灵活的话至关方便,若是有须要的话,能够参考引1。

引1:Microsoft Developer Netword:展开运算符(...)

应用

1.利用arguments实现方法的重载

下面咱们利用arguments对象来实现一个参数相加的函数,不论传入多少参数都行,将传入的参数相加后返回。

function add() {
    var len = arguments.length,
        sum = 0;
    for(;len--;){
        sum += arguments[len];
    }
    return sum;
}

console.log( add(1,2,3) );   //6
console.log( add(1,3) );     //4
console.log( add(1,2,3,5,6) );   //17
复制代码

因为JS是一种弱类型的语言,没有重载机制,当咱们重写函数时,会将原来的函数直接覆盖,这里咱们能利用arguments,来判断传入的实参类型与数量进行不一样的操做,而后返回不一样的数值。

2.利用arguments.callee实现递归

先来看看日常咱们是怎么实现递归的,这是一个结算阶乘的函数。

function foo(num) { 
    if(num<=1) { 
        return 1; 
    }else { 
        return num * foo(num-1); 
    } 
} 
复制代码

可是当这个函数变成了一个匿名函数时,咱们就能够利用callee来递归这个函数。

function foo(num) { 
    if(num<=1) { 
        return 1; 
    }else { 
        return num * arguments.callee(num-1); 
    } 
} 
复制代码

这个方法虽然好用,可是有一点值得注意,ECMAScript4中为了限制JS的灵活度,让JS变得严格,新增了严格模式,在严格模式中咱们被禁止不使用var来直接声明一个全局变量,固然这不是重点,重点是arguments.callee这个属性也被禁止了。不过这都不是事儿,ES6为咱们新增了不少好用的变量声明方式和新的语法糖,做为一个时髦的前端,咱们赶忙学习一些ES6的新语法吧。

若是以为文章对你有些许帮助,欢迎在个人GitHub博客点赞和关注,感激涕零!

相关文章
相关标签/搜索