Repository navigation
JavaScript深入之类数组对象与arguments #14
Description
Activity
@eczn 哈哈,把原型指向Array.prototype后就可以调用Array.prototype上的方法,行为上确实是跟数组一样,然而Array.isArray和Object.prototype.toString不认呐😂
Reacted by zimeigui, yoky, 西觉, 贞, Eric, Sphinm, Vaan Kong, lovelyBug, lzuliuyun, WangWei and 51 moreReacted by 云层上的光|初心 and LuckyPoldiReacted by zxl7, GeorgeDad, leogogo, Dengyy, 云层上的光|初心 and LuckyPoldi233 很接近 但是还是有所区别
Reacted by GloryLin and Wang ZhangArray.prototype.concat.apply([], arguments)
这个是不是写错了,不应该是arguments@stoneyallen 十分感谢指出,确实是写错了。o( ̄▽ ̄)d
谢谢楼主的分享!我把您的文章里的 demo 全敲了一遍,有两个地方不太明白,还请指教!
md格式的不太会用写的有点丑陋,还请见谅callee 属性 解决闭包经典面试题的那个例子,虽然跑通了,但不明白是什么意思?
这是什么写法,不太懂??
(data[i] = function () { console.log(arguments.callee.i) }).i = i;传递参数里面,demo 没有跑通
`
function foo(){ bar.apply(this,arguments); // 这句的意思是把 bar的参数 传递给 foo 吗? 如果是的话,下面会打印出 3 , console.log(arguments.callee.length); // 0 } function bar(a,b,c){ console.log(arguments); // [] console.log(arguments.callee.length); // 3 } foo() bar()`
还有楼主应该在补充讲一下,arguments还有一个属性 caller 指向 调用当前函数的函数的引用
哈哈,那我把我的回复再回复一遍哈,如果以后有相同的问题,大家也都可以看到~
Reacted by hugeorange, Roronoa Zoro and shuxin关于第一个问题,写个简单例子:
var fun1 = function(){} fun1.test = 'test'; console.log(fun1.test)
函数也是一种对象,我们可以通过这种方式给函数添加一个自定义的属性。
这个解决方式就是给 data[i] 这个函数添加一个自定义属性,这个属性值就是正确的 i 值。Reacted by hugeorange, Run, StEve YoUng, wuxiaobin, xc, 西觉, zzzzzjp, jingke, hankzhuo, lzuliuyun and 19 moreReacted by ShingLi, cling, zjxtues and Song WenboReacted by zjxtues and Song Wenbo关于第二个问题,是把foo的参数传递给bar,可以看这个跑通的例子:
function foo() { bar.apply(this, arguments); } function bar(a, b, c) { console.log(a, b, c) } foo(1, 2, 3)
Reacted by hugeorange, zzzzzjp and cheng0821解释的很详细!!我再补充点
类数组检测
function isArrayLike(o) { if (o && // o is not null, undefined, etc. typeof o === 'object' && // o is an object isFinite(o.length) && // o.length is a finite number o.length >= 0 && // o.length is non-negative o.length===Math.floor(o.length) && // o.length is an integer o.length < 4294967296) // o.length < 2^32 return true; // Then o is array-like else return false; // Otherwise it is not }
arguments
- arguments的长度只与实参的个数有关,与形参定义的个数没有直接关系。
- arguments 有一个Symbol(Symbol.iterator)属性这个表示该对象是可迭代的
思考
- 字符串可以像类数组一样操作是因为js自动包装成String对象的原因,String对象照上面检测函数也是类数组对象。不过因为本身值不能被改变,所以给指定下标赋值不会改变。
Reacted by XIAO_LIN_123@gnipbao 非常感谢补充!o( ̄▽ ̄)d
这个类数组对象的判断方法应该是来自《JavaScript权威指南》吧,很多库比如 underscore 和 jQuery 中也有对数组和类数组对象的判断,比如 jQuery 的实现:
function isArrayLike(obj) { // obj必须有length属性 var length = !!obj && "length" in obj && obj.length; var typeRes = type(obj); // 排除掉函数和Window对象 if (typeRes === "function" || isWindow(obj)) { return false; } return typeRes === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj; }
underscore 的实现:
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var isArrayLike = function(collection) { var length = collection.length; return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; };
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var isArrayLike = function(collection) {
var length = collection.length;
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
这种判断方式 array 也会返回 true@dongliang1993 确实是这样的, jQuery 和 underscore 的 isArrayLike 都是既判断数组又判断类数组对象的~
22 remaining items
@HuangQiii 感谢指出哈,这里写错了,应该是
数组@AngellinaZ 其实是 (...).i = i 给函数添加了 i 属性,然后通过 arguments.callee.i 获取了这个属性值:
(data[i] = function () { console.log(arguments.callee.i) }).i = i;
就相当于:
data[i] = function () { console.log(arguments.callee.i) data[i].i = i;
Reacted by AngellinaZ, 贞, xiaoping wu , wd2010, 宝宝宝, Miller Shi, pengzhuoxin, beexpectedone, Jackywa124, shannon-z and 1 morevar 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我想问下, 按照之前的文章,AO是在执行函数的时候才进行初始化,然后在函数执行的过程中改变AO。这行代码 data[0].i=0 执行的时候,还没有执行 data[0],所以这时候还没有进入函数执行上下文,那么i是怎么保存到 data[0] Context 的 AO中的?
@EtheriousNatsu i 的值是存放在 data[i].i 中的,当执行 data[0]() 的时候,此时相当于:
var data = [ {i: 0}, {i: 1}, {i: 2} ] function() { console.log(data[0].i) }
这行代码 data[0].i=0 执行的时候,虽然没有执行 data[0],但是 data[i] = function () { console.log(arguments.callee.i) } 已经执行了,i 的值就保存在这个函数对象中。
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
作者你好,你说的这里利用闭包,我没太理解,执行结果我是理解的。
上面循环完的结果就是下面这样data[0] = function(){ console.log(arguments.callee.i) } data[0].i = 0; data[1] = function(){ console.log(arguments.callee.i) } data[1].i = 1; data[2] = function(){ console.log(arguments.callee.i) } data[2].i = 2;
所以
data[0](); //0 data[1](); //1 data[2](); //2
因为我理解的闭包就是在函数中声明了某个变量,然后在函数内部返回了一个子函数且子函数使用了这个变量;
😂然后上面的例子我感觉就是访问了一个(函数)对象的属性--------分割线-------
是我看错了😂,没认真看标题
讲个闭包经典面试题使用 callee 的解决方法:,原来你说的意思是利用callee达到闭包的效果,并不是说利用闭包大大,请问这里是不是应该是形参和arguments不会共享?arguments代表实参的值呀
传入的参数,实参和 arguments 的值会共享,当没有传入时,实参与 arguments 值不会共享
除此之外,以上是在非严格模式下,如果是在严格模式下,实参和 arguments 是不会共享的。
`
function foo(name, age, sex, hobbit) {
'use strict';
console.log(name, arguments[0]); // name name// 改变形参 name = 'new name'; console.log(name, arguments[0]); // new name name // 改变arguments arguments[1] = 'new age'; console.log(age, arguments[1]); // age new age // 测试未传入的是否会绑定 console.log(sex); // undefined sex = 'new sex'; console.log(sex, arguments[2]); // new sex undefined arguments[3] = 'new hobbit'; console.log(hobbit, arguments[3]); // undefined new hobbit}
foo('name', 'age')
`sex = 'new se
x';console.log(sex, arguments[2]); // new sex undefined
大大,请问这里是不是应该是形参和arguments不会共享?arguments代表实参的值呀
传入的参数,实参和 arguments 的值会共享,当没有传入时,实参与 arguments 值不会共享
除此之外,以上是在非严格模式下,如果是在严格模式下,实参和 arguments 是不会共享的。
`
function foo(name, age, sex, hobbit) {
'use strict';
console.log(name, arguments[0]); // name name// 改变形参 name = 'new name'; console.log(name, arguments[0]); // new name name // 改变arguments arguments[1] = 'new age'; console.log(age, arguments[1]); // age new age // 测试未传入的是否会绑定 console.log(sex); // undefined sex = 'new sex'; console.log(sex, arguments[2]); // new sex undefined arguments[3] = 'new hobbit'; console.log(hobbit, arguments[3]); // undefined new hobbit}
foo('name', 'age')
`当没有传入时,实参与 arguments 值不会共享
// 测试未传入的是否会绑定
console.log(sex); // undefinedsex = 'new sex';
console.log(sex, arguments[2]); // new sex undefined
(data[i] = function () {
console.log(arguments.callee.i)
})
js 的赋值语句会返回值,比如上面就会返回
function () {
console.log(arguments.callee.i)
}`
function test(a, b, c = 10) {
console.log(arguments); // [1, 2]
console.log(a, b, c); // 1 2 10
arguments[0] = 2; // [2, 2]
console.log(a); // 1
a = 3; // 3
console.log(arguments); // [2, 2]
b = 3;
console.log(arguments[1]); // [2, 2]
c = 20;
console.log(arguments); // [2, 2]
}test(1, 2);
`
目前浏览器和node环境测试表明 实参和arguments之间没有什么关联了。有人解释下吗`
function test(a, b, c = 10) {
console.log(arguments); // [1, 2]
console.log(a, b, c); // 1 2 10
arguments[0] = 2; // [2, 2]
console.log(a); // 1
a = 3; // 3
console.log(arguments); // [2, 2]
b = 3;
console.log(arguments[1]); // [2, 2]
c = 20;
console.log(arguments); // [2, 2]
}test(1, 2);
`
目前浏览器和node环境测试表明 实参和arguments之间没有什么关联了。有人解释下吗MDN找到答案了,是因为我使用了参数默认值。
在严格模式下,剩余参数、默认参数和解构赋值参数的存在不会改变 arguments对象的行为,但是在非严格模式下就有所不同了。
当非严格模式中的函数没有包含剩余参数、默认参数和解构赋值,那么arguments对象中的值会跟踪参数的值(反之亦然)
只有非严格模式下,且形参中没有rest参数、默认值和结构赋值时 arguments 才会与参数绑定。
function foo() { bar.apply(this,arguments) }
这个里面的this 是谁的this呀,为啥这样写arguments 和对应参数的绑定-部分
不应该是形参和arguments的值是共享的吗?
实参是函数调用时传递的值,给形参进行初始赋值,函数中name值的改变应该是形参的值吧- 您好!您的邮件已收到,我会尽快查收!若有急事请拨打我的电话:13668913609(663609)谢谢!
- 这是来自QQ邮箱的假期自动回复邮件。你好,你的邮件我收到,谢谢。
类数组对象
所谓的类数组对象:
举个例子:
即便如此,为什么叫做类数组对象呢?
那让我们从读写、获取长度、遍历三个方面看看这两个对象。
读写
长度
遍历
是不是很像?
那类数组对象可以使用数组的方法吗?比如:
然而上述代码会报错: arrayLike.push is not a function
所以终归还是类数组呐……
调用数组方法
如果类数组就是任性的想用数组的方法怎么办呢?
既然无法直接调用,我们可以用 Function.call 间接调用:
类数组转数组
在上面的例子中已经提到了一种类数组转数组的方法,再补充三个:
那么为什么会讲到类数组对象呢?以及类数组有什么应用吗?
要说到类数组对象,Arguments 对象就是一个类数组对象。在客户端 JavaScript 中,一些 DOM 方法(document.getElementsByTagName()等)也返回类数组对象。
Arguments对象
接下来重点讲讲 Arguments 对象。
Arguments 对象只定义在函数体中,包括了函数的参数和其他属性。在函数体中,arguments 指代该函数的 Arguments 对象。
举个例子:
打印结果如下:
我们可以看到除了类数组的索引属性和length属性之外,还有一个callee属性,接下来我们一个一个介绍。
length属性
Arguments对象的length属性,表示实参的长度,举个例子:
callee属性
Arguments 对象的 callee 属性,通过它可以调用函数自身。
讲个闭包经典面试题使用 callee 的解决方法:
接下来讲讲 arguments 对象的几个注意要点:
arguments 和对应参数的绑定
传入的参数,实参和 arguments 的值会共享,当没有传入时,实参与 arguments 值不会共享
除此之外,以上是在非严格模式下,如果是在严格模式下,实参和 arguments 是不会共享的。
传递参数
将参数从一个函数传递到另一个函数
强大的ES6
使用ES6的 ... 运算符,我们可以轻松转成数组。
应用
arguments的应用其实很多,在下个系列,也就是 JavaScript 专题系列中,我们会在 jQuery 的 extend 实现、函数柯里化、递归等场景看见 arguments 的身影。这篇文章就不具体展开了。
如果要总结这些场景的话,暂时能想到的包括:
...
欢迎留言回复。
下一篇文章
JavaScript深入之创建对象的多种方式以及优缺点
深入系列
JavaScript深入系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。