Javascript——高级进阶

本文深入探讨JavaScript的基础和高级概念,包括数据类型、对象、函数、原型链、执行上下文和作用域。详细阐述了数据类型的区别、对象的使用、函数的定义与调用、原型与原型链的工作原理,以及执行上下文和作用域链的影响。同时,文章讨论了变量、内存和对象在JavaScript中的关系,以及闭包的概念和产生条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、基础总结深入

1.1、数据类型

1.1.1、分类

——基本(值)类型

string:任意字符串

Number:任意的数字

Bloolean:true/false

underfined:underfined

null:null

两种特殊的值:NaN(非法数字) Infinity (无穷大)

——对象(引用)类型

Object:任意对象 (存储数据)

Function:一种特别的对象(可运行 可调用执行)

Array:一种特别的对象(数值下标,内部数据是有序的)

——七大原始值

1、Number(数值)

2、String (字符串)

3、Bigint (大数值)

4、Bloolean(布尔值)

5、Underfined(未定义)

6、Null(空值)

七大原始值时构成各种数据的基石

        原始值在js中时不可变类型,一旦创建就不能更改。(指的是创建的值不能更改,而不是变量不能更改,变量更改的是指向值的地址(开辟一个新的空间存值),而不是将原地址中的值直接更改)

——实例与类型

实例:实例对象

类型:类型对象

1.1.2、判断

——typeof(返回的是数据类型的字符串表达)

可以判断 underfined/ 数值/ 字符串/ 布尔值/ function

不能判断 null object-array (都返回object)

var a;
console.log(a,typeof a === 'undefined',a === underfined);

// underfined ‘undefined’ true true

var a = 3;
console.log(a,typeof a === 'number');
//不能用 a===3 数值是可变 

var a = 'aaa';
console.log(a,typeof a === 'string');


var a = true;
console.log(a,typeof a === 'boolean');


var a = null;
console.log(a,typeof a === 'object'); //null true

——instanof(判断对象的具体类型)

检查某个对象是否由某个类来创建 

//对象
        var b = {
            a:[1,3,2,'aa',console.log],
            b1:function(){
                console.log('xxx')
                return function(){
                    return 'ccc'
                }
            }
        };
        console.log(b instanceof Object);//true
        console.log(b.a instanceof Array);
        //true
        console.log(b.b1 instanceof Function);
        //true
        console.log(typeof b.b1 === 'function');
        //true
        console.log(typeof b.a[4] === 'function');
        b.a[4](4);
        b.b1()();
        console.log(typeof b.b1);//'function'
        console.log(typeof b.a);//'object'

—— ===

