Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript深入之类数组对象与arguments #14

Open
mqyqingfeng opened this issue May 8, 2017 · 41 comments
Labels

Comments

@mqyqingfeng
Copy link
Owner

@mqyqingfeng mqyqingfeng commented May 8, 2017

类数组对象

所谓的类数组对象:

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

举个例子:

var array = ['name', 'age', 'sex'];

var arrayLike = {
    0: 'name',
    1: 'age',
    2: 'sex',
    length: 3
}

即便如此,为什么叫做类数组对象呢?

那让我们从读写、获取长度、遍历三个方面看看这两个对象。

读写

console.log(array[0]); // name
console.log(arrayLike[0]); // name

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

长度

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('4');

然而上述代码会报错: arrayLike.push is not a function

所以终归还是类数组呐……

调用数组方法

如果类数组就是任性的想用数组的方法怎么办呢?

既然无法直接调用,我们可以用 Function.call 间接调用:

var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }

Array.prototype.join.call(arrayLike, '&'); // name&age&sex

Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] 
// slice可以做到类数组转数组

Array.prototype.map.call(arrayLike, function(item){
    return item.toUpperCase();
}); 
// ["NAME", "AGE", "SEX"]

类数组转数组

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

var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
// 1. slice
Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"] 
// 2. splice
Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"] 
// 3. ES6 Array.from
Array.from(arrayLike); // ["name", "age", "sex"] 
// 4. apply
Array.prototype.concat.apply([], arrayLike)

那么为什么会讲到类数组对象呢?以及类数组有什么应用吗?

要说到类数组对象,Arguments 对象就是一个类数组对象。在客户端 JavaScript 中,一些 DOM 方法(document.getElementsByTagName()等)也返回类数组对象。

Arguments对象

接下来重点讲讲 Arguments 对象。

Arguments 对象只定义在函数体中,包括了函数的参数和其他属性。在函数体中,arguments 指代该函数的 Arguments 对象。

举个例子:

function foo(name, age, sex) {
    console.log(arguments);
}

foo('name', 'age', 'sex')

打印结果如下:

arguments

我们可以看到除了类数组的索引属性和length属性之外,还有一个callee属性,接下来我们一个一个介绍。

length属性

Arguments对象的length属性,表示实参的长度,举个例子:

function foo(b, c, d){
    console.log("实参的长度为:" + arguments.length)
}

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

foo(1)

// 形参的长度为:3
// 实参的长度为:1

callee属性

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

接下来讲讲 arguments 对象的几个注意要点:

arguments 和对应参数的绑定

