JS 之 (三)作用域链、闭包、面向对象

作用域链

定义

变量在当前环境now、内部环境f1、内部深层环境f2/f3….都起作用的现象形成了一个链条,这个链条就称为变量的”作用域链”

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    var title = '链条';
    //title 在各个环境都起作用的现象形成了一个链条,称为“作用域链”
    console.log('now:' + title);
    function f1(){
        console.log('f1:' + title);
        function f2(){
            console.log('f2:' + title);
            function f3(){
                console.log('f3:' + title);
            }
            f3();
        }
        f2();
    }
    f1();
</script>
</head>
<body>
    <h2>作用域链</h2>
</body>
</html>

作用域链的作用

变量必须“先声明、后使用”

函数可以“先使用、后声明”,原因是函数有“预加载”过程(函数声明先于其他执行代码进入内存)。本质还是函数的声明在前,使用在后。

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //变量必须“先声明、后使用”
    //函数可以“先使用、后声明”

    /*
        同名函数和变量的执行顺序问题,函数预先加载,之后代码一步步执行
    */
    //var getInfo = 'school';//第二加载,会污染函数

    getInfo();//第三加载,调用函数
    var getInfo = 'school';//第四加载,没问题
    function getInfo(){//预先加载
        console.log('function info');
    }
    var getInfo = 'school';//第五加载,没问题
</script>
</head>
<body>
    <h2>作用域链的作用</h2>
</body>
</html>

内部环境可以访问外部环境的变量,反之不然

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //内部环境可以访问外部环境的变量,反之不然
    var age = 20;
    function f1(){
        var height = 170;
        console.log('age:' + age);//内部可访问外部
        function f2(){
            console.log('height:' + height);//内部可访问外部
        }
        f2();
    }
    f1();
    console.log('height:' + height);//外部不可访问内部
</script>
</head>
<body>
    <h2>作用域链的作用</h2>
</body>
</html>

变量的作用域是声明时决定的,而不是运行时

这个没什么好说的
同名变量只在其作用域内有效,不同层级的同名变量互不干扰

AO活动对象

AO: Active Object 活动对象
执行环境:

  • js代码执行是有环境的
  • 该环境定义了其有权访问的其他数据
  • 环境有一个与之关联的“活动对象AO”
  • 环境中所有的变量和函数都是活动对象AO的属性
  • 全局环境是最外围的执行环境,活动对象是window对象
  • 执行环境中的代码执行完毕后就被销毁
    这里写图片描述
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //AO活动对象
    var mingzi = 'tom';
    console.log('名字:' + mingzi);
    function f1(){
        var age = 20;
        console.log('f1 名字:' + mingzi + ' 年龄:' + age);
        function f2(){
            var height = 170;
            console.log('f2 名字:' + mingzi + ' 年龄:' + age + ' 身高:' + height);
            function f3(){
                var mingzi = 'zhaosi';
                console.log('f3 名字:' + mingzi + ' 年龄:' +age + ' 身高:' + height);
            }
            f3();
        }
        f2();
    }
    f1();
    /*
        以上代码都可执行,具体看f3
        f3获得变量顺序:
        1、在本环境内部寻找
        2、在其外部寻找f2
        3、再在外部环境寻找
        。。。
        直到找到全局环境为止

        以上寻找变量过程貌似是一个“动态过程”,其实本质是“固态过程”
        f3环境里可访问什么信息,早早的已经固化为其AO“成员属性”信息
        在每个环境里都有AO活动对象,内部存储着可以访问的成员信息
        最外层AO对象为window,访问时一般都省略,比如 window.mingzi window.age
    */
</script>
</head>
<body>
    <h2>AO活动对象</h2>
</body>
</html>

执行环境可以访问变量的类型及优先顺序

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //执行环境可以访问变量的类型及优先顺序

    //优先级:内部环境变量 >>> 本环境函数 >>> 形参 >>> 外部环境变量
    var height = 170;//1、外部环境变量
    function f1(){
        var height = 180;//外部环境变量、形参、函数
        function f2(height){//2、形参
            var height = 190;//4、内部环境变量         
            console.log(height);            
            function height(){//3、本环境函数 
                alert('00004');
            }
        }
        f2(200);
    }
    f1();
</script>
</head>
<body>
    <h2>执行环境可以访问变量的类型及优先顺序</h2>
</body>
</html>