可以判断 underfined null (只有一个值

——类型转换

 ——运算符

运算符(操作符)

                - 运算符可以用来对一个或多个操作数(值)进行运算

1、算术运算符

                   + 加法运算符

                    - 减法运算符

                    * 乘法运算符

                    / 除法运算符

                    ** 幂运算

                    % 模运算,两个数相除取余数

- 注意:

                    - 算术运算时,除了字符串的加法,

                        其他运算的操作数是非数值时,都会转换为数值然后再运算

2、赋值运算符

赋值运算符用来将一个值赋值给一个变量

                =

                    - 将符号右侧的值赋值给左侧的变量

                ??=

                    - 空赋值

                    - 只有当变量的值为null或undefined时才会对变量进行赋值

                +=

                    - a += n 等价于 a = a + n

                -=

                    - a -= n 等价于 a = a - n

                *=

                    - a *= n 等价于 a = a * n

                /=

                    - a /= n 等价于 a = a / n

                %=

                    - a %= n 等价于 a = a % n

                **=

                    - a **= n 等价于 a = a ** n

3、一元运算符

一元的±

                + 正号

                    - 不会改变数值的符号

                - 负号

                    - 可以对数值进行符号位取反

               

                当我们对非数值类型进行正负运算时,会先将其转换为数值然后再运算

4、自增和自减

 ++ 自增运算符

                - ++ 使用后会使得原来的变量立刻增加1

                - 自增分为前自增(++a)和后自增(a++)

                - 无论是++a还是a++都会使原变量立刻增加1

                - 不同的是++a和a++所返回的值不同

                    a++ 是自增前的值 旧值

                    ++a 是自增后的值 新值

5、逻辑运算符

 ! 逻辑非

                - ! 可以用来对一个值进行非运算

                - 它可以对一个布尔值进行取反操作

                    true --> false

                    false --> true

                - 如果对一个非布尔值进行取反,它会先将其转换为布尔值然后再取反

                    可以利用这个特点将其他类型转换为布尔值

                - 类型转换

                    转换为字符串

                        显式转换

                            String()

                        隐式转换

                            + ""

                    转换为数值

                        显式转换

                            Number()

                        隐式转换

                            +

                    转换为布尔值

                        显式转换

                            Boolean()

                        隐式转换

                            !!

&& 逻辑与

                - 可以对两个值进行与运算

                - 当&&左右都为true时,则返回true,否则返回false

                - 与运算是短路的与,如果第一个值为false,则不看第二个值

                - 与运算是找false的,如果找到false则直接返回,没有false才会返回true

                - 对于非布尔值进行与运算,它会转换为布尔值然后运算

                    但是最终会返回原值

                    - 如果第一个值为false,则直接返回第一个值

                        如果第一个值为true,则返回第二个值

            || 逻辑或

                - 可以对两个值进行或运算

                - 当||左右有true时,则返回true,否则返回false

                - 或运算也是短路的或,如果第一个值为true,则不看第二个值

                - 或运算是找true,如果找到true则直接返回,没有true才会返回false

                - 对于非布尔值或运算,它会转换为布尔值然后运算

                    但是最终会返回原值

                    - 如果第一个值为true,则返回第一个

                        如果第一个值为false,则返回第二个

6、关系运算符

                - 关系运算符用来检查两个值之间的关系是否成立

                    成立返回true,不成立返回false

                >

                    - 用来检查左值是否大于右值

                >=

                    - 用来检查左值是否大于或等于右值

                <

                    - 用来检查左值是否小于右值

                <=

                    - 用来检查左值是否小于或等于右值

            注意:

                当对非数值进行关系运算时,它会先将前转换为数值然后再比较

                当关系运算符的两端是两个字符串,它不会将字符串转换为数值,

                    而是逐位的比较字符的Unicode编码  

                    利用这个特点可以对字符串按照字母排序  

                注意比较两个字符串格式的数字时一定要进行类型转换  

7、相等运算符

 ==

                - 相等运算符,用来比较两个值是否相等

                - 使用相等运算符比较两个不同类型的值时,

                    它会将其转换为相同的类型(通常转换为数值)然后再比较

                    类型转换后值相同也会返回true

                - null和undefined进行相等比较时会返回true

                - NaN不和任何值相等,包括它自身

            ===

                - 全等运算符,用来比较两个值是否全等

                - 它不会进行自动的类型转换,如果两个值的类型不同直接返回false

                - null和undefined进行全等比较时会返回false

            !=

                - 不等,用来检查两个值是否不相等

                - 会自动的进行类型转换

            !==

                - 不全等,比较两个值是否不全等

                - 不和自动的类型转换

8、条件运算符

          条件运算符

                条件表达式 ? 表达式1 : 表达式2

                - 执行顺序:

                    条件运算符在执行时,会先对条件表达式进行求值判断,

                        如果结果为true,则执行表达式1

                        如果结果为false,则执行表达式2

9、运算符的优先级

 和数学一样,JS中的运算符也有优先级,比如先乘除和加减。

            可以通过优先级的表格来查询运算符的优先级

                https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

               

                - 在表格中位置越靠上的优先级越高,优先级越高越先执行,优先级一样自左向右执行

                    优先级我们不需要记忆,甚至表格都不需要看

                    因为()拥有最高的优先级,使用运算符时,如果遇到拿不准的,可以直接通过()来改变优先级即可

1.1.3、相关问题

——undedined和null

undefined 定义未赋值

null 定义赋值了 但赋值为null

——什么时候赋值为null

*初始赋值为null,表明将要赋值为对象

*结束时,让b指向的对象成为垃圾对象(被垃圾回收器回收)

  • 初始赋值为null,表明将要赋值为对象 // var b = null
  • 确定对象就赋值
  • 最后 b = null 让b指向的对象成为垃圾对象(被垃圾回收器回收)

—— 严格区别变量类型和数据类型

数据类型:

基本类型

对象类型

变量类型(变量内存值的类型):

基本类型:保存的是基本类型的数据

引用类型:保存的是地址值

1.2、数据、变量与内存

1.2.1、什么是数据?

存储在内存中代表特定信息的'东东'

特点:

  • 可传递
  • 可读

 一切皆数据

内存中所有操作的目标都是数据

  • 算术运算
  • 逻辑运算
  • 赋值
  • 运行函数 

1.2.2、什么是内存?

内存条通电后产生的可储存数据临时空间

内存产生和死亡:内存条(电路)-通电-产生-存储数据-处理数据-断电-内存和数据都消失

——内存里的两个数据:

内存存储的数据(一般数据/地址数据)

内存地址值数据

——内存分类:

栈 ——全局变量/局部变量(空间较小)

堆——对象数据

——分配内存

声明变量和函数或创建对象时,JS引擎会自动为此分配一定大小的内存来存放对应的数据

——释放内存

清空内存中数据,标识内存可以再分配使用,内存不释放不能复用

1.2.3、什么是变量?

可变化的量,由变量名和变量值组成

每个变量都对应的一块小内存,变量名用来查找对应的内存

变量值就是内存中保存的数据

1.2.4、内存、数据、变量之间的关系

内存是用来存储数据的空间

变量是内存的标识,通过变量名来找到对应的内存,进而操作(读/写)内存中的数据

1.2.5、相关问题

——var a = xxx,a内存中保存的是什么?

xxx是基本数据,保存的就是这个数据

xxx是对象,保留的是这个对象的地址值

xxx是变量,保存的是xxx的内存内容 可能是基本数据,也可能的地址值

——关于引用变量赋值相关问题

2个引用变量指向同一个对象,保存的都是这个变量的内存的地址值

通过一个变量修改对象内部数据,另一个变量也会随之改变

2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一引用变量依然指向前一个对象

——在js调用函数时传递变量参数时,是值传递还是引用传递

1)值传递