function foo(name, age, sex, hobbit) {

    console.log(name, arguments[0]); // name name

    // 改变形参
    name = 'new name';

    console.log(name, arguments[0]); // new name new name

    // 改变arguments
    arguments[1] = 'new age';

    console.log(age, arguments[1]); // new 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 的值会共享,当没有传入时,实参与 arguments 值不会共享

除此之外,以上是在非严格模式下,如果是在严格模式下,实参和 arguments 是不会共享的。

传递参数

将参数从一个函数传递到另一个函数

// 使用 apply 将 foo 的参数传递给 bar
function foo() {
    bar.apply(this, arguments);
}
function bar(a, b, c) {
   console.log(a, b, c);
}

foo(1, 2, 3)

强大的ES6

使用ES6的 ... 运算符,我们可以轻松转成数组。

function func(...arguments) {
    console.log(arguments); // [1, 2, 3]
}

func(1, 2, 3);

应用

arguments的应用其实很多,在下个系列,也就是 JavaScript 专题系列中,我们会在 jQuery 的 extend 实现、函数柯里化、递归等场景看见 arguments 的身影。这篇文章就不具体展开了。

如果要总结这些场景的话,暂时能想到的包括:

  1. 参数不定长
  2. 函数柯里化
  3. 递归调用
  4. 函数重载
    ...

欢迎留言回复。

下一篇文章

JavaScript深入之创建对象的多种方式以及优缺点

深入系列

JavaScript深入系列目录地址:https://github.com/mqyqingfeng/Blog

JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。

@eczn

This comment has been minimized.

Copy link

@eczn eczn commented May 8, 2017

image

感觉如果类数组对象的原型指向 Array.prototype 他可以被认为是一个数组了。


image

毕竟 typeof {} 跟 typeof [] 结果是一样的。

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented May 8, 2017

@eczn 哈哈,把原型指向Array.prototype后就可以调用Array.prototype上的方法,行为上确实是跟数组一样,然而Array.isArray和Object.prototype.toString不认呐😂

default

@eczn

This comment has been minimized.

Copy link

@eczn eczn commented May 8, 2017

233 很接近 但是还是有所区别

@stoneyallen

This comment has been minimized.

Copy link

@stoneyallen stoneyallen commented May 9, 2017

Array.prototype.concat.apply([], arguments)
这个是不是写错了,不应该是arguments

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented May 9, 2017

@stoneyallen 十分感谢指出,确实是写错了。o( ̄▽ ̄)d

@hugeorange

This comment has been minimized.

Copy link

@hugeorange hugeorange commented May 10, 2017

谢谢楼主的分享!我把您的文章里的 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 指向 调用当前函数的函数的引用

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented May 10, 2017

哈哈,那我把我的回复再回复一遍哈,如果以后有相同的问题,大家也都可以看到~

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented May 10, 2017

关于第一个问题,写个简单例子:

var fun1 = function(){}

fun1.test = 'test';

console.log(fun1.test)

函数也是一种对象,我们可以通过这种方式给函数添加一个自定义的属性。
这个解决方式就是给 data[i] 这个函数添加一个自定义属性,这个属性值就是正确的 i 值。

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented May 10, 2017

关于第二个问题,是把foo的参数传递给bar,可以看这个跑通的例子:

function foo() { bar.apply(this, arguments); }

function bar(a, b, c) { console.log(a, b, c) }

foo(1, 2, 3)
@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented May 10, 2017

关于caller,直接截图MDN哈:

default

@gnipbao

This comment has been minimized.

Copy link

@gnipbao gnipbao commented Jun 2, 2017

解释的很详细!!我再补充点

类数组检测

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

image
如图可以看出

  1. arguments的长度只与实参的个数有关,与形参定义的个数没有直接关系。
  2. arguments 有一个Symbol(Symbol.iterator)属性这个表示该对象是可迭代的

思考

image

  • 字符串可以像类数组一样操作是因为js自动包装成String对象的原因,String对象照上面检测函数也是类数组对象。不过因为本身值不能被改变,所以给指定下标赋值不会改变。
@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Jun 4, 2017

@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;
};
@dongliang1993

This comment has been minimized.

Copy link

@dongliang1993 dongliang1993 commented Aug 9, 2017

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

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Aug 9, 2017

@dongliang1993 确实是这样的, jQuery 和 underscore 的 isArrayLike 都是既判断数组又判断类数组对象的~

@dongliang1993

This comment has been minimized.

Copy link

@dongliang1993 dongliang1993 commented Aug 9, 2017

这样的话,感觉 _.each 函数就有点问题了,
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context)
var i, length
if (isArrayLike(obj)) { // const obj = {a: 1, length: 1} 会直接进入到下面的循环,变成了obj[0],obj[1]
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj)
}
} else {
const keys = _.keys(obj)
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj) // (value, key, obj)
}
}
return obj
};
其实应该是要用 for in 来遍历吧?

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Aug 9, 2017

@dongliang1993 没有问题呐,类数组对象是可以使用 for 循环遍历的呐~

@dongliang1993

This comment has been minimized.

Copy link

@dongliang1993 dongliang1993 commented Aug 9, 2017

@mqyqingfeng 我的意思是,如果是 obj = { name: 'xiaoming', length: 1 } 这样的类数组对象,isArrayLike 判断为 true,然后进入相应的迭代器,用 for 循环是 iteratee(obj[i], i, obj) 这样的,可是 i 是 0, 1, 2...这样的数字,那 obj[0],obj[1] 都是 undefined呀,可是 obj 明明是有 'name' 这个属性的。不知道大佬有没有看明白我的意思。。。

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Aug 9, 2017

