Blogger Information
Blog 17
fans 0
comment 0
visits 11598
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
JavaScript高级知识(一)
指纹指恋的博客
Original
639 people have browsed it

作用域

<script>
    var a = 2;
    function t1(){
      var b = 3;
        function t2(){
          var c = 4;
          alert(a+b+c);
        }
      t2();
    }
    t1();
</script>
  • 在JS中,函数嵌套是非常普遍的,在函数嵌套中,对变量进行如下的寻找方式:首先在函数内寻找,寻找不到,则往函数外层寻找,……,直到全局(window)区域

声明变量var

  • var是在函数运行的上下文中,声明一个变量,如果不加var,则是一个赋值操作,但不要狭隘的理解为-->声明了一个全局变量【见下例】

<script>
    function t1(){
      var a;
      function t2(){
        a = 2;
        b = 4;
      }
      t2();
    }
    t1();   
    console.log(a);    //输出undefined
    console.log(b);    //输出2
</script>
  • 以window.xxx引用全局变量,寻找不到,作为某个属性不存在,返回undefined 【window.a】

  •  直接以xxx引用某个变量,寻找不到,则是报xxx is not defined错误的 【a】

<script>                                             <script>                                       
    var str1 = "global";                                var str1 = "global";
    function t1(){                                      function t1(){
      console.log(str1);   //global                         console.log(str1);    //global  
      console.log(str2);   // str is not definded           console.log(str2);    //undefinded
      str2 = "local";                                       var str2 = "local";
    }                                                    }
    t1();                                                t1();
</script>                                            </script>
  • JS代码自上而下执行,但是,JS代码在整体运行时分为【词法分析期】和【运行期】两部分

  • 自上而下执行之前,先有一个【词法分析】过程,以上面的结果为例:

第一步:分析t1()
       t1(){
           var str2;    //分析出t1内有str2局部变量,注意此时函数未执行,因此str2的值是undefined
       }
       
第一步:执行t1()
       console.log(str1);    //global                     
       console.log(str2);    //undefinded     
       str2 = "local";       //此时str2的值为local

词法分析

语法分析,分析3样东西:

  • 第一步:先分析参数

  • 第二步:在分析变量声明

  • 第三步:分析函数声明

  • 一个函数能使用的局部变量,就从上面的3步分析而来

具体步骤:

  • 第一步:函数运行前的一瞬间,生成Active Object(活动对象),下称AO

  • 第二步:把函数声明的参数,形成AO的属性,值全是undefined,如果有实参,则接收,并形成AO相应的属性的值

  • 第三步:分析变量声明!如var age,如果AO上还没有age属性,则添加AO属性,值是undefined;如果AO上已经有age属性,则不做任何影响

  • 第四步:分析函数声明,如 function foo(){ },则把函数赋给AO.foo属性,如果此前foo属性已经存在,则被无情的覆盖

<script>
    function t3(great){
        var great = "hello";
        alert(great);
        function great(){
        }
        alert(great);
    }
    t3(null);        //输出 hello hello
</script>

/*
  分析过程:
  1.形成AO = {}
  2.分析形参,AO = {great:undefined},接收实参,AO = {b:null}
  3.分析great变量声明,发现AO已有age属性,不做任何影响
  4.分析great函数声明,AO.great = function great(){},被覆盖成函数
  
  执行过程:
  great = "hello";
  alert(great);
  alert(great);
*/

<script>
    function a(b){
        alert(b);
        function b(){
            alert(b);
        }
        b();
    }
    a(1);       
</script>

/*
  分析过程:
  1.形成AO = {}
  2.分析形参,AO = {b:undefined},接收实参,AO = {b:1}
  3.分析var变量声明,此函数没有var
  4.分析函数声明,AO = {b:function b(){alert(b);}},被覆盖成函数
  
  执行过程:
  alert(b);        //function
  b();             //由作用域寻找到a函数中的b,即function
*/

<script>
    function a(b){
        alert(b);
        b = function (){
            alert(b);
        }
        b();
    }
    a(1);       
</script>

/*
  分析过程:
  1.形成AO = {}
  2.分析形参,AO = {b:undefined},接收实参,AO = {b:1}
  3.分析var变量声明,此函数没有var
  4.分析函数声明,没有!b = function(){},是一个赋值,在执行期才有用
  
  执行过程:
  alert(b);        // 1
  b = function (){
            alert(b);
        }
  b();             //由作用域寻找到a函数中的b,即function
*/

函数声明与函数表达式

  • 函数可以赋值给变量,也可以作为参数来传递

  • JS被称为披着C外衣的lisp语言,lisp是一种强大的函数式语言

function t1(){}

t2 = function(){}

/*
这两种方式效果是不同的:
t1是函数声明,虽然全局内也得到一个t1变量,值是function
而t2只是一个赋值过程,值就是右侧表达式的返回结果,即函数

因此t1,t2两种方式在词法分析时,有着本质去区别:
前者在词法分析阶段就发挥作用,而后者,在运行阶段才发挥作用
*/