基本数据 和 地址值

2)可能是值传递,也可能是引用传递(地址值)

——js引擎如何管理内存

1)内存生命周期

        分配小内存空间,得到他的使用权

        存储数据,可以反复进行操作

        释放小内存空间

2)释放内存

        局部变量:函数执行完自动释放

        对象:成为垃圾对象——垃圾回收器自动回收

1.3、对象

1.3.1、什么是对象?

多个数据的封装体(集合体)

用来保存多个数据的容器,一个对象代表现实中的一个事物,是该事物在编程中的抽象

               对象中可以存储多个各种类型的数据

                对象中存储的数据,我们称为属性

1.3.2、为什么要用对象?

便于对多个数据进行统一管理,存储复杂数据

1.3.3、对象的组成

属性:状态数据

由属性名(字符串)和属性值(任意)组成

属性名

                - 通常属性名就是一个字符串,所以属性名可以是任何值,没有什么特殊要求

                    但是如果你的属性名太特殊了,不能直接使用,需要使用[]来设置

                    虽然如此,但是我们还是强烈建议属性名也按照标识符的规范命名

                - 也可以使用符号(symbol)作为属性名,来添加属性

                    获取这种属性时,也必须使用symbol

                  使用symbol添加的属性,通常是那些不希望被外界访问的属性

                - 使用[]去操作属性时,可以使用变量

属性值

                - 对象的属性值可以是任意的数据类型,也可以是一个对象

方法:行为数据

一种特殊的属性(属性值是函数)

1.3.4、如何访问对象

 .属性名:编码简单,有时不能用

['属性名']:编码复杂,通用

——什么时候用['属性名']方式

属性名有特殊字符:- 空格等

变量名不确定

属性值是变化的值

    <script>
        // 对象的组成 属性 方法
        var stu = {
            name:'na',//属性:属性名(字符串)和属性值(任意)组成
            age:12,
            setname: function(name){//方法 一种特殊的属性(属性值是函数)
                this.name = name
            }
        }
        // 如何访问对象属性 .属性名:编码简单,有时不能用 ['属性名']:编码复杂,通用
        console.log(stu.age);
        stu.setname('tom');
        stu['setname']('bob');
        console.log(stu.name,stu['age']);
        // 什么时候用['属性名']的方式
        // stu.content-type = 'text/json'; 属性名含有特殊字符 不能用
        stu['content-type'] = 'text/json';
        // 属性名不确定不能用
        var proname = 'nmn';
        var value = 13;
        stu[proname] = value;
        console.log(stu[proname]);
    </script>

