• JS进阶笔记(原型、继承、this指向、闭包、递归、正则表达式)


    文章目录

    1、构造函数、实例对象、原型对象三者之间的关系

    在这里插入图片描述
    实例对象可以通过__proto__.constructor指向构造函数,实际上是先借助__proto__指向原型对象prototype,然后再通过原型对象的.constructor指向构造函数。

    2、原型链

    在这里插入图片描述
    // 1、只要是原型对象就有__proto__原型,指向原型对象。
    // 2、Star原型对象里面__proto__原型指向的是Object.prototype
    // 3、Object原型对象里面__proto__原型指向的是null ,即已经到了最顶层

    2.1、JS成员查找机制
    1、当访问一个对象的属性或者方法时,首先会查找这个对象自身有没有该属性
    2、如果没有就去该对象的原型对象中查找(也就是`__proto__`指向的`prototype`原型对象)
    3、如果还没有就查找原型对象的原型(`Object`的原型对象)
    4、以此类推一直找到`Object`为止(null)
    
    2.2、原型对象中的this指向

    1、在构造函数中,this指向实例对象
    2、原型对象函数里面的this,指向的是实例对象

    2.3、利用原型对象扩展内置对象方法
    //  扩展求和方法
    Array.prototype.sum = function () {
            let sum = 0;
            for (let i = 0; i < this.length; i++){
                sum += this[i];
            }
            return sum
        };
    

    错误示范

    //  会报错,会把原先的方法覆盖掉
        Array.prototype= {
            sum: function () {
                let sum = 0;
                for (let i = 0; i < this.length; i++){
                    sum += this[i];
                }
                return sum
            }
        }
    

    3、继承

    3.1 call方法的应用

    调用这个函数,并且修改函数运行时的this指向
    fun.call(thisArg, arg1, arg2, ...)
    thisArg:当前调用函数this的指向对象(当前调用的函数this指向谁)
    arg1、arg2:传递的其他参数

    ES6之前并没有提供extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承

    核心原理
    通过call()把父类的this指向子类的this,这样就可以实现子类继承父类的属性

    // 借用父构造函数继承属性
        // 1、父构造函数
        function Father(uname, age) {
            // this指向父构造函数的实例对象
            this.name = uname;
            this.age = age
        }
        Father.prototype.money = function (){
            console.log('父亲要挣钱')
        };
        // Son.prototype = Father.prototype;   直接赋值会有问题,如果
        // 子原型对象发生变化,那么父原型对象也会变化
        
        // 2、子构造函数
        function Son(uname, age, score) {
            // this指向子构造函数的实例对象
            Father.call(this, uname, age,);
            this.score = score
        }
        // 这个是子构造函数专门的方法
        // 将Father实例对象赋值给Son原型对象,此时,Father实例对象和Father原型对象
        处于不同的存储地址中,所以不会造成数据混乱
        // 又因为Father实例对象可以通过__proto__访问Father原型对象,所以Son原型对象
        可以使用Father原型对象中的money方法
    
        Son.prototype = new Father();
        // 如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的构造函数
        Son.prototype.constructor = Son;
        Son.prototype.exam = function (){
            console.log('孩子要考试');
        }
        let son = new Son('ckw',18, 100);
        // console.log(son);
        console.log(son);
        console.log(Father.prototype);
        console.log(Son.prototype.constructor);
    

    4、类的本质

    类的本质还是一个函数,我们也可以简单的认为 类 就是构造函数的另外一种写法

    //  ES6之前通过 构造函数+原型对象 实现面向对象变成
        // 1、构造函数有原型对象prototype
        // 2、构造函数原型对象prototype里面有constructor指向构造函数本身
        // 3、构造函数可以通过原型对象添加方法
        // 4、构造函数创建的实例对象里面有__proto__原型指向构造函数的原型对象
        class Star{
    
        }
    
        console.log(typeof Star);
        //  ES6之后通过 类 实现面向对象编程
        //  1、类也有原型对象prototype
        console.log(Star.prototype);
        // 2、类的原型对象prototype里面也有constructor指向构造函数本身
        console.log(Star.prototype.constructor);
        // 3、类也可以通过原型对象添加方法
        Star.prototype.sing = function (){
            console.log('我会唱歌')
        }
        let ldh = new Star();
        console.dir(ldh);
        // 5、类创建的实例对象也有__proto__原型指向类的原型对象
        console.dir(ldh.__proto__ === Star.prototype);
        // 所以ES6的类就是语法糖
    

    5、ES5中的新增方法

    5.1、数组方法

    迭代(遍历)方法:forEach()、map()、filet()、some()、every()

    1、array.forEach(function(currentValue, index, arr))

    • currentValue:数组当前项的值
    • index:数组当前项的索引
    • arr:数组对象本身
    • forEach没有返回值

    2、 array.filter(function(currentValue, index, arr))
    filter()方法会创建一个新的数组,新数组的元素是经过筛选以后的数组

    • currentValue:数组当前项的值
    • index:数组当前项的索引
    • arr:数组对象本身
    // filter筛选方法
        let arr = [11,22,33,44,20];
        let newArr = arr.filter(function (value, index){
            return value >= 20;
        })
        console.log(newArr);
    

    3、array.some(function(currentValue, index, arr){})

    • some()方法用于检测数组中的元素是否满足指定条件,
    • 注意它的返回值是boolean值,如果查到就返回true,没查到就返回false
    • 如果找到第一个满足条件的元素,就终止循环,不再继续查找
    • currentValue:数组当前项的值
    • index:数组当前项的索引
    • arr:数组对象本身
    let arr = [11,22,33,4];
        let flag = arr.some(function (value, index){
            return value < 3;
        })
        console.log(flag);
        let arr1 = ['red', 'pink', 'yellow']
        let flag1 = arr1.some(function (value){
            return value === 'red';
        })
        console.log(flag1);
    
        // filter也是查找满足条件的元素,但返回值是一个数组,而且会返回所有符合条件的元素
        // some的返回值是true/false,如果查找到第一个满足条件的元素,就会终止循环
    

    forEach和some的区别
    1、在forEach里面,return不会终止迭代
    2、

    let arr = [1,2,4,'pink','green',7,11,22,33];
        arr.some(function (value){
            if (value === 'green'){
                console.log('找到了该元素');
                return true;     //some中return true会终止迭代,效率更高
            }
            console.log(value)
        });
        arr.forEach(function (value){
            if (value === 'green'){
                console.log('找到了该元素');
                return true;        // forEach中return不会终止迭代
            }
            console.log(value);
        })
    

    forEach输出结果在这里插入图片描述
    some输出结果在这里插入图片描述

    5.2、trim方法

    trim方法:去除字符串两端的空格,但是不能去除字符串中的空格

    5.3、Object.defineProperty方法

    object.defineProperty:定义新属性或修改原有的属性
    object.defineProperty(obj, prop, descriptor)接收三个参数
    obj:必传。目标对象
    prop必传。需要定义或修改的属性的名字
    descriptor必传。目标属性所拥有的特性
    descriptor说明
    该参数需要以对象形式书写

    • value:设置属性的值,默认为undefined
    • wirtable:值是否可以重写,默认为false
    • enumerable:目标属性是否可以被枚举,true/false,默认为false
    • configurable:目标属性是否可以被删除或再次修改特性,true/false,默认为false

    6.函数的定义和调用

    6.1、函数的定义方式

    1、函数声明方式function关键字(命名函数)

    function fn() {}
    

    2、函数表达式(匿名函数)

    let fn = function () {}
    

    3、new Function(‘参数1’, ‘参数2’, ‘函数体’)
    Function里面参数都必须是字符串格式
    这种方法执行效率低,不方便书写,较少使用

    let f = new Function ('console.log('123')') {}		//不传参情况
    

    4、所有函数都是 Function的实例(对象)
    函数也属于对象,因为有原型__proto__
    在这里插入图片描述

    6.2、函数的调用方式
    // 1、普通函数
        function fn() {
            console.log('nb666');
        }
        fn();
        fn.call();
        // 2、对象的方法
        let o = {
            name:'ckw',
            say: function (){
                console.log('我是o对象的say方法');
            }
        }
        o.say();
        // 3、构造函数
        function Star() {}
        new Star();
        // 4、绑定事件函数
        btn.onclick = function (){
            console.log('123123')
        }
        // 5、定时器函数
        setInterval(function (){},1000);     //每1秒执行一次
        // 6、立即执行函数
            (function (){
                console.log(123);
            })()
        // 立即执行函数会自动调用
    
    6.3 函数内this的指向

    this指向

    6.3.1改变函数内部this指向

    1、call()方法
    fun.call(thisArg, arg1, arg2, ...)
    call()方法调用一个对象,简单理解为调用函数的方式,但是它可以改变函数的this指向

        let o = {
            name: 'ckw'
        };
        function fn(a, b) {
            console.log(this);
            console.log(a+b)
        }
        fn.call(o,1,2);
    
        function Father(uname, age, sex){
            this.uname = uname;
            this.age = age;
            this.sex = sex;
        }
        function Son(uname, age, sex){
            Father.call(this,uname, age, sex);
        }
        let son = new Son('ckw',18,'男');
        console.log(son);
    

    输出结果:在这里插入图片描述
    在这里插入图片描述

    call方法:第一个可以调用函数,第二个可以改变函数内的this指向。也可以实现继承

    2、apply()方法

    // 2、apply()方法
        let o = {
            name: 'andy'
        };
        function fn(arr){
            console.log(this);
            console.log(arr);		// 此处只输出第一个pink
        }
        fn.apply(o, ['pink','blue','aqua']);
        // 1、apply()方法既可以调用函数,也可以修改函数内部的this指向
        // 2、但是它的第二个参数必须是数组/伪数组
        // 3、apply()的主要应用,比如说可以利用 apply 借助Math内置对象求最大值
        let arr = [1,0,3,8,9,77];
        let arr1 = ['red', 'aqua']
        let res = Math.max.apply(null,arr);
        let res1 = Math.max.apply(null,arr1);			// 如果传入的是字符串数组,则会输出NaN
        console.log(res);
        console.log(res1);
    

    值得注意的是,当apply方法的第二个参数传入的是一个有多个元素的数组时,它只会输出一个元素。

    3、bind()方法
    bind()方法不会调用函数,但是能改变函数内部this指向
    fun.bind(thisArg, arg1, arg2, ...)

    • thisArg:在fun函数运行时指定的this值
    • arg1, arg2:传递的其他参数
    • 返回由指定的this值和初始化参数改造的原函数拷贝
    6.3.2 call、apply、bind方法总结

    相同点:都可以改变函数内部的this指向
    不同点:

    1. call和apply会调用函数,并改变函数内部this指向。
    2. call和apply传递的参数不一样,call是用arg1,arg2, ...的方式传递,apply是用[arg1,arg2, ...]的方式传递
    3. bind不会调用函数,就可以改变函数内部的this指向

    主要应用场景

    1. call经常用作继承
    2. apply经常跟数组有关,例如借助数学对象求最大和最小值
    3. bind不调用函数,但是需要改变this指向,例如改变定时器中的this指向

    7、严格模式

    7.1 什么是严格模式

    JS除了提供普通模式以外,还提供了严格模式(strict mode),ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格模式下运行JS代码。
    严格模式对正常的JS语义做了一些更改

    1. 除了JavaScript语法的一些不合理,不严谨之处,减少了一些怪异行为。

    2. 消除代码运行的一些不安全之处,保证代码运行的安全。

    3. 提高编译器效率,增加运行效率

    4. 禁用了ECMAScript的未来版本中可能会定义的一些语法,为未来的JS版本做好铺垫。比如一些保留字:

    5. enum,class,export,extends,import等不能做变量名

    7.2 怎么开启严格模式
    1. 为脚本开启严格模式
      在这里插入图片描述

    2. 为函数开启严格模式
      在这里插入图片描述

    7.3 严格模式中的变化
    1. 严格模式下的this指向问题
      普通模式中全局作用域的函数中的this指向window对象
      严格模式下全局作用域的函数中的this为undefined
    2. 严格模式下如果构造函数不加new调用,this会报错
    3. 定时器中的this指向的还是window对象
    4. 事件、对象还是指向调用者

    严格模式中的函数不能有重名参数

    8、高阶函数

    高阶函数是对其他函数进行操作的函数,它接收函数作为参数或者将函数作为返回值输出

    function fn(a, b, callback) {
            console.log(a + b);
            callback && callback();
        }
        fn(1,2,function (){alert('我是最后调用的')});
    
    function fn1() {
            return function (){};
        }
    fn1();
    

    上面两种函数都属于高阶函数

    9、 闭包

    9.1变量作用域

    变量根据作用域的不同可以分为:全局变量局部变量

    1. 函数内部可以使用全局变量
    2. 函数外部不可以使用局部变量
    3. 当函数执行完毕,本作用域内的局部变量会销毁
    9.2 什么是闭包

    闭包指有权访问另一个函数作用域中变量的函数
    即一个作用域可以访问另一个函数内部的局部变量

    // 闭包:fun这个函数访问到了fn函数作用域中的局部变量num,此时就产生了闭包
    // fn外面的作用域也可以访问fn内部的局部变量num
    // 闭包的主要作用:延伸了变量的作用范围。
        function fn() {
            let num = 10;
            // function fun() {
            //     console.log(num);
            // }
            return function () {
                // 此时这个函数内并没有Num变量,但是依旧可以正常打印,这就产生了闭包
                console.log(num);
            };
        }
        let f = fn();
        f();
    // 类似于
    // f = function fun() {
    // console.log(num);
    // }
    
    9.3 闭包总结
    9.3.1 闭包是什么?

    闭包是一个函数(一个作用域可以访问另一个作用域的局部变量)

    9.3.2 闭包的作用是什么?

    延伸了变量的作用范围

    10、 递归

    10.1 什么是递归?

    如果一个函数可以在内部调用其本身,那么这个函数就是递归函数
    递归函数的作用和循环效果一样
    由于递归很容易发生“栈溢出”错误(stack overflow ),所以必须要加退出条件return

    10.2 递归的应用案例
    10.2.1.利用递归求阶乘
    function fn(n){
            if (n === 1){
                return 1;
            }
            return n * fn(n - 1);
        }
    
        console.log(fn(3));
        console.log(fn(4));
    
        // 详细思路
        // 第一次执行,return 3*fn(2)
        // 第二次执行,return 3*( 2 * fn(2 - 1) )
        // 第三次执行,return 3*( 2 * 1 ),最后输出结果为6
    
    10.2.2.利用递归求斐波那契数列
    function fb(n) {
            if (n === 1 || n === 2) {
                return 1;
            }
            return fb(n - 1) + fb(n - 2);
        }
    
        console.log(fb(6));
        // 输入n = 6
        // 第一次 return fb(5)+fb(4)
        // 第二次 return fb(4)+fb(3) + return fb(3)+fb(2)
        // 第三次 return fb(3)+fb(2) + return fb(2)+fb(1)  +  return fb(2)+fb(1) + return 1
        // 第四次 return fb(2)+fb(1) + return 1   +   return 1+1 + return 1+1 + return 1
        // return 1+1 + return 1 + return 1+1 + return 1+1 + return 1;
        // 输出结果为8
    
    10.2.3.利用递归遍历数据
    let data = [
        {
          id: 1,
          name: '家电',
          goods: [
            {
              id: 11,
              gname: '洗衣机'
            },
            {
              id: 22,
              gname: '冰箱',
              goods: [
                {
                  id: 111,
                  name: '海尔'
                },
                {
                  id: 112,
                  name: '西门子'
                }
              ]
            }
          ]
        },
        {
          id: 2,
          name: '服饰'
        }
      ]
      // 需要输入id就返回name
      // 利用forEach()遍历数组中的每一个对象
      function getId(json, id) {
        let obj = {}
        json.forEach(item => {
          // console.log(item);
          if (item.id === id) {
            // console.log(item);
            obj = item;
            //    想要里层的id:11/id:12的数据,可以利用递归函数
            //    里面应该有goods这个数组,且数组不为空
          } else if (item.goods && item.goods.length !== 0) {
            // 再调用一次getId
            obj = getId(item.goods, id)
          }
        });
        return obj;
      }
    
      console.log(getId(data, 1));
      console.log(getId(data, 22));
      console.log(getId(data, 112));
    
    10.3 浅拷贝和深拷贝

    1.浅拷贝只是拷贝一层,更深层次对象级别的只会拷贝引用(地址)

      let obj = {
        id: 1,
        name: 'zs',
        msg: {
          age: 18
        }
      };
      let o = {};
      // for (let k in obj){
      //     k是属性名,obj[k]是属性值
      //     o[k] = obj[k];
      // }
      // console.log(o);
      // o.msg.age = 20;
      // console.log(obj);
    
      // 用ES6语法糖:Object.assign()来实现浅拷贝
      Object.assign(o, obj);
      console.log(o);
      o.msg.age = 20;
      console.log(obj);
    

    在这里插入图片描述
    也可以用ES6语法糖Object.assign()来实现浅拷贝,但是修改age的结果与上面这种方法相同
    2.深拷贝会拷贝多层,每一层级别的数据都会拷贝
    递归方式实现深拷贝

      let obj = {
        id: 1,
        name: 'zs',
        msg: {
          age: 18
        },
        color: ['red', 'blue']
      };
      let o = {};
    
      // 封装函数
      // newobj[k]是key,item是value
      function deepCopy(newobj, oldobj) {
        for (let k in oldobj) {
          // 判断属性值是哪种数据类型
          // 1.获取属性值   oldobj[k]
          let item = oldobj[k];
          // 2.判断这个值是否是数组
          if (item instanceof Array) {
            newobj[k] = [];
            deepCopy(newobj[k], item)
          } else if (item instanceof Object) {
            // 3.判断这个值是否是对象
            newobj[k] = {};
            deepCopy(newobj[k], item)
          } else {
            // 4.属于简单数据类型
            newobj[k] = item
          }
        }
      }
    
      deepCopy(o, obj);
      console.log(o);
    

    11.、正则表达式

    11.1 什么是正则表达式

    正则表达式是用于匹配字符串中字符组合的模式,在JS中,正则表达式也是对象。正则表达式通常被用来检索、替换那些符合某 个模式/规则的文本,例如表单验证等等。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等。

    11.2 正则表达式的特点

    1.灵活性、逻辑性和功能性非常强
    2.可以迅速的用极简单的方式达到字符串的复杂控制
    3.对初学者来说比较晦涩难懂
    4.实际开发中,一般是直接复制写好的正则表达式,但是需要会使用并且根据实际情况修改。

    11.3 正则表达式在JS中的使用
    11.3.1 创建正则表达式。

    1.通过RegExp对象的构造函数创建

    // 利用RegExp对象来创建正则表达式
        let regexp = new RegExp('/123/');
        console.log(regexp);
    
    1. 通过字面量创建正则表达式
        // 利用字面量创建正则表达式
        let reg = /123/;
    
        // test方法用来检测字符串是否符合正则表达式要求的规范
        console.log(reg.test(123));
        console.log(reg.test('abc'));
    
    11.3.2 正则表达式的组成

    一个正则表达式可以由简单的字符构成,例如/abc/,也可以是简单和特殊字符的组合,例如/ab*c/。其中,特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如^、$、+

    1、边界符

    在这里插入图片描述

    let rg = /abc/; //正则表达式里面不需要加引号,无论数字型或字符串型
        // /abc/ 只要包含有abc这个字符串返回的都是true
        console.log(rg.test('abcc'));   // true
        console.log('---------');
        let reg = /^abc/;
        // /^abc/ 表示以必须以abc开头
        console.log(reg.test('abcd'));  // true
        console.log(reg.test('aabcd')); //false
        console.log('---------');
        let reg1 = /^abc$/;
        // /^abc$/ 表示精确匹配,要求必须是abc字符串才符合规范
        console.log(reg1.test('abc'));  // true
        console.log(reg1.test('abcd'));  // false
        console.log(reg1.test('aabcd')); // false
        console.log(reg1.test('abcabc')); // false
    
    2、字符类

    []字符类:表示有一系列字符可供选择,只要匹配其中一项就可以(多选一)

    let rg = /[abc]/    //只要包含a、b、c中的任何一项,则返回true
        console.log(rg.test('aa1q123'));    //true
        console.log(rg.test('zsdf3123'));    //false
        console.log('----------');
        let rg1 = /^[abc]$/     //三选一,只有是a/b/c才返回true
        console.log(rg1.test('ab'));    //false
        console.log(rg1.test('a'));    //true
        console.log(rg1.test('b'));    //true
        console.log(rg1.test('c'));    //true
        console.log('----------');
        let reg = /^[a-z]$/     //26个英文字母中的任意一个都返回true
        console.log(reg.test('a'));     //true
        console.log(reg.test('b'));     //true
        console.log(reg.test('c'));     //true
        console.log(reg.test('A'));     //false
        console.log(reg.test(1));     //false
        console.log('------------');
    
    
        // 字符组合
        let reg1 = /^[a-zA-Z0-9_·-]$/     //大小写的英文字母和数字都可以
        console.log(reg1.test('a'));     //true
        console.log(reg1.test('B'));     //true
        console.log(reg1.test('1'));     //true
        console.log(reg1.test('_'));     //true
        console.log(reg1.test('-'));     //true
        console.log(reg1.test('·'));     //true
        console.log(reg1.test('-_'));    //false
        console.log('--------------------');
    
        // 如果中括号中有'^'则代表取反,需要与边界符分割开理解
        let reg2 = /^[^a-zA-Z0-9_·-]$/
        console.log(reg2.test('!'));    // true
    
    3、量词符
    // 量词符:用来设定某个模式出现的次数
        // 简单理解:就是让下面的/^a$/字符重复多少次
        // let reg = /^a$/
    
        // * 代表 >= 0 代表可以出现0次和很多次
        let reg = /^a*$/
        console.log(reg.test(''));      // true
        console.log(reg.test('a'));     // true
        console.log(reg.test('aa'));    // true
        console.log('----------------');
    
        // + 代表 >=1 代表可以出现1次和很多次
        let reg1 = /^a+$/
        console.log(reg1.test(''));      // false
        console.log(reg1.test('a'));     // true
        console.log(reg1.test('aa'));    // true
        console.log('-----------------');
    
        // ? 相当于 1 || 0
        let reg2 = /^a?$/
        console.log(reg2.test(''));      // true
        console.log(reg2.test('a'));     // true
        console.log(reg2.test('aaaa'));    // false
        console.log('-------------');
    
        // {n}重复n次
        let reg3 = /^a{3}$/
        console.log(reg3.test(''));      // false
        console.log(reg3.test('a'));     // false
        console.log(reg3.test('aaaa'));    // false
        console.log(reg3.test('aaa'));    // true
        console.log('----------------');
    
        // {n,}大于等于n次
        let reg4 = /^a{3,}$/
        console.log(reg4.test(''));      // false
        console.log(reg4.test('a'));     // false
        console.log(reg4.test('aaaa'));    // true
        console.log(reg4.test('aaa'));    // true
        console.log('--------');
    
        // {n,m}大于等于 n 次,小于等于 m 次
        let reg5 = /^a{3,5}$/
        console.log(reg5.test(''));      // false
        console.log(reg5.test('a'));     // false
        console.log(reg5.test('aaaa'));    // true
        console.log(reg5.test('aaa'));    // true
        console.log(reg5.test('aaaaa'));    // true
        console.log(reg5.test('aaaaaaa'));    // false
    

    用量词符实现简单的表单验证

    	<input type="text">
        <button>提交button>
        <div id="warn">div>
    
    	let inputVal = document.getElementsByTagName("input");
        let btn = document.getElementsByTagName("button");
        let warn = document.getElementById('warn');
        let reg = /^[a-zA-z0-9-_]{6,16}$/;
        console.log(inputVal);
        inputVal[0].onblur = function (){
            // console.log(reg.test(this.value));
            warn.innerHTML = '';
            if (reg.test(this.value)){
                inputVal[0].style.borderColor = 'aquamarine';
            } else {
                warn.innerHTML = '请输入正确的用户名';
            }
        }
    
    11.3.3 正则表达式的预定义类

    在这里插入图片描述

    11.3.4 正则替换

    1.replace正则替换

    	let str = 'andy';
        let newstr = str.replace(/n/, '*');
        console.log(newstr);	// a*dy
    

    2.正则表达式参数:

    /表达式/[switch]
    switch参数:
    g:全局匹配
    i:忽略大小写
    gi:全局匹配+忽略大小写
    

    12、变量提升与函数提升

    12.1、变量提升

    ​ 通过var声明的变量在定义语句之间就可以访问到,值为undefined

    12.2、函数提升
    • 通过function声明的函数,在声明之前就可以调用,值:函数定义(对象)
    • 块级作用域内的函数,会在声明语句执行过后,被映射到全局作用域。
    • 在块级作用域中定义的函数,会被提升到块级作用域的头部。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rsnuNGTp-1663658730051)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20220920094733229.png)]

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

    13.1、执行上下文
    13.1.1、全局执行上下文
    1. 在执行全局代码前将window确定为全局执行上下文
    2. 对全局数据进行预处理
      • var定义的全局变量===>undefined,添加为window的属性
      • function声明的全局函数===>赋值,添加为window的方法
      • this ===> 赋值 window
    3. 开始执行全局代码
    13.1.2、函数执行上下文
    1. 在调用函数、准备执行函数体之前,创建对应的函数执行上下文对象
    2. 对局部数据进行预处理
      • 形参变量 ===> 赋值(实参),添加为执行上下文的属性
      • arguments ===> 赋值(实参列表),添加为执行上下文的属性
      • var 定义的局部变量 ===> undefined,添加为执行上下文的属性
      • function声明的函数 ===> 赋值(fun),添加为执行上下文的属性
      • this ===> 赋值(调用函数的对象),
    3. 开始执行函数体代码

    函数执行上下文只在调用的时候产生,不调用不产生

    13.2、执行上下文栈
    1. 在全局代码执行前,JS引擎就会创建一个栈来存储所有的执行上下文对象
    2. 在全局执行上下文(window)确定后,将其放入栈中(压栈)
    3. 在函数执行上下文创建后,压入栈中
    4. 在当前函数执行完毕后,出栈
    5. 当所有的代码执行完后,栈中只剩下window

    14、作用域与作用域链

    14.1、理解
    • 作用域相当于一块地盘
    • 相对于上下文对象是静态的,在编写代码时就确定了
    14.2、分类
    • 全局作用域
    • 局部作用域
    • 块作用域(ES6新增)
    14.3、作用

    隔离变量:不同作用域的同名变量不会冲突

    15、作用域与执行上下文

    区别1
    • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义的时候就确定了,而不是在函数调用时
    • 全局执行上下文环境是在全局作用域确定后,js代码马上执行之前创建
    • 函数执行上下文是在调用函数时,函数体执行代码之前创建
    区别2
    • 作用域是静态的,只要函数定义好了就一直存在,且不会再变化
    • 上下文环境是动态的,调用函数时创建,函数调用完毕上下文环境就会被释放
    联系
    • 上下文环境从属于所在的作用域
    • 全局上下文对象=>全局作用域
    • 函数上下文环境=>对应的函数作用域
  • 相关阅读:
    【SpringMVC】web.xml和spring_mvc.xml文件配置
    rust的排序
    10 【异步组件 组合式函数(hooks)】
    【科学文献计量】利用python将中国知网文献导出的文件整理为文献引用格式+摘要正文,并写入到Word中
    解锁数据资产的无限潜能:深入探索创新的数据分析技术,挖掘其在实际应用场景中的广阔价值,助力企业发掘数据背后的深层信息,实现业务的持续增长与创新
    JavaScript大作业:基于HTML实现紫色化妆品包装设计公司企业网站
    Ubuntu 释放nvidia显存
    前端面试经典题--页面布局
    【IC刷卡数据专题】IC刷卡数据分析的技术要点
    解决Pyinstaller打包文件太大的办法(绝对有效,亲测!!!)
  • 原文地址:https://blog.csdn.net/weixin_43889695/article/details/126954185