Function 函数
对于JS而言,万物皆为对象。
在 JavaScript中,函数是头等(first-class)对象,因为它们可以像任何其他对象一样具有属性和方法。它们与其他对象的区别在于函数可以被调用。简而言之,它们是Function对象。
具名函数
function f_name(para1, para2, ...){
statement;
return value;
}
匿名函数
去掉函数名则为匿名函数
也称为函数表达式
let f = function(x,y){return x+y;}
tips: 不写return, 具名和匿名函数就没有返回值。
箭头函数
let f = para => (statement)
e.g.
let f1 = x => x*x;
let f2 = (x,y) => x+y
let f3 = (x,y) => {return x+y};
let f4 = (x,y) => ({name:x, age:y});
如果箭头函数返回对象,则需要在对象外加上圆括号,否则报错
构造函数
(基本没人用)
let f = new Function('x','y','return x+y');
JS中万物皆为对象,但所有函数都由Function构造,Object,Array,Function都是。
函数调用
e.g.
let fn = {} => console.log('hi');
let fn2 = fn;
fn2();
fn,fn2只保存了匿名函数的地址,他们都只是该匿名函数的引用。
调用时机
参考for loop 例子
let i = 0;
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i);
},0);
}
// < 6 6 6 6 6 6
setTimeout()之中的代码会在整个块执行完毕后执行,此时i已为6,打印6次6。
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i);
},0);
}
// < 0, 1, 2, 3, 4, 5
let i作为一个仅在块级作用域中存在的局部变量,作用域会被创建5次,也即最后打印时存在五个不同的let i。
此外立即执行函数有同样效果,创建多次作用域即可打印多个不同的let i。
全局变量
- 在顶级作用域声明的变量是全局变量
- 挂在window上的属性是全局变量
除此二者之外皆为局部变量。
作用域规则
如果多个作用域拥有同名变量a:
- 查找a声明时,向上取最近的作用域
- 即为就近原则
- 查找过程与函数执行无关,但函数执行会影响a的值
闭包
参考:
关于返回值
每个函数都有返回值,函数执行完毕才会返回,只有函数有返回值。
如果函数没有return,默认返回值为undefined。
call stack 调用栈
调用函数 -> 函数所在环境压入调用栈(数组) -> 执行完毕 -> 环境被弹出调用栈 -> 返回原本环境继续执行
参考: developer.mozilla.org/zh-CN/docs/…
爆栈
压入调用栈的函数环境过多会引发爆栈。
比如递归阶乘(10000),就会往调用栈中压入一万个函数环境。
每个浏览器的调用栈长度不一:
- Chrome: 12578
- Firefox: 26773
- Node: 12536 由于使用了相同的JS引擎,Chrome和Node调用栈长度较为接近,但栈内初始内容不一致,使得最终可用的栈长度不同。
this
参考:
- segmentfault.com/a/119000001… 箭头函数没有this.
一道奇怪的面试题
function foo(arg){
this.a = arg;
return this
};
var a = foo(1);
var b = foo(10);
console.log(a.a); // ?
console.log(b.a); // ?
-----------------------答案---------------------
答案 : undefined 10 解析 :考点 1. 全局污染 2. this默认绑定
这道题很有意思,问题基本上都集中在第一undefined上,这其实是题目的小陷阱,但是追栈的过程绝对精彩
让我们一步步分析这里发生了什么:
foo(1)执行,应该不难看出是默认绑定吧 , this指向了window,函数里等价于 window.a = 1,return window;
var a = foo(1) 等价于 window.a = window , 很多人都忽略了var a 就是window.a ,将刚刚赋值的 1 替换掉了。
所以这里的 a 的值是 window , a.a 也是window , 即window.a = window ; window.a.a = window;
foo(10) 和第一次一样,都是默认绑定,这个时候,将window.a 赋值成 10 ,注意这里是关键,原来window.a = window ,现在被赋值成了10,变成了值类型,所以现在 a.a = undefined。(验证这一点只需要将var b = foo(10);删掉,这里的 a.a 还是window)
var b = foo(10); 等价于 window.b = window;
本题中所有变量的值,a = window.a = 10 , a.a = undefined , b = window , b.a = window.a = 10;
比较让我在意的地方是var a === window.a;ES6之后var应该用的越来越少了,如果这道题的var a, var b都改成 let a, let b,结果为10,10,全局污染消失了。