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++;
    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){
            viewport.removeChild(child);
        }
    }
    window.jView = obj;
})(document);

  

这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。功能很简单,但这里涉及到了另外一个概念:立即执行函数。 简单了解一下就行。主要是要理解这种写法是怎么实现闭包功能的。

 

闭包并不是万能的,它也有它的缺点

1、闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页性能问题。另外在IE下有可能引发内存泄漏 (内存泄漏指当你的页面跳转的时候 内存不会释放 一直占用你的CPU 只有当你关闭了浏览器才会被释放);

2、闭包会在父函数外部改变父函数内部的变量的值,所以不要随便改动父函数内部的值。

 

posted @ 2016-10-22 20:20  石shi  阅读(757)  评论(0编辑  收藏  举报