(function(window,undefined){})            //内层表达式,返回值是函数,包在小括号里,当成表达式来执行
(function(window,undefined){})(window)    //立即调用

/*
内层函数没有起名字,称为匿名函数,这种手法,匿名函数,立即执行,不污染全局,称为立即执行匿名函数表达式,
在第二个括号里面传入window,是为了我提升内部查找变量的速度,不传undefined,是为了安全,因为在低版本IE和火狐中,
undefined可以被重新赋值,如undefined = 3,声明undefined局部变量(名字是undefined而已),
同时,又不传参,值自然是undefined,防止了外界对undefined的污染
*/

作用域链

QQ20171222-141204.png

argumengs详解

  • 是一个长得很像数组的对象

  • 内容是函数运行时的实参列表

//arguments收集“所有”的实参,即使没有与之相对应的形参
(function(d,e,f){
    //在此函数内,无法用d,e,f形参来取得“haha”,因为没有与之相应的形参,但我们可以用arguments来获取任意多个实参
    console.log(arguments[3]);
    arguments[3] = "china";
    console.log(arguments[d]);    //输出 china,形参与对应的arguments单元,其实是相互映射的,互相影响
})("hello","world","!","haha")

//arguments可以获取函数运行时,收到的实参个数(在多态里可以用到)
(function(d,e,f){
    console.log(arguments.length);    //输出 3
})("hello","world","!","haha")

//arguments.callee属性代表“当前运行的函数”

(function(){            //不用函数名,匿名函数,立即执行,完成递归
    if(n <= 1){
        return 1;
    }else{
        return n + arguments.callee(n-1);
    }
})(100);

函数运行期内,有三个关键的对象:

  • AO    ——>    本函数AO上没有某属性,则继续去外层函数的AO上找,直到全局对象,叫做 作用域链

  • arguments     ——>    每个函数都有自己的callee,但不会向外层函数接着找arguments的相关属性,即不形成链

  • this     ——>    也不形成链

this详解

  • 在JS中函数有4种调用方式

alert(window.x);    //输出 undefined
function t(){
    this.x = 333;
}
t();
alert(window.x);    // 输出 333

/*
作为普通函数来调用时,this的指向window,准确的说,this为null,但被解释成window,但在ECMASCRIPT标准中,
如果this为null则解释成undefined
*/

//作为对象的方法来调用,this指向方法的调用者,即该对象
var obj = {xx:999,yy:888,t:function(){alert(this.xx);}};
obj.t();    //输出 99
var dog = {xx:"wangwang"};
dog.t = obj.t;
dog.t()    //输出 wangwang

/*
作为方法调用时,this指向其调用那一刻的调用者,即母体对象,不管被调用函数,声明时属于方法还是函数,
*/

//函数作为构造函数调用时
//JS中没有类的概念,创建对象是用构造函数来完成,或直接用json格式来写对象
function Dog (name,age){
    this.name = name;
    this.age = age;
    this.bark = function(){
      alert('I am ' + this.name + '!');
    };
}
var dog = new Dog('huzi',2);
dog.bark();

/*
new Dog发生了以下几个步骤:
1.系统创建空对象{},(空对象construcor属性指向Dog函数,先别管)
2.把函数的this ——> 指向该空对象
3.执行该函数
4.返回该对象
*/

function Pig(){
    this.age = 99;
    return 'abc';
}
var pig = new Pig();    //返回pig对象,因为函数作为构造函数运行时,return的值是忽略的,还是返回对象

//函数通过call,apply调用
//语法格式:函数.call(对象,参数1,参数2,……,参数N);
function t (num){
    alert('我的真实年龄是'+ this.age);        //输出 我的真实年龄是28
    alert('但我告诉别人是'+ (this.age+num));  //输出 但我告诉别人是18
  }
var human = {name:'lisi',age:28};
human.t = t;
human.t(-10);    //this指向了human,但human多了一个方法

//接下来我们不把t赋为human的属性,也能把this指向human
var wangwu = {name:'wangwu',age:30};
t.call('wangwu',5);    //输出 我的真实年龄是30 但我告诉别人是35

/*
解释fn.call(对象,参数1,参数2,……,参数N);
运行如下:
1.fn函数中的this ——> 指向对象obj
2.运行fn(对象,参数1,参数2,……,参数N);
*/

闭包

QQ20171224-170822.png

  • 在大部分语言中,t1被调用执行,则申请内存并把其局部变量push入栈。t1函数执行完毕,内部的局部变量,随着函数的退出而销毁,因此age = 20的局部变量已经消息。

  • 但是在JS中,age = 20这个变量,却被t2捕捉,即使t1执行完毕,通过t2,依然能访问该变量

  • 像这种情况:返回的函数,并非孤立的函数,甚至把其周围的变量环境,形成了一封闭的“环境包”,共同返回,所以叫闭包。

  • 一句话概括:函数的作用域取决于声明时,而不取决于调用时!


Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!