JS基础整理(1)—函数和闭包
这段空闲时间,打算整理一些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 => Window {0: global, window: Window, self: Window, 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();
发表评论 (审核通过后显示评论):