这段空闲时间, 打算整理一些 JS 非常基础, 但是又常常搞不清记不住的知识点.
第一篇, 先整理一下 JS 的函数, 特别是闭包的知识.
函数的声明方式
闭包
匿名函数
函数的三种定义方式
函数声明: 声明一个变量, 把函数对象赋值给它, 会提升到脚本, 代码块顶部
function f(x){ return x; }
函数表达式: 不会声明变量, 不会提升
let f = function(x){ return x; }
箭头函数: 从定义自己的环境继承 this 的值
let f = (x,y)=>{ return x+y; }
闭包
语法作用域 (lexical scoping) 规则: JavaScript 函数执行时, 使用的是定义函数时生效的变量作用域, 而不是调用函数时生效的变量作用域.
闭包(closure): 函数对象与作用域组合起来解析函数变量的机制.
例子一:
- let scope = "global scope";
- function checkScope(){
- let scope = "local scope":
- function f(){ return scope; }
- return f();
- }
- checkScope(); // 返回什么?
例子一:
- let scope = "global scope";
- function checkScope(){
- let scope = "local scope":
- function f(){ return scope; }
- return f;
- }
- checkScope()(); // 返回什么?
第一个例子中, checkScope()声明一个局部变量, 定义了一个返回该变量的值的函数并调用它;
第二个例子中, checkScope()中的 () 转移到了外部, 返回的是嵌套函数, 在定义它的函数外部调用这个嵌套函数.
结果: 两个例子都会返回 "local scope".
分析: 根据词法作用域规则, 定义函数 f()时, scope 绑定的值是 "local scope", 在 f()执行时仍然有效.
使用
匿名函数(立即调用函数表达式)
创建函数的私有状态
闭包可以捕获一次函数调用的局部变量, 可以将这些变量作为私有状态.
- let f = (function(){
- let counter = 0; // 以下函数的私有状态
- return function() { return counter++; }
- }())
- f(); // 0
- f(); // 1
如果同一个外部函数中有多个闭包, 则它们共享相同的作用域. 但是, 如果使用循环创建多个闭包, 需要注意一个问题:
- function f(){
- let funcArray = [];
- for(var i=0; i <10; i++){
- funcArray[i] = () => i;
- }
- return funcArray;
- }
- let funcs = f();
- funcs[3](); // 结果是 3 吗?
结果: 10
分析: f()中创建是 10 个闭包, 它们共享同一个 var 声明的变量 i,for 循环结束时, i 的值是 10. 因为闭包关联的作用域是活的, 所以 10 个闭包都共享了 10 个这值.
解决: 把 var 改成 const 或 let, 因为通过 var 声明的变量作用域是整个函数体, ES6 后增加的 let 和 const 的作用域是块极的, 每次循环都会定义一个与其它循环不同的独立作用域.
- function f(){
- let funcArray = [];
- for(let i=0; i <10; i++){
- funcArray[i] = () => i;
- }
- return funcArray;
- }
- let funcs = f();
- funcs[3](); // 3
- funcs[7](); // 7
使用闭包时还需要注意一个问题:
箭头函数: 会继承包含它们的函数中的 this 值, 使用箭头函数创建的闭包可以访问外部的 this 值;
function: 它的 this 值指向全局对象或者 undefined
参考以下三个例子:
- // 例子 1
- let o = {
- a: 1,
- m: function(){
- function f() { return this.a; }
- return f();
- }
- }
- o.m(); // undefined
- // f()中的 this => Windows {0: global, Windows: Windows, self: Windows, document: document, name: "", location: Location, ...}
- // 例子 2
- let o = {
- a: 1,
- m: function(){
- f = () => this.a;
- return f();
- }
- }
- o.m(); // 1
- // 箭头函数中的 this => {a: 1, m: ƒ}
- // 例子 3
- let o = {
- a: 1,
- m: function(){
- let self = this; // 把 this 的值保存在变量中, 让以下嵌套函数可以访问外部 this 的值
- function f() { return self.a; }
- return f();
- }
- }
- o.m(); // 1
彩蛋
分享最近看到的一道题目:
- function Foo() {
- getName = function () {
- console.log(1);
- };
- return this;
- }
- Foo.getName = function () {
- console.log(2);
- };
- Foo.prototype.getName = function () {
- console.log(3);
- };
- var getName = function () {
- console.log(4);
- };
- function getName() {
- console.log(5);
- }
- // 以下调用会得到什么结果?
- Foo.getName();
- getName();
- Foo().getName();
- getName();
- new Foo.getName();
- new Foo().getName();
- new new Foo().getName();
来源: http://www.jianshu.com/p/c776847d0422