1.3.5、检查object中有没有该属性

‘属性名’ in object 

in 运算符

                - 用来检查对象中是否含有某个属性

                - 语法 属性名 in obj

                - 如果有返回true,没有返回false

1.36、对象字面量

 对象字面量

                - 可以直接使用{} 来创建对象

                - 使用{}所创建的对象,可以直接向对象中添加属性

                - 语法:

                    {

                        属性名:属性值,

                        [属性名]:属性值,

                    }

1.4、函数

1.4.1、什么是函数

实现特定功能的n条语句的封装体

只有函数可以执行,其他类型的数据不能执行

                - 函数也是一个对象

                - 它具有其他对象所有的功能

                - 函数中可以存储代码,且可以在需要时调用这些代码

语法:

                function 函数名(){

                    语句...

                }

 使用typeof检查函数对象时会返回function

1.4.2、为什么要用函数

提高代码复用

便于阅读交流

1.4.3、如何定义函数

1.函数声明

                    function 函数名(){

                        语句...

                    }

2.函数表达式

                    const 变量 = function(){

                        语句...

                    }

3.箭头函数

                    () => {

                        语句...

                    }

function fn(){
            console.log("函数声明所定义的函数~")
        }

        const fn2 = function(){
            console.log("函数表达式")
        }

        const fn3 = () => {
            console.log("箭头函数")
        }

        const fn4 = () => console.log("箭头函数")
        

        console.log(typeof fn)//function
        console.log(typeof fn2)//function
        console.log(typeof fn3)//function
        console.log(typeof fn4)//function

        fn4()//箭头函数

1.4.4、如何调用函数

  调用函数就是执行函数中存储的代码

  - 语法:

        直接调用:函数名()

        通过对象调用:obj.函数名()

        new调用:new 函数名()

        临时让test成为obj的方法进行调用:函数名.call/apply(obj)

        var fn1 = function() {
            console.log('fn1');
        }
        fn1();

        var obj = {};
        function test () {
            this.age = 23
        }
        // 不能直接调用 因为obj对象里面 没有这个方法
        test.call(obj);
        console.log(obj.age);

1.4.5、回调函数

——什么函数才是回调函数

1)定义了

2)没有调用

3)执行了

——常见的回调函数

dom事件回调函数

定时器(超时/循环)回调函数

ajax请求回调函数

生命周期回调函数

1.4.6、IIFE

立即执行函数 Immediately-Involved Function Expression

                - 立即是一个匿名的函数,并且它只会调用一次

                - 可以利用IIFE来创建一个一次性的函数作用域,避免变量冲突的问题

——作用

隐藏实现

不会污染外部(全局)命名空间

——如果使用let声明的变量,可以使用{}来创建块作用域

        // 匿名函数自调用
        ;(function () {
            var a = 3
            console.log(a + 123)
        })()
        /*
        (function () {
            var a = 3
            console.log(a + 123)
        }())
        */
        var a = 4
        console.log(a)
        ;(function () {
            var a = 1
            function test () {
                console.log(++a)
            }
            window.$ = function() {
                return {
                    test:test
                }
            }
        })()
        $().test()

1.4.7、函数中的参数

形式参数

                - 在定义函数时,可以在函数中指定数量不等的形式参数(形参)

                - 在函数中定义形参,就相当于在函数内部声明了对应的变量但是没有赋值

实际参数

                - 在调用函数时,可以在函数的()传递数量不等的实参

                - 实参会赋值给其对应的形参

                -传递实参时,传递并不是变量本身,而是变量中存储的值

                - 参数:

                    1.如果实参和形参数量相同,则对应的实参赋值给对应的形参

                    2.如果实参多于形参,则多余的实参不会使用

                    3.如果形参多于实参,则多余的形参为undefined

                 - 参数的类型

                    - JS中不会检查参数的类型,可以传递任何类型的值作为参数