重新认识全局变量和局部变量

  • 全局变量:

    • 声明:
      • ① 在函数外部声明的变量
      • ② 在函数内部不使用var声明的变量(前提是函数执行之后才起作用)
    • 其是活动对象window的成员信息
    • 在访问的时候,window是否设置都可以
  • 局部变量:

    • 声明:

      • 在函数内部通过var声明的变量
    • 其在一定范围内可以看做是“全局变量”。

    • 其在本环境、内部环境、内部深层环境都可以被访问。

闭包

什么是闭包

闭包就是一个函数,两个函数彼此嵌套,内部函数就是闭包
形成闭包条件是内部函数需要通过return给返回出来。

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //简单的闭包
    function f1(){
        var age = 20;
        var height = 170;
        function f2(){
            console.log('年龄:' + age + ' 身高:'+ height);
        }
        return f2;
    }
    //对象赋值,其实为引用传递,就是创建一个新的引用,但它们有共同的实体
    var ff = f1();//ff就是一个闭包,它和f2共同指向同一个function
    ff();
</script>
</head>
<body>
    <h2>闭包</h2>
</body>
</html>

闭包特点

闭包有权利调用其上级环境的变量信息。

闭包使用规则

同一个闭包机制可以创建多个闭包函数出来,它们彼此没有联系,都是独立的。
并且每个闭包可以保存自己个性化的信息。

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //同一个闭包机制可以创建多个闭包函数出来,它们彼此没有联系,都是独立的。
    //并且每个闭包可以保存自己个性化的信息。
    function f1(n){
        function f2(){
            console.log(n);
        }
        return f2;
    }

    var fa = f1(100);
    var fb = f1(200);
    fa();
    fb();
</script>
</head>
<body>
    <h2>闭包</h2>
</body>
</html>

闭包案例

闭包生成数组元素

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    var arr = new Array();
    for(var i=0; i<4; i++){
        arr[i] = function(){
            console.log(i);
            return i;
        }
    }
    /*
        以上代码可以确定的是,添加了4个数组元素出来,下标为0.1.2.3
        每个元素都要访问i的信息,而内存中变量i只有一个
        而下面的代码在访问的时候,它们都调用了同个变量i,且i===4
    */
    arr[1]();
    arr[3]();
</script>
</head>
<body>
    <h2>闭包生成数组元素</h2>
</body>
</html>
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    var arr = new Array();
    for(var i=0; i<4; i++){
        //闭包方式创建数组元素
        arr[i] = f1(i);//f1被创建了4次,分别给4个元素赋值
    }
    function f1(n){
        function f2(){
            console.log(n);
        }
        return f2;
    }
    arr[1]();
    arr[3]();
    /*
        利用闭包就可以创建符合要求的数组元素
        每个元素都是函数 内部有访问的独特信息
    */
</script>
</head>
<body>
    <h2>闭包生成数组元素</h2>
</body>
</html>

闭包事件操作

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    window.onload = function(){
        var lis = document.getElementsByTagName('li');

        for(var i = 0; i<3;i++){
            lis[i].onmouseover = over(i);
            lis[i].onmouseout = out(i);
        }

        //闭包
        var cols = ['red','blue','green'];
        function over(i){
            function f2(){
                lis[i].style.backgroundColor=cols[i];
            }
            return f2;
        }
        function out(i){
            function f2(){
                lis[i].style.backgroundColor='';
            }
            return f2;
        }
    }
</script>
</head>
<body>
    <h2>闭包事件操作</h2>
    <ul>
        <li>阿大</li>
        <li>阿二</li>
        <li>阿三</li>
    </ul>
</body>
</html>

面向对象

PHP里边,是从一个类里边获得一个具体对象。
Javascript里边,没有类的概念,可以直接创建一个对象出来,对象可以有默认成员,后期也可以给对象丰富成员出来。

创建对象

  1. 字面量方式创建
  2. 构造函数创建对象
  3. Object方式创建对象
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    /*① 字面量方式创建
    var obj = {};//空对象
    var obj = {name:'tom',swim:function(){console.log('在游泳')}}
    给对象丰富成员
        对象.成员名 = 值;
        对象[成员名] = 值;
    访问成员
        对象.成员名
        对象[成员名]()
    */
    var obj = {};
    console.log(obj);//Object {}

    var dog = {name:'大黄',age:5,hobby:function(){
        console.log('吃屎');
    }};
    //给已有对象丰富其他成员
    dog.color = 'yellow';
    dog['weight'] = 80;
    dog.run = function(){
        console.log('冲冲冲');
    }
    console.log(dog);//Object {name: "大黄", age: 5, color: "yellow", weight: 80}
    //访问成员
    console.log(dog.color);//yellow
    dog.hobby();//吃屎
    dog['run']();//冲冲冲