@dongliang1993 确实会出现这样的问题, { name: 'xiaoming', length: 1 } 可以通过 underscore 的 isArrayLike 验证,但是在 each 函数中,obj[0] 为 undefined。关键还是在于这个对象并不是一个严格意义上的类数组对象,isArrayLike 可以校验出我们开发中会用到的 arguments 对象,满足我们的开发需求,但是对于我们故意创造出的对象,确实也会漏掉~

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Aug 9, 2017

@dongliang1993 如果用 for in 遍历类数组对象的话,length 和 自定义的一些属性也会被遍历到,也会导致问题吧~

@huangmxsysu

This comment has been minimized.

Copy link

@huangmxsysu huangmxsysu commented Sep 4, 2017

好像说在函数中传递arguments给任何参数,将导致Chrome和Node中使用的V8引擎跳过对其的优化,这也将使性能相当慢。
请问博主知道其中的原因么?

@huangmxsysu

This comment has been minimized.

Copy link

@huangmxsysu huangmxsysu commented Sep 4, 2017

忘了在哪里看到的了

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Sep 5, 2017

@huangmxsysu 这个是来自 blueBird 的 wiki,https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments,以前也查过这个问题,之所以降低性能,是因为:

Leaking the arguments object kills optimization because it forces V8 to instantiate the arguments as a Javascript object instead of optimizing them into stack variables.

当时想不明白的是为什么 [].slice.call(arguments) 依然会导致性能损失,现在想想,可能是因为将 this 指向 arguments,所以依然保持了对 arguments 的引用吧

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Sep 5, 2017

其实本篇应该添加 leaking arguments 的部分,告诉大家不要乱用 arguments 😂

@huangmxsysu

This comment has been minimized.

Copy link

@huangmxsysu huangmxsysu commented Sep 5, 2017

噢好像是因为这个原因哈!
是啊,昨天看到你数组去重那篇中有个_.union函数,就想着应该讲arguments转换一下,类似这样

function union() {
	//最好能把arguments转换一下
	var args = new Array(arguments.length);
	for(var i = 0; i < args.length; ++i) {
	    args[i] = arguments[i];
	}
    return unique(flatten(args, true, true));
}
@huangmxsysu

This comment has been minimized.

Copy link

@huangmxsysu huangmxsysu commented Sep 5, 2017

flatten那篇

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Sep 5, 2017

@huangmxsysu 我并不确定直接传递 arguments 会不会导致性能损失,因为在 wiki 里也只是讲了以下三种情况会导致 leaking arguments:

function leaksArguments1() {
    return arguments;
}

function leaksArguments2() {
    var args = [].slice.call(arguments);
}

function leaksArguments3() {
    var a = arguments;
    return function() {
        return a;
    };
}

而且也讲到了:

STRICTLY fn.apply(y, arguments) is ok, nothing else is, e.g. .slice. Function#apply is special.

我简单做了个试验:

function otherFunc(a, b) {
  "use strict";
  return a + b;
}

function withArguments() {
    "use strict";
    var a = arguments;
    return otherFunc.apply(null, arguments);
}

function withLeakingArguments() {
  "use strict";
  var a = arguments;
  return otherFunc.apply(null, [].slice.call(arguments));
}

function withCopy() {
  "use strict";
  var a = [];
  var i, len = arguments.length;
  for (i = 1; i < len; i += 1) {
    a[i - 1] = arguments[i];
  }
  return otherFunc.apply(null, a);
}

console.time('with arguments')
for (var i = 0; i < 100000; i++) {
    withArguments(1, 2)
}
console.timeEnd('with arguments')

console.time('with leaking arguments')
for (var i = 0; i < 100000; i++) {
    withLeakingArguments(1, 2)
}
console.timeEnd('with leaking arguments')

console.time('with copy')
for (var i = 0; i < 100000; i++) {
    withCopy(1, 2)
}
console.timeEnd('with copy')

一次试验的结果为:

default