箭头函数的参数

                -当箭头函数中只有一个参数时,可以省略()

                -定义参数时,可以为参数指定默认; 默认值,会在没有对应实参时生效

1.4.7、对象作为参数

对象可以作为参数传递时,传递时对象(变量)中存储的值

                -修改变量时,只会影响当前的变量

                -修改对象时,如果有其他变量指向该对象则所有指向该对象的变量都会受到影响

function fn(a){
            // console.log("a =", a)
            // console.log(a.name)

            // a = {} // 修改变量时,只会影响当前的变量
            a.name = "猪八戒" // 修改对象时,如果有其他变量指向该对象则所有指向该对象的变量都会受到影响
            console.log(a)
            
        }

        // 对象可以作为参数传递
        let obj = {name:"孙悟空"}

        // 传递实参时,传递并不是变量本身,而是变量中存储的值
        // fn(obj)

        // console.log(obj)

        let obj2 = {name:"沙和尚"}
        
        // 函数每次调用,都会重新创建默认值
        function fn2(a = {name:"沙和尚"}){
            console.log("a =", a)
            a.name = "唐僧"
            console.log("a =", a)
        }

        fn2() // 沙和尚 唐僧
        fn2() // 沙和尚 唐僧 or 唐僧 唐僧

1.4.8、函数的返回值

在函数中,可以通过return关键字来指定函数的返回值

                   -返回值就是函数的执行结果,函数调用完毕返回值便会作为结果返回      

                   -任何值都可以作为返回值使用(包括对象和函数之类)

                    -如果return后不跟任何值,则相当于返回undefined

                    -如果不写return,那么函数的返回值依然是undefined

  return一执行函数立即结束

箭头函数的返回值:

                -箭头函数的返回值可以直接写在箭头后

                -如果直接在箭头后设置对象字面量为返回值时,对象字面量必须使用()括起来

1.4.9、作用域

作用域(scope)

 - 作用域指的是一个变量的可见区域

- 作用域有两种:

       全局作用域

            - 全局作用域在网页运行时创建,在网页关闭时消耗

           - 所有直接编写到script标签中的代码都位于全局作用域中

           - 全局作用域中的变量是全局变量,可以在任意位置访问

        局部作用域

             - 块作用域

             - 块作用域是一种局部作用域

             - 块作用域在代码块执行时创建,代码块执行完毕它就销毁

             - 在块作用域中声明的变量是局部变量,只能在块内部访问,外部无法访问

1.4.10、函数中的this

——this是什么

任何函数本质上都是通过某个对象来调用的

所有函数内部都有一个变量this

它的值是调用函数的当前对象

——如何确定this的值

函数名():如果没有指定就是window (直接调用)

变量名.函数名():变量

new 函数名():新创建的对象

变量名.call(obj):obj

1.4.11、window对象

      - 在浏览器中,浏览器为我们提供了一个window对象,可以直接访问

       - window对象代表的是浏览器窗口,通过该对象可以对浏览器窗口进行各种操作

                    除此之外window对象还负责存储JS中的内置对象和浏览器的宿主对象

        - window对象的属性可以通过window对象访问,也可以直接访问

        - 函数就可以认为是window对象的方法

向window对象中添加的属性会自动成为全局变量

var声明变量:

var 用来声明变量,作用和let相同,但是var不具有块作用域

                - 在全局中使用var声明的变量,都会作为window对象的属性保存

                - 使用function声明的函数,都会作为window的方法保存

                - 使用let声明的变量不会存储在window对象中,而存在一个秘密的小地方(无法访问)

                - var虽然没有块作用域,但有函数作用域

1.4.12、提升

变量的提升

                    - 使用var声明的变量,它会在所有代码执行前被声明

                        所以我们可以在变量声明前就访问变量

 函数的提升

                    - 使用函数声明创建的函数,会在其他代码执行前被创建

                        所以我们可以在函数声明前调用函数

let声明的变量实际也会提升,但是在赋值之前解释器禁止对该变量的访问

            console.log(a)  // 2

            var a = 1

//debugger // 在代码中打了一个断点

            console.log(a) // 1

            function a() {
                alert(2)
            }

            console.log(a) // 1

            var a = 3

            console.log(a) // 3

            var a = function () {
                alert(4)
            }

            console.log(a) // 4

            var a

            console.log(a) // 4

