函数参数: 显式参数(parameters)和隐式参数(arguments)
函数的显式参数在函数定义时列出
函数隐式参数在函数调用时传递给函数真正的值
参数规则:
js函数定义显式参数时没有指定数据类型
js函数对隐式参数没有进行类型检测
js函数对隐式参数的个数没有进行检测
函数的默认值es6允许函数参数设置默认值,即可以直接写在参数定义的后面
function fn(a,b='true'){
console.log(a,b)
}
fn('false') // false true
fn('false','no') //false no
fn('false','') //false
与解构赋值默认值结合使用
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined, 5
foo({x: 1}) // 1, 5
foo({x: 1, y: 2}) // 1, 2
foo() // 报错
函数的length属性
如果设置默认值的参数在最后,设置了默认值之后,函数的length属性就失效了一样,不再计算这个值的长度
如果设置默认值的参数在最前面,则length不会往下计算长度
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
作用域
name属性 指函数的名称
** 箭 头 函 数 =>
箭头函数会默认帮我们绑定外层this的值,所以箭头函数中的this的值和外层的this是一样的
箭头函数不能提升的,需要在使用前定义
使用const比使用var更安全,因为函数表达式始终是一个常量
如果函数部分只是一个语句,则可以省略return关键字和大括号
1: 函数体内的this对象,是指定义时的对象,并非使用的时候所在的对象
2: 不可以当作构造函数,也就是说,不可以使用new命令,不然会出错
3: 不可以使用arguments对象,该对象在函数体内不存在arguments,如果要使用,可以使用Reast
4: 不可以使用yield命令,因此箭头函数不能用作Generator函数
箭头函数中,this的指向是固定的
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
上面的这个this指向不是window,而是指向函数定义所在对象的id
箭头函数可以让setTimeout
里面的this
,绑定定义时所在的作用域,而不是指向运行时所在的作用域。
箭头函数没有自己的this ,所以不能使用call,apply.bind去改变this的指向
箭头函数可以嵌套使用
let insert = (value) => ({into: (array) => ({after: (afterValue) => {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}})});
insert(2).into([1, 3]).after(1); //[1, 2, 3]
绑定this
函数绑定运算符 是并排的两个双冒号(::) ,双冒号左边是一个对象,右边是一个函数,该运算符会自动将左边的对象作为上下文的this
绑定到右边的函数中
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}
如果双冒号左边为空,右边是一个对象的时候,会将这个方法绑定到对象中去
var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;
let log = ::console.log;
// 等同于
var log = console.log.bind(console);
由于双冒号运算符返回的还是原对象,因此可以采用链式写法。
// 例一
import { map, takeWhile, forEach } from "iterlib";
getPlayers()
::map(x => x.character())
::takeWhile(x => x.strength > 100)
::forEach(x => console.log(x));
// 例二
let { find, html } = jake;
document.querySelectorAll("div.myClass")
::find("p")
::html("hahaha");
尾调用优化 函数的最后一步调用另一个函数
function f(x){
return g(x);
}
尾调用不一定出现在函数的尾部,只要是最后一步操作就可以
尾调用优化,就是只保留内层函数的调用帧.如果所有的函数都是尾调用,那么完全可以做到每次
执行时,调用帧只有一项,这会大大节省内存
但是只有在不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法使用
尾递归 函数调用自身,称为递归,如果尾调用自身,就是尾递归
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity
只要使用尾递归,就不会发生栈溢出,节省内存