预编译与函数词法作用域(Precompiled & Scoped)

预编译

Javascript脚本的宿主在执行代码之前对脚本做了预编译处理,比如浏览器对Js进行了预编译,编译器会扫描所有的声明(变量、函数、对象,无论它们是否嵌套在其他函数中,都会被扫描),对声明进行编译,编译期间会忽略任何可执行的语句,直到编译结束后才会调用解释器对代码进行执行。

1.为声明的变量(变量式函数被视为变量)划分内存空间,标识符=Undefined(未初始化),执行时才会发生初始化。

2.为所有声明式函数划分内存空间,空间保存了代码体,但代码体的变量被设为Undefined。

var aa = 10;    
aler(aa); //print 10
var aa = 20; 
alert(aa);//print 20

var bb = function () {
    alert("hello");
}

bb();//print hello

var bb = function () {  
    alert("world");
}

bb();//print world

function fun() {
    alert("sam");
}

fun();//print leo,由于编译器会将声明式函数的代码体存储在内存中,所以最后一个声明的fun的代码体就覆盖了前一个

function fun() { 
    alert("leo");
}

函数词法作用域

1.闭包的作用域

Js中只有闭包才会遵循子承父的原则,也即闭包函数可以获取在父函数里定义的变量,但父函数不能获取闭包函数中定义的变量,闭包中的变量只在闭包中有效。而在浏览器中,你定义的任何函数都是作为window的构造函数的闭包。

var aa = function () {
    alert(x)
    function bb() { var x = 10 }
}
aa()//return x未定义,因为x只在闭包函数bb的作用域内

