看过好多次书了,以为已经理解了,但是在实际操作中还是不能做的很好,那就再看一遍并记录方便查看。感觉还是有点模糊,再有理解后面在更改吧。
更新记录
- 闭包中离不开的作用域概念。 2019-09-14
作用域
变量作用域解析方式`LHS` '`RHS`
-
LHS
左侧查询let a = 2; // 显示声明 function getName(b){ // b 隐式声明 return b; }
-
RHS
右侧解析查询let c = a; // =a 查找a function getName(b){ return b; // ...b 返回值方式的右侧引用 } getName("hello"); // 查找 getName console.log(age); // 不成功的 RHS 引用age报错 `ReferenceError`
-
影响作用域的方式(已不推荐用,做记录理解)
eval()
可以解析字符串eval("console.log(2)"); // 可以解析字符串 function getAge(){ return age; } console.log(getName()); // 报错 `age` is not defined console.log(getName(eval("var age=12"))); // 12
with
接受一个对象,内部对属性进行赋值操作。let obj = {age:12}; with(obj){ age = 23; } console.log(obj); // {age:23} with(obj){ name = "hello"; age = 23; } console.log(obj); // {age:23} window.name = "hello" // 当前对象中没有的属性会被定义到全局作用域对象中
闭包
-
概念理解
我理解为在其他地方不能直接调用到的函数所存在的作用域的一个整体构成。
使用了回调函数就是在使用闭包。
-
示例说明
从例子中看懂闭包的实际应用,其实我们平时已经在这样做。(function one(){ //do something alert("hello"); })();
直接执行执行函数,在其他地方是不能调用到的(也可用匿名函数,但最好有函数名,这样别人就知道你这个是干嘛的)。隐式调用(执行)
不够清楚?在加一个内部的执行函数。
(function one(){ var str = "something"; function oneInner(){ alert(str); } oneInner(); alert("hello"); })();
内部函数,内部调用,其他地方是不能调用的。
继续进化——>
function one(){ var str = "something"; function oneInner(){ alert(str); } oneInner(); alert("hello"); }; one();
这次进行了函数声明后,显示的调用该函数,它的内部方法在起内部直接调用,和上面不同之处:我们可以在其他地方调用该方法(“one()”)。
我们就要调用它的内部方法oneInner(),
继续改———>function one(){ var str = "something"; function oneInner(){ alert(str); } return oneInner; }; var inner = one(); inner();
在one函数中将其内部函数oneInner作为返回值,不要写成return oneInner();这样是返回oneInner()函数执行的结果。好了在其他的地方我们可以访问到内部函数oneInner()了。
类似的变形写法,供参考。
function one(){ var str = "something"; function oneInner(){ alert(str); } two(oneInner); }; function two(fn){ fn(); } one();
其实就是值传递了,把函数作为参数。一个应用场景就是该函数要在不同条件下,传递不同参数执行完成不同的结果;
var fn; function one(){ var str = "something"; function oneInner(){ alert(str); } fn = oneInner; }; function two(){ fn(); } one(); two();
这个传递的过程就比较明显了,声明一个全局变量fn,将one中的内部函数oneInner赋值,这样就可以调用它获得执行结果了。
-
其他补充说明
关于for循环中的定时器的执行,这个我想已经见多很多了。for(var i=0;i<5;i++){ setTimeout(function(){ console.log(i); },1000); }
我们计划的是一每秒输出i,输出:0,1,2,3,4。结果却是输出了五次:5。这根JavaScript的执行代码的机制有关,之前看过系列的文章:js按序执行,但是对于延时函数,并不是在主队列中,他是放在一个附属队列中等待执行,那就是只有等主队列中的程序执行完,才会去查附属队列中是不是有待执行的程序。
好,那我们可以想一想延时函数,如果主队里里有很多程序执行,延时函数早已完成时间延迟等待在附属队列中。等主队列中程序执行完,附属队列的程序按序执行并不能达到延时的效果。我们要解决该问题,有几种方式,以此记录:
(1)、let 声明、作用域劫持,就是上一个i永远都是上一个i的值,与下一个i没关系。感觉就是再次声明了i,前面的i不知道去哪了。for(let i=0;i<5;i++){ setTimeout(function(){ console.log(i); },1000); }
(2)、除了let,肯定有老式的处理方法。使用闭包,闭包的东西外界是不能访问的,除非传递出来。
for(var i=0;i<5;i++){ (function(j){ setTimeout(function(){ console.log(j); },1000); })(i); }
看完闭包看这个应该挺清楚的了。外部for循环声明的i,作为参数传递进去匿名函数中(闭包)。闭包中的函数获取到值为j,那么j就是内部的东西,外界访问不到,此次执行匿名函数已经加入到主队里中,且附属队里的延时函数与此有关联变量j;后续for循环的值,也是再次执行该匿名函数,即再次往主队列中加入,就是他们彼此之间已经不相关联了。就是中间隔了一层主队列中的函数,无需等待,按序执行。
可以使用上面的变形一步一步来改变它,直到你理解了。
命名空间
-
概念理解
所属关系,给变量以及函数加一个前缀,使得他们有一个上级,要使用这些变量就要通过他们的上级来查询。
我们在写功能方法的时候,每一个功能中都可能牵扯到许多方法,以该功能名称做空间名。比如书(book)有方法添加、删除、修改等等其他。那么这样去写每一个方法:var Book = {}; Book.add=fucntion(){ //add book ... }; Book.remove =function(){ //remove book ... } ...
等等。有可能其他的功能比如用户(User)也有添加、修改、删除的方法。就不会导致命名冲突了。
当然我们也可以这样去声名每一个方法,function bookAdd(){…};function userAdd(){…};完全个人习惯,我还是更喜欢使用命名空间这样的。
-
示例说明
两种常规的声明命名空间的方式:var T=window.T||{}; T.one = function(){ //do something }; T.two = function(){ //do something again }; T.one(); T.two();
检查该命名空间是否已被命名,防止冲突。
the other
T.Book ={ add:function(){ //add something }, remove:function(){ //remove something }, ... }
-
其他补充说明