</script>
</head>
<body>
    <h2>创建对象</h2>
</body>
</html>
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    /*② 构造函数创建对象
    var obj = new 函数();
    */
    function Animal(){
        //设置默认成员,通过this关键字声明
        this.name = '大黄';
        this.age = 5;
        this.run = function(){
            console.log('跑');
        }
    }
    var dog = new Animal();
    console.log(dog);
    dog.run();
</script>
</head>
<body>
    <h2>创建对象</h2>
</body>
</html>
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    /*③ Object方式创建对象
    var obj = new 函数();
    */
    var dog = new Object();
    dog.color = 'yellow';
    console.log(dog);
    console.log(dog.color);
</script>
</head>
<body>
    <h2>创建对象</h2>
</body>
</html>

对象在内存分配

与对象有关系的内存区域:

  1. 栈空间
    存放的数据大小比较小,一般固定大小的信息适合存放在该空间,例如 整型、boolean、对象的引用(名称)。
  2. 堆空间
    该空间存储的数据比较多,空间较大,一般数据长度不固定的信息在该空间存放,例如: string、Array、对象实体
  3. 数据空间
    该空间存放常量、类的静态属性
  4. 代码空间
    存放函数体、方法体代码

对象调用其他函数/对象成员

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //3. 对象调用其他函数/对象成员
    var mingzi = '李雷';
    function say(){
        console.log('我叫 ' + this.mingzi);
    }

    var dog = {mingzi:'大黄',eat:'便便'};
    //使当前dog对象调用上边的say函数
    //解决:给say函数创建一个别名赋给dog对象的成员即可
    dog.wangwang = say;
    dog.wangwang();
</script>
</head>
<body>
    <h2>3. 对象调用其他函数/对象成员</h2>
</body>
</html>

构造函数 与 普通函数

构造函数和普通函数的区别:
没有区别,就看使用,new就是构造函数,函数()就是普通函数调用。

函数的各种执行方式

普通函数调用

  1. 构造函数执行new
  2. 作为对象的成员方法执行
  3. 通过call和apply执行
    call和apply可以明显控制变量污染的风险。
    这两个方法基本上是一个意思,区别在于 call 的第二个参数可以是任意类型,而apply的第二个参数必须是数组
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //call和apply可以明显控制变量污染的风险。
    var mingzi = '李雷';
    function say(){
        console.log('我叫 ' + this.mingzi);
    }

    var dog = {mingzi:'大黄',eat:'便便',act:function(a,b){console.log(this.mingzi + '在' + a + ',吃' + b)}};
    var lilei = {mingzi:'李雷雷'};
    //使用call方法调用
    say.call(dog);
    dog.act.call(lilei,'卖当当','叉烧包');
</script>
</head>
<body>
    <h2>call和apply可以明显控制变量污染的风险。</h2>
</body>
</html>

this都是谁

  1. 在函数/方法里边 代表调用该函数/方法的当前对象
  2. 在事件中,代表元素节点对象
    divnode.onclick = function(){
    alert(this.value);
    }
  3. 代表window
  4. 可以任意代表其他对象
    在call和apply使用的时候,可以任意设置被执行函数内部this的代表

获取构造器

构造器:使用什么元素实例化的对象,元素就称为该对象的构造器

对象.constructor; //获得构造器

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //函数就是一个对象,函数的构造器是Function
    function f1(){
        alert('123');
    }
    console.log(f1.constructor);//Function() { [native code] }

    var ff = new Function('name','age',"console.log(name + ':' + age)");
    ff('梅梅','20');
</script>
</head>
<body>
    <h2>获取构造器</h2>
</body>
</html>

return对实例化对象的影响

<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
    //return对实例化对象的影响
    //仍然实例化对象,不过return后边的代码不执行
    function ff(){
        this.xing = '李';
        this.ming = '白';

        return this;
        this.zi ='吃';
    }

    var f1 = new ff();
    console.log(f1);//ff {xing: "李", ming: "白"}
</script>
</head>
<body>
    <h2>return对实例化对象的影响</h2>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值