2、函数高级

2.1、原型和原型链

2.1.1、原型(prototype)

每个函数(function)都有原型属性,默认指向object空对象(原型对象)

原型对象(object)中有一个属性constructor,它指向函数对象

——给原型对象添加属性(一般都是方法)

作用:函数的所有实例对象自动拥有原型中的属性(方法)

2.1.2、显式原型和隐式原型

1)每个函数(function)中都有一个prototype,即显式原型(属性)

2)每个实例对象中都有一个__proto__,称为隐式原型(属性)

3)对象的隐式原型的值为其对应构造函数的显式原型的值

4)可以直接操作显式原型,但不能直接操作隐式原型(ES6之前)

  <script>
        function Fn() {
        } //内部语句:this.prototype = {}
    // 每个函数(function)中都有一个prototype,即显式原型(属性)
    console.log(Fn.prototype)
    // 2)每个实例对象中都有一个__proto__,称为隐式原型(属性)
    var fn = new Fn()
    // 创建的实例对象继承了函数的原型属性  该属性被称为隐式原型
    // 内部语句:this.__proto__ = Fn.prototype
    console.log(fn.__proto__)//对象
    // 对象的隐式原型的值为其对应构造函数的显式原型的值
    //即地址值相同
    console.log(Fn.prototype === fn.__proto__)
    //true
    // 给原型添加属性
    Fn.prototype.test = function (){
        console.log('test');
    }
    //通过实例对象调用原型方法
    fn.test()
    </script>

2.1.3、原型链

1)访问一个对象的属性时:

        先在自身属性中查找,找到返回

        如果没有再沿着__proto__这条链向上查找,找到返回

        如果最终没找到,返回underfined

别名:隐式原型链

作用:查找对象的属性(方法)

  • 继承
  • prototype用来实现基于原型的继承与属性的共享
  • 避免了代码冗余,公用的属性和方法,可以放到原型对象中,这样,通过该构造函数实例化的所有对象都可以使用该对象的构造函数中的属性和方法!
  • 减少了内存占用
<script>
        // 执行代码前先导入内置函数
       // console.log(Object.prototype)
        console.log(Object)
        function fn() {
            this.test1 = function() {
                console.log('test1')
            }
        }
        fn.prototype.test2 = function () {
            console.log('test2')
        }
        var Fn = new fn()
        Fn.test1()
        Fn.test2()
       // Fn.test3()//underfined
</script>

 2)构造函数/原型/实体对象的关系

3)构造函数/原型/实体对象的关系

所有函数都是Function的实例(包括Function)

而函数又是特殊的对象(Object)

所以 函数的显式原型指向的对象都是默认的空Object实例对象

最后 原型链的尽头就是object的prototype里面的隐式原型__proto__

 2.1.4、原型链_属性的相关问题

读取对象的属性值时 会自动到原型链中查找

设置对象的属性值时 不会查找原型链 如果当前对象中没有此属性 直接添加此属性和设置其值

方法一般定义在原型中 属性一般通过构造函数定义在对象本身上

function person(name,age) {
            this.name = name
            this.age = age
       }
       person.prototype.test = function(name){
            this.name = name
       }
       var p1 = new person('bob',12)
       p1.test('cat')
       var p2 = new person('tom',13)
       p2.test('bob')
       console.log(p1,p2);
       console.log(p1.__proto__===p2.__proto__);//true

  2.1.5、探索instanceof

表达式:A instanceof B 

如果 B函数的显式原型对象在A对象的原型链上,返回True,否则返回False

用来判断A对象是不是B函数的实例对象

2.1.6、面试题

//    测试题1
       function A() {
       }
       A.prototype.n = 1
       var b = new A()
       A.prototype = {
        n:2,
        m:3
       }
       var a = new A()
    //    这里原型对象指向的object时新开辟的空间
       console.log(b.n,b.m,a.n,a.m);// 1 underfined 2 3
    // 测试题2
    function F(){}
    Object.prototype.a = function(){
        console.log('a()')
    }
    Function.prototype.b = function(){
        console.log('b()')
    }
    var f = new F()
    f.a() //a()
    //f.b() //报错
    F.a() //a() 把这个函数看成实例对象
    F.b() //b()

2.2、执行上下文与执行上下文栈