var aa = function () {
    var x = 10
    function bb() {alert(x) }

aa()//alert 10

function aa() {
    var t = "s"
    window.say = function () { //全局的say方法,同时又作为了aa的闭包
        alert(t); //t在哪里被定义?当前函数未定义,于是向上查找,发现t在aa中定义
    }
    window.say();
}

aa(); //由此可见全局函数作为闭包使用时,变量作用域的查找也是按照逐层向上的规则进行的

2.块的作用域

Js的块作用域是开放的,也即没有子承父的原则,在一个函数中的子块里定义的变量,父块也可以访问,但离开函数的作用域,则不能再访问。此处需要明确函数闭包作用域和块作用域的区别,前者的父函数不能访问闭包函数里的变量,后者的函数可以访问其代码体内的任意块里的变量。

function aa() {
    for (var i = 1; i < 11; i++) {
        var g = 100;
    }
    alert(g); //可以访问到g
}
aa();

这与C#不同。为了不引起混乱,建议在函数代码体一开始的位置就把需要用到的变量都声明一次,不要在分支语句中创建临时变量,除非有必要这样做。

function aa() {
    if (true) { var x = 100; } //子块       
    { var y = 200; } //子块
    alert(x); //print 100
    alert(y); //print 200
}

aa();

3.作用域查找

假设声明了一个全局变量,而后又在某个函数中声明一个与全局变量同名的变量,那么在在函数中试图输出这个变量时,应该输出全局变量还是局部变量呢?答案是局部变量,因为解释器执行到输出变量时,它会问自己,这个变量再哪里被定义的,首先它会采取就近原则,先查找该变量是否在当前作用域中被定义,如果有则获取它,否则逐层向上查找祖先作用域,直到找到为止,如果任何作用域都找不到该变量,则会报错。

var global = "global"; //全局global
function f() {
    alert(global);
    var global = "part";
    alert(global); //print "hello"
}

f();
//预编译:全局blobal赋值undefined,内存存储函数f的代码体,代码体alert(undefined),再声明一个局部blobal=undefined,再alert(undefined)
//解释执行:全局global="global",调用函数f,代码体内部alert(undefined),因为查找是就近原则,当前函数内部定义了一个global,但此时在内存中其值为undefined
//所以第一次会弹出提示框显示undefined,接下来为局部变量global赋值,也即为已经存在于内存中的局部blobal赋值为part,最后执行弹出框显示global的值为part

4.深入理解作用域

function initAnchors() {
    var i;
    var anchor;
    for (i = 1; i <= 3; i++) {
        anchor = ADS.$("anchor" + i); //根据ID获取超链接元素
        ADS.addEvent(anchor, "click", function () { //为每一个超链接绑定click事件
            alert("my ID is" + i);
        })
    }
}

ADS.addEvent(window, "load", initAnchors);
//当点击任何超链接时,都输出3
//编译器编译时,代码体的i=undefined
//解释器执行时,每循环一次为超链接注册click事件,但事件并未触发,所以匿名的事件处理函数其在内存中的代码体中的alert('my id is undefined'),而循环完成后,i=3
//接下来,click发生,事件处理器开始执行,此时alert时,i=3

//要解决这个问题很简单,将注册事件的代码封装到另一个函数中:
function initAnchors() {
    var i;
    var anchor;
    for (i = 1; i <= 3; i++) {
        anchor = ADS.$("anchor" + i); //根据ID获取超链接元素
        registerEvent(anchor, i);
    }
}

ADS.addEvent(window, "load", initAnchors);
        
function registerEvent(linkObjArray, i) {
    ADS.addEvent(linkObjArray, "click", function () { //为每一个超链接绑定click事件
        alert("my ID is" + i);
    })
}

特殊值

1.xx未定义

所有未声明就被引用的变量=xx未定义。

2.undefined

所有已声明但未赋值的变量/无返回值的函数=Undefined。Undefined==null。

3.null

null本身就是一个对象,所以它可以赋值给任何变量而不会抛错,这个对象不能定义任何属性与方法,它表示空。null!="",null !=0,但1 + null = 1。

4.false

false == 0,false="   ",0="",false != null。

5.NaN

not a number的意思,即非数字。

Javascript - 学习总目录

Javascript - 预编译与函数词法作用域的更多相关文章

  1. JavaScript 预编译与作用域

    JavaScript 预编译与作用域 JavaScript 预编译的过程和作用域的分析步骤是 JS 学习中重要的一环,能够帮助我们知道代码的执行顺序,更好理解闭包的概念 预编译 JavaScript ...

  2. 关于JavaScript预编译和执行顺序以及函数引用类型的思考

    昨晚在对项目中的一部分做模块化处理的时候,遇到了一个问题,一个重新定义的function对一个通用类中的function进行赋值覆盖的时候,失败了.问题抽象出来是这样的: <script > ...

  3. 热重载 预编译 编译器 JS引擎 作用域

    热重载就是页面每次改动,不需要手动去刷新,可自动刷新.保持vuex的状态. JS之预编译 JavaScript的预编译 编译器 JS引擎 作用域三者之间的关系 建议你先去看看你不知道的JavaScri ...

  4. javaScript 预编译过程浅尝

    javaScript 预编译过程 1.创建AO对象(Activation Object) AO{ a: } 2.找形参和变量声明,将变量和形参作为AO属性名,值为undefined AO{ a:und ...

  5. js隐式类型转换,预编译、递归、作用域,作用域链、闭包、立即执行函数、继承圣杯模式

    隐式类型转换 调用Number()当有运算符(加减乘除,求余)时,会调用Number()转为数字再运算,除了 加 当 有字符串时就变身成拼接Boolean();String(); typeof()st ...

  6. JavaScript 预编译(变量提升和函数提升的原理)

    本文部分内容转自https://www.cnblogs.com/CBDoctor/p/3745246.html 1.变量提升 console.log(global); // undefined var ...

  7. javascript预编译的过程

    预编译的两种情况 全局: 1.全局 直接是script标签中的代码,不包括函数执行执行前:1.首先生成一个GO(global object)对象,看不到,但是可以模拟出来用来分析2.分析变量声明,变量 ...

  8. JavaScript预编译原理分析

    一直对变量对象,活动对象,预编译,变量提升,执行上下文的时间顺序有着凌乱的认识,但是这些对理解JS语法有着很重要的作用.读了很多人的文章,都没有一个特别清晰的把这些写出来. 今天主要总结一下现阶段自己 ...

  9. JS笔记--------预编译,闭包和作用域

    (一)JS预编译四部曲: 1,创建AO对象. 2,找形参和变量声明,将变量和新参名作为AO属性名,值为undefined. 3,将实参值和形参值统一. 4,在函数体里找函数声明,值赋给函数体. (二) ...

随机推荐

  1. 【读书笔记】Linux内核设计与实现(第一章&第二章)

    http://pan.baidu.com/s/1hqYAZNQ OneNote做的笔记没法儿带着格式一起导进来.所以上传到百度云,麻烦老师下载一下了. 下次不再用OneNote.

  2. 《Linux内核分析》 之 计算机是如何工作的

    [李行之原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] <Linux内 ...

  3. Zero-shot learning(零样本学习)

    一.介绍 在传统的分类模型中,为了解决多分类问题(例如三个类别:猫.狗和猪),就需要提供大量的猫.狗和猪的图片用以模型训练,然后给定一张新的图片,就能判定属于猫.狗或猪的其中哪一类.但是对于之前训练图 ...

  4. Python3《机器学习实战》学习笔记(一):k-近邻算法(史诗级干货长文)

    https://blog.csdn.net/c406495762/article/details/75172850

  5. 小程序 JM

    // 本地 // 'https://ly.com/' // 短信验证码参数: let dataValue = { 'type': 1, mobile: '13615814562' }; dataVal ...

  6. Java == ,equals 和 hashcode 的区别和联系(阿里面试)

    今天阿里的人问我 equals 与hashcode的区别,我答不上来, 仔细查了一下,做了总结: (1) == 这是Java 比较内存地址,就是内存中的对象: java中的==是比较两个对象在JVM中 ...

  7. git merge以及比较两个repo

    一.需求 1)有两个相关的branch github下载的repo有master和work两个分支,由于远端已经更新,master分支落后于远端分支,work分支又有新的更新内容,需要将远端maste ...

  8. Swap HDU - 2819 (有关矩阵的二分匹配)

    题意见大佬:https://www.cnblogs.com/gj-Acit/archive/2013/08/17/3265502.html 题目大意很明确,交换图的某些行或者是某些列(可以都换),使得 ...

  9. IDEA中在目录中如何快速指定到当前的类

    类似于myeclipse的 Link with Editor 其实也在IDEA的这个位置,跟狙击镜的图标一样,叫做Scroll from Source 不同的的是,IDEA的这个功能,需要手动点击,才 ...

  10. USACO Section 2.1 Sorting a Three-Valued Sequence 解题报告

    题目 题目描述 给N个整数,每个整数只能是1,2,或3.现在需要对这个整数序列进行从小到大排序,问最少需要进行几次交换.N(1 <= N <= 1000) 样例输入 9 2 2 1 3 3 ...