JavaScript经典语录
Js的解析与执⾏过程:
全局中的解析和执⾏过程:
⼀:预处理:创建⼀个此法环境LE,
扫描JS:1.⽤声明的⽅式声明的函数(不是函数表达式),2.⽤var定义的变量。加到预处理阶段的此法环境中
全局环境中的预处理:预处理创建的词法作⽤域LE相当于windows
⼆:命名冲突的处理:
1.处理函数声明有冲突时,会覆盖,处理变量声明有冲突时,会忽略。
2.两者都有时,函数声明的权重⾼⼀些,最终结果指向函数声明的引⽤
三:全局函数的执⾏
1.⾸先进⾏全局预处理
2.执⾏完预处理后,代码⼀步步解析
补充:运⽤词法的作⽤域,可以很好的解释⼀个带多个参数的函数只传递⼀个参数的例⼦。
函数中的解析和执⾏过程:
函数中的解析和执⾏过程区别不是很⼤,有⼀个(arguments)注意⼀下:
1.函数的预处理和全局的预处理类似(只是加了⼀个arguments,调⽤函数时实际调⽤的参数个数)
如果不是var 声明的变量,会变成最外部LE的成员,即全局变量
JS的作⽤域和作⽤域链:
作⽤域⼀般分为四类:块级作⽤域、函数作⽤域、动态作⽤域、词法作⽤域(静态作⽤域)
块级作⽤域:(js没有块级作⽤域)
函数作⽤域:没有纯粹的函数作⽤域
动态作⽤域:没有动态作⽤域
词法作⽤域(静态作⽤域)javascript的作⽤域为静态作⽤域
f[[scope]]==LE==window
分析:1.创建⼀个作⽤域对象f[[scope]]==创建它时的词法环境LE(LE==window)
2.真正执⾏的时候(⼀步⼀步往上)LE--->f.[[scope]]==window
在词法解析阶段,就已经确定了相关的作⽤域。作⽤域还会形成⼀个相关的链条,称为作⽤域链
new Function的情况⼜不⼀样
问题:
多个函数都想要⼀个变量,每次都要写⼀个好⿇烦
⽅法:将变量设置为全局变量
问题:
不是说要减少全局⽤量的使⽤么?因为在我们做⼤项⽬的时候难免要引⼊多个JS库,变量间的命名可能会有冲突,且出错后不易查,这个时候我们该怎么办呢?
⽅法:将变量设置在⼀个function中,
问题:
在外⾯⼜访问不到了,怎么办?
⽅法:我们使⽤匿名函数的⽅法
Javascript中的作⽤域链:
当执⾏⼀段JavaScript代码(全局代码或函数)时,JavaScript引擎会创建为其创建⼀个作⽤域⼜称为执⾏上下⽂(Execution Context)
在页⾯加载后会⾸先创建⼀个全局的作⽤域,然后每执⾏⼀个函数,会建⽴⼀个对应的作⽤域,从⽽形成了⼀条作⽤域链。
每个作⽤域都有⼀条对应的作⽤域链,链头是全局作⽤域,链尾是当前函数作⽤域。
作⽤域链的作⽤是⽤于解析标识符,当函数被创建时(不是执⾏),
会将this、arguments、命名参数和该函数中的所有局部变量添加到该当前作⽤域中,
当JavaScript需要查变量X的时候(这个过程称为变量解析),它⾸先会从作⽤域链中的链尾也就是当前作⽤域进⾏查是否有X属性,如果没有到就顺着作⽤域链继续查,直到查到链头,也就是全局作⽤域链,仍未到该变量的话,就认为这段代码的作⽤域链上不存在x变量,并抛出⼀个引⽤错误(ReferenceError)的异常。
1.JavaScript中没有块级作⽤域,但是有词法作⽤域
函数内部不⽤var关键字申明的变量,则默认该变量为全局变量
在javascript中如果不创建变量,直接去使⽤则报错
2.javascript中如果创建值⽽不赋值,则该值为unefined
javascript 的函数在被执⾏之前,会将其中的变量全部申明⽽不赋值
3.词法作⽤域是不可逆的
闭包函数的由来:
作⽤域的存在帮我们省不少事,如果想在函数A中调⽤函数B该怎么办?
思路:我们给函数B设⼀个返回值,然后在函数A中调⽤,代码如下:
function A(){
function B(){
console.log("Hello foodoir!");
}
return B;
}
var c = A();
c();//Hello foodoir!
这样我们就可以得到我们想要的结果。这样,我们基本上到了⼀个最简单的闭包形式。我们再回过头分析代码:
(1)定义了⼀个普通函数A
(2)在A中定义了普通函数B
(3)在A中返回B(确切的讲,在A中返回B的引⽤)
(4)执⾏A(),把A的返回结果赋值给变量 c
(5)执⾏ c()
把这5步操作总结成⼀句话:函数A的内部函数B被函数A外的⼀个变量 c 引⽤。当⼀个内部函数被其外部函数之外的变量引⽤时,就形成了⼀个闭包。
来看下⾯的⼏种闭包:demo1:
function fn(){
var b = "foodoir";
return function(){
console.log(b);//foodoir
return b;
}
}
//console.log(b);//b is not defined
var result = fn();
console.log(result());//foodoir
demo2: 
var n;
function f(){
var b = "foodoir";
n = function(){
return b;
}
}
f();
console.log(n());//foodoir
demo3:
//相关定义与闭包
function f(arg){
var n = function(){
return arg;
};
arg++;
javascript全局数组
return n;
}
var m = f(123);
console.log(m());//124
//注意,当我们返回函数被调⽤时,arg++已经执⾏过⼀次递增操作了,所以m()返回的是更新后的值。
demo4:闭包中的读取与修改
//闭包中的设置与修改
var getValue,setValue;
(function(){
var n = 0;
getValue = function(){
return n;
};
setValue = function(x){
n = x;
}
})();
//console.log(n);
console.log(getValue());//0
console.log(setValue());//undefined
setValue(123);
console.log(getValue());//123
demo5:⽤闭包实现迭代效果
//⽤闭包实现迭代器效果
function test(x){
//得到⼀个数组内部指针的函数
var i=0;
return function(){
return x[i++];
};
}
var next = test(["a","b","c","d"]);
console.log(next());//a
console.log(next());//b
console.log(next());//c
console.log(next());//d
demo6:循环中的闭包
//循环中的闭包
function fn(){
var a = [];
for(var i=0;i<3;i++){
a[i] = function(){
return i;
}
}
return a;
}
var a = fn();
console.log(a[0]());//3
console.log(a[1]());//3
console.log(a[2]());//3
/*
* 我们这⾥创建的三个闭包,结果都指向⼀个共同的局部变量i。
* 但是闭包并不会记录它们的值,它们所拥有的只是⼀个i的连接,因此只能返回i的当前值。 * 由于循环结束时i的值为3,所以这三个函数都指向了3这⼀个共同值。
* */
思考:如何使结果输出分别为0、1、2呢?
思路⼀:我们可以尝试使⽤⾃调⽤函数
function fn(){
var a = [];
for(var i=0;i<3;i++){
a[i] = (function(x){
return function(){
return x;
}
})(i);
}
return a;
}
var a = fn();
console.log(a[0]());//0
console.log(a[1]());//1
console.log(a[2]());//2
思路⼆:我们将i值本地化
function fa(){
function fb(x){
return function(){
return x;
}
}
var a = [];
for(var i=0;i<3;i++){
a[i] = fb(i)
}
return a;
}
console.log(a[0]());//0
console.log(a[1]());//1
console.log(a[2]());//2
在这⾥,我们来对闭包进⾏更深⼀步的操作
我们再将demo1的例⼦进⾏扩展
代码⽰例如下:
function funcTest(){
var tmpNum=100; //私有变量
//在函数funcTest内
//定义另外的函数作为funcTest的⽅法函数
function innerFuncTest(
{
alert(tmpNum);
//引⽤外层函数funcTest的临时变量tmpNum
}
return innerFuncTest; //返回内部函数
}
//调⽤函数
var myFuncTest=funcTest();
myFuncTest();//弹出100
到这,我们对闭包的概念和⽤法有更加熟悉
闭包和this相关
闭包应⽤举例,模拟类的私有属性,利⽤闭包的性质,局部变量只有在sayAge⽅法中才可以访问,⽽name在外部也访问,从⽽实现了类的私有属性。
function User(){
this.name = "foodoir";  //共有属性
var age = 21;    //私有属性
this.sayAge=function(){
console.log("my age is " + age);
}
}
var user = new User();
console.log(user.name); //"foodoir"
console.log(user.age);  //"undefined"
user.sayAge();  //"my age is 21"
关于闭包更深⼊的了解
(function(document){
var viewport;
var obj = {
init:function(id){
viewport = document.querySelector("#"+id);
},
addChild:function(child){
viewport.appendChild(child);
},
removeChild:function(child){
}
}
window.jView = obj;
})(document);
这个组件的作⽤是:初始化⼀个容器,然后可以给这个容器添加⼦容器,也可以移除⼀个容器。功能很简单,但这⾥涉及到了另外⼀个概念:⽴即执⾏函数。简单了解⼀下就⾏。主要是要理解这种写法是怎么实现闭包功能的。
闭包并不是万能的,它也有它的缺点
1、闭包会使得函数中的变量都保存在内存中,内存消耗很⼤,所以不能滥⽤闭包,否则会造成⽹页性能问题。另外在IE下有可能引发内存泄漏(内存泄漏指当你的页⾯跳转的时候内存不会释放⼀直占⽤你的CPU 只有当你关闭了浏览器才会被释放);
2、闭包会在⽗函数外部改变⽗函数内部的变量的值,所以不要随便改动⽗函数内部的值。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。