当然这个测试可能并不准确,并不能确定就是 leaking arguments 导致的性能损失,不过我想直接使用 arguments 的话,底层应该会有优化

@veedrin

This comment has been minimized.

Copy link

@veedrin veedrin commented Nov 2, 2017

@mqyqingfeng

let  elements = document.getElementsByClassName('box');

Array.prototype.splice.call(elements, 0);

// Uncaught TypeError: Cannot assign to read only property 'length' of object '#<HTMLCollection>'

// slice方法可以
@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Nov 3, 2017

@veedrin 感谢指出哈~ 确实没有注意到这个问题。

如果是普通的类数组对象,使用 splice 是可以转换的,比如:

var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] 

然而对于 HTMLCollection,length 属性为只读,splice 底层还是会修改 length 的长度,这才导致了报错。

@jxZhangLi

This comment has been minimized.

Copy link

@jxZhangLi jxZhangLi commented Dec 5, 2017

@mqyqingfeng 为什么你们判断是不是类数组,isArrayLike 函数需要这么多判断,直接查看它的 是否为 [object object] 和 length 存在就行了吗?难道在哪些情况下会有问题吗?这个我还没遇到过哦,请指教指教!

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Dec 6, 2017

@jxZhangLi 哈哈,都可以啦,因为对于类数组对象的判断,其实可以很宽,也可以很严格,比如说判断 length 属性存在,更严格的话,可以判断 length 属性值必须是数字,再严格的话,可以判断 length 属性值必须大于 0,再再严格的话,可以判断 length -1 属性值必须存在,看 API 的设计者想严格到什么程度啦,在满足当前业务的情况下,即使设计的很宽松,也是可以的,但是作为一个库的设计者的话,还是应该设计的更加严格一点~

@ClarenceC

This comment has been minimized.

Copy link

@ClarenceC ClarenceC commented Dec 25, 2017

这篇文章的内容比上那几章瞬间简单了很多啊😂,没有源码没有模拟.不过精彩的地方还是在评论区,很多学习的地方啊.

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Dec 26, 2017

@ClarenceC 有读者说看我的文章没有看懂,看评论看懂了😂

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Jan 16, 2018

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Jan 16, 2018

补充一点箭头函数和 arguments 相关的规范部分:

函数初始化的时候,如果是箭头函数,会设置内部属性 [[ThisMode]] 为 'lexical'

If kind is Arrow, set the [[ThisMode]] internal slot of F to lexical.

创建函数上下文的时候:

If the value of the [[ThisMode]] internal slot of func is lexical, then

NOTE Arrow functions never have an arguments objects.

Let argumentsObjectNeeded be false.

@HuangQiii

This comment has been minimized.

Copy link

@HuangQiii HuangQiii commented Feb 1, 2018

_20180201220715

这里应该是数组,类数组本来就是对象~

@AngellinaZ

This comment has been minimized.

Copy link

@AngellinaZ AngellinaZ commented Feb 5, 2018

(data[i] = function () { console.log(arguments.callee.i) }).i = i;

请问大大,arguments.callee.i是给函数添加i属性,那外围的(...).i = i 是什么意思

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Feb 8, 2018

@HuangQiii 感谢指出哈,这里写错了,应该是数组

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Feb 8, 2018

@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;
@EtheriousNatsu

This comment has been minimized.

Copy link

@EtheriousNatsu EtheriousNatsu commented Mar 21, 2018

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

我想问下, 按照之前的文章,AO是在执行函数的时候才进行初始化,然后在函数执行的过程中改变AO。这行代码 data[0].i=0 执行的时候,还没有执行 data[0],所以这时候还没有进入函数执行上下文,那么i是怎么保存到 data[0] Context 的 AO中的?

@mqyqingfeng

This comment has been minimized.

Copy link
Owner Author

@mqyqingfeng mqyqingfeng commented Mar 28, 2018

@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 的值就保存在这个函数对象中。

@tobeapro

This comment has been minimized.

Copy link

@tobeapro tobeapro commented Oct 31, 2019

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达到闭包的效果,并不是说利用闭包

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.