JavaScript面向对象和高级04

来源:互联网 发布:免费手机qq群发软件 编辑:程序博客网 时间:2024/06/05 18:19

词法作用域和作用域链

1 词法作用域

1.1 什么是作用域

作用域,即 起作用的区域。变量定义后,可以在哪个范围内使用该变量

1.2 两种作用域

  • 词法作用域
  • 块级作用域(JavaScript不支持,了解)
块级作用域:用一个块(代码块)结构限制变量的访问区域。即,块内的每一段代码都有各自的作用域,变量在声明它们的代码段之外是不可见的。代表语言: C 语言( C系,如:Java、Object-C、C#)
  • 案例:
for(var i = 0; i < 10; i++) {    console.log(i);}alert(i);

1.3 词法作用域

变量的作用范围, 在书写代码的时候就已经决定, 与运行时无关。即,它们在定义它们的作用域里运行,而不是在执行它们的作用域里运行。分割作用域的只有函数,也就是说只有函数 才会形成 作用域。当定义了一个函数,当前的作用域链就保存起来,并且成为函数的内部状态的一部分。
  • 面试题
// 1function foo() {    var num = 123;    console.log(num);  // 123}foo();console.log(num); // 报错: num is not defined// 2var scope = "global";function foo() {    console.log(scope); // undefined    var scope = "local";    console.log(scope); // local}foo();// 3if("a" in window){    var a = 10;}alert(a); // ?  10if(!"a" in window){    var a = 10;}alert(a); // ? undefined// 4var foo = 1;function bar() {    if(!foo) {        var foo = 10;    }    alert(foo); // 10}bar();// 4 提升完之后的结果var foo;function bar() {    // var foo; //     if(!foo) { //        foo = 10;    }    alert(foo); // }foo = 1;bar();

1.3.1 变量提升和函数提升(hoisting)

  • JavaScript的执行过程
JS运行的两个步骤:解析和执行1 预解析(不是在执行代码)。 将所有的声明都加载到内存中,也就是:告诉解释器有什么东西了2 一步一步的执行代码, 按照从上往下的顺序
  • 变量提升的是:变量的声明
  • 函数提升的是:整个函数

  • 案例和练习

// 2 变式if("b" in window) {    var a = 10;}alert(a); // ?undefined// 3if(true) {    function f() { alert("true"); }} else {    function f() { alert("false"); }}f();// ? 不确定,会根据不同浏览器实现不同// 4if(true) {    var f = function () { alert("true"); }} else {    var f = function () { alert("false"); }}f();// ? true
  • 函数表达式
函数表达式中 可以省略函数名字,如果有函数名字,那么这个函数名只能在函数内部使用。(IE8 除外)函数声明不允许出现在其他语句

2 作用域链

2.1 作用域结论

  • 1 声明会提升
  • 2 只有函数才会限定作用域,即:函数作用域

2.2 作用域链是什么

每个函数都会形成一个作用域,如果函数被其他函数包裹,包裹函数也有作用域,一直往上直到全局环境。这样就形成了一条作用域链。
  • 多个函数才会形成作用域链吗?
// 分析以下函数的作用域链var a = 11;function outer() {    var num = 123;    function inner() {        var str = "abc";    }}// 函数outer的作用域链: 函数自身 -> 全局环境

2.3 绘制作用域链规则

1 将全部的 script 标签看做一个整体,是一个 0 级别的链2 由于只有函数才可以限定作用域,因此在函数上引出一条新链, 级别为 n + 13 如果有函数,继续绘制下去 注意点:1 链中的成员包括:在该链所属范围内 *声明* 的变量, 函数, 对象... 2 由于提升机制的存在,在开始的时候将 声明 都写在前面, 绘图的时候按照顺序绘制, 较为简单3 先绘制链中的成员,其他赋值操作,等到代码执行的时候再看。4 各链之间互不影响
  • 案例:
// 1var num = 123;var f = function() {};var arr = [];// 2var num = 123;var f = function() {    var num = 456;    var o = {};    var foo = function() {};};var arr = [];

2.4 变量搜索原则

在代码的运行过程中, 如果访问某一个变量,那么:1 首先在当前链上找    如果有,则停止查找    如果没有, 在 n-1 级上找( 在函数内部允许访问定义在函数外部的变量 )2 如此往复, 直到 0 级链    如果找到, 则结束寻找, 直接获得该链上变量的数据    如果还没有 抛出异常。
  • 案例:
var num = 456;function f() {    num = 678;    function foo() {        var num = 999;        console.log(num); //     }    foo();    console.log(num); // }f();

2.4.1 函数名和变量名同名的问题

如果一个函数和一个变量出现同名,此时:1 如果是变量只声明了,但没有赋值,变量名会被忽略var f;function f() {}console.log(f); // f指的是函数2 如果变量声明同时也被赋值了,此时:    a 如果是在赋值之前,获取到的是函数    b 如果是在赋值之后,获取到的是变量的值console.log(f); // function f() {}var f = 123;function f() {}console.log(f); // 123
  • 两个变量命名冲突
重复的var声明无效,会被忽略,只会起到赋值的作用var f = 123;var f = 456;var f = 123;f = 456;

6 作用域面试题分析

  • 面试题分析:
function Foo() {    getName = function(){ alert(1); };    return this; }Foo.getName = function() { alert(2); };Foo.prototype.getName = function(){ alert(3); };var getName = function() { alert(4); };function getName(){ alert(5); }Foo.getName();             // ? 2getName();                 // ? 4Foo().getName();           // ? 1getName();                 // ? 1new Foo.getName();         // ? 2new Foo().getName();       // ? 3new new Foo().getName();   // ? 3
  • 面试题-1
var num = 123;function f1() {    console.log(num);}function f2() {    var num = 456;    f1();}f2(); // ? 123
  • 面试题1-变式
var num = 123;function f1(num) {    console.log(num); // 456}function f2() {    var num = 456;    f1(num);}f2();
  • 面试题1-变式
var num = 123;function f1() {    console.log(num); // 456}f2();function f2() {    num = 456;    f1();}console.log(num); // 456
  • 面试题2-练习
(function (a) {    console.log(a);    var a = 10;    function a(){}}( 100 ));

补充

函数参数跟函数体内部的变量重名的情况:1 参数 首先被提升2 如果是声明的变量没有赋值(或者在赋值之前就打印这个变量的值),此时值为:     传进来的 实参的值3 如果是声明的函数,此时函数会把同名的参数覆盖,此时值为: 函数4 如果函数内部声明都没声明,此时值为:实参的值