1、明确一个观点,函数实际上是一个对象,所以函数名其实是一个保存着函数对象指针的变量。
2、箭头函数不能使用arguments、super和new.target,也不能用作构造函数,也没有prototype属性。
3、任何函数都会暴露一个只读的name属性,返回函数名,其中箭头函数返回的是空字符串,而匿名函数返回的是"anonymous"。
4、函数传入的参数的类型和个数都是没有限制的,参数只是为了方便才写出来,都可以在arguments对象中获取到。
5、如果修改arguments中的值,则对应的命名参数也会跟着修改,根据《Javascript高级程序设计》第四版中所说的,如果修改命名参数是不会影响arguments中的值的,但测试结果却是会的:
function testArguments (a, b) {
arguments[0] = 1
console.log(a,b)
b = 4
console.log(arguments)
}
testArguments(2,3)
//1 3
//[Arguments] { '0': 1, '1': 4 }
另外在有设置参数的默认值的情况下,修改命名参数就不会影响到arguments中的值,而修改arguments中的值也不会影响到命名参数, 就是说arguments始终以接收到的参数为准:
function testArguments (a, b = 100) {
arguments[0] = 1 //arguments[0]修改了, 但a不受影响
console.log(a,b)
b = 4 //a b的值改了, 但arguments[1]的值不受影响
console.log(a, b, arguments)
}
testArguments(2,3)
//2 3
//2 4 [Arguments] { '0': 1, '1': 3 }
好了,现在确实如书中所说,修改命名参数不会影响arguments中的值,但为何修改arguments的值不会同步到命名参数呢?希望有大佬解答。。
6、扩展参数是在函数调用的时候使用的,表示将传入的数组展开成一个个参数。收集参数是在函数定义的时候使用的,表示将输入的多个数据集成一个数组。
7、arguments.callee返回该函数的指针,也就是说可以通过arguments.callee来调用本函数,如果本函数在外部被重写了,使用原函数名来调用的话会调用被重写的函数,而如果使用arguments.callee会调用原来的函数。
8、funName.caller可以返回调用该函数的对象。
9、没有重载,因为对参数没有限制,如果想要模拟重载,可以用arguments对输入的参数进行判断。
10、new.target可以检测函数是普通调用还是通过new调用, 如果是new调用则new.target将引用被调用的构造函数,否则是undefined。
11、函数没有指明上下文的调用this为window,在严格模式下为undefined。
12、尾调用是指函数return的值也是一个函数。按照ES6以前的机制,如果有多层嵌套,则会将调用的函数层层入栈,然后层层出栈。在ES6优化之后,他发现你都要return了,并且return的值也是一个函数,那原来的函数就可以直接出栈了呀,只保留当前执行的函数就可以了。不过优尾调用优化也是有一定的限制的,例如return函数后不能再干别的操作,例如 return fun.toString(),return的不能是一个闭包这些情况都起不到优化的效果。
可以优化的例子:
function fib(n) {
if (n < 2) return n
return fib(n - 1) + fib(n - 2)
}
//这里虽然是尾调用,但并没有达到优化的效果,因为返回的结果还要相加
//如果求fib(1000)浏览器就承受不住了
//优化的方法有两个,换成非递归,或者将该递归拆成两个函数,一个是基础框架,一个执行递归
function fib(n) {
return fibImpl(0, 1, n)
}
function fibImpl(a, b, n) { //递归
if (n == 0) return a
return fibImpl(b, a + b, n - 1)
}
优化之后fib(1000)可以秒出结果。
13、内部函数不能直接访问到外部函数的this和arguments。这是因为每个函数在调用的时候都会创建两个特殊的变量,就是this和arguments,如果想要访问外部的,可以用别的名称的变量将外部的保存起来,例如let that = this,这样子在内部函数就可以拿到外部函数的this了(因为没有命名冲突了),(之前只是这样用,没想过为什么。),这一点在闭包中经常使用到。
14、闭包指的是那些引用了另一个函数作用域中变量的函数,通常实在嵌套函数中实现的。
15、在调用一个函数时,会为这个函数调用创建一个执行上下文,并创建一个作用域链。然后用arguments和命名参数来初始化这个函数的活动对象。外部函数的活动对象是内部函数作用域链上的第二个对象。这个作用域链一直向外串起了所有包含该调用函数的活动对象,直到全局执行上下文才终止。
16、虽然JS中没有私有对象属性的概念,但可以使用闭包来实现公共的方法,访问位于包含作用域中定义的变量。
17、可以访问私有变量的公共方法叫做特权方法,特权方法可以使用构造函数或原型模式通过自定义类型中实现,也可以使用模块模式或模块增强模式在单例对象上实现。