笔记 - JS函数

133 阅读3分钟

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

参考:

一道奇怪的面试题

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,全局污染消失了。