2.2.1、变量提升和函数提升

变量提升:

通过(var)声明的变量,在定义语句之前就可以访问到

值为underfined

函数提升:

通过function声明的函数,在之前就可以直接调用

值为函数定义(对象)

var a = 3
    function fn(){
        console.log(a)//如果在函数内部没有定义a 则直接输入 全局变量 a =3
        //如果有定义 则进行变量提升 输出underfined
        var a = 4 
    }
    fn()
    fn1() //函数提升 可直接调用
    function fn1(){
        console.log('fn1()');
    }

 2.2.2、执行上下文

1)代码分类

全局代码

函数(局部)代码

2)全局执行上下文

  • 在执行全局代码前将window确定为全局执行上下文
  • 对全局数据进行预处理
  •  var定义的全局变量——》underfined 添加为window的属性
  • function声明的全局函数——》赋值(fun) 添加为window的方法
  • this——》赋值(window)
  • 开始执行全局代码

执行全局代码时 在window中找变量

3)函数执行上下文

  • 在调用函数,准备执行函数体之前,创建对应的函数上下文对象
  • 对局部数据进行预处理
  • 形参变量——》赋值(实参)——》添加为执行上下文的属性
  • arguments——》赋值(实参列表)添加为执行上下文的属性
  • var定义 的局部变量——》underfined 添加为执行上下文的属性
  • function声明的函数——》赋值(fun)添加为执行上下文的方法
  • this——》赋值 (调用函数的对象)
  • 开始执行函数体代码

函数执行后 才给变量创建空间

2.2.3、执行上下文栈

  • 在全局代码执行前,js引擎会创建一个栈来存储管理所有的执行上下文对象
  • 在全局执行上下文(window)确定后,将其添加到栈中(压栈)
  • 在函数执行上下文创建后,将其添加到栈中(压栈)
  • 在当前函数执行完后,将栈顶的对象移除(出栈)
  • 当所有的代码执行完后,栈中只剩下window

2.3、作用域与作用域链

2.3.1、作用域

一个代码段所在区域 时静态的 在编写代码时就确定了

——分类

全局作用域

函数作用域

ES5没有块级作用域

——作用

隔离变量 不同作用域下同名变量不会有冲突

——区别1

全局作用域之外,每个函数都会创建自己的作用域

作用域在函数定义时就已经确定了,而不是在函数调用时

全局执行上下文环境实在全局作用域确定之后,js代码马上执行之前创建

函数执行上下文环境实在调用函数时,函数体代码执行之前创建

——区别2

作用域时静态,只要函数定义好了就一直存在,且不会变化呀

上下文环境时动态。调用函数时创建,函数调用结束时就会释放

——联系

上下文环境(对象)是从属于所在的作用域

全局上下文环境——》全局作用域

函数上下文环境——》对应的函数作用域

2.3.2、作用域链

——理解

多个上下级关系的作用域形成的链,方向时从下向上(从内到外)

查找变量时就是沿着作用域来查找

——查找变量的规则

在当前作用域下的执行上下文中查找对应的属性,如果有直接返沪,否则进入外层作用域

在上一级的作用域的执行上下文查找对应的属性,如果直接返回,没有就进入下一级作用域

再次查找,指导全局作用域,如果找不到的就报错异常

2.3.3、面试题

var fn1 = function () {
        console.log(fn1);
    }
    fn1()
    var obj = {
        fn2:function() {
            //console.log(fn2)//报错 函数作用域里找不到 fn2
            console.log(this.fn2);//输出这个函数 
        }
    }
    obj.fn2()

2.4、闭包

2.4.1、如何产生闭包

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包

2.4.2、闭包到底是什么

使用chrome调试查看

  • 闭包是嵌套的内部函数
  • 包含被引用变量(函数)的对象

注意:闭包存在于嵌套的内部函数中

2.4.3、产生闭包的条件

函数嵌套

内部函数引用了外部函数的变量/函数,使得内部函数形成闭包

//闭包的产生
    function fn3 () {
        var a = 2
        function fn0 () {
            console.log(a)
        }
        //fn0() 调用执行函数 或者返回函数 现在才会产生闭包 如果没有执行调用 js引擎会进行优化
        return fn0()
    }
    fn3()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值