JavaScript高级——函数进阶

函数

函数4种调用方式

  • .函数三种执行方式:普通函数 、对象方法 、构造函数
    • 共同点:this的指向无法修改,是固定的(一旦修改程序报错)
      • 普通函数:this指向window(fun();
      • 对象方法:this 指向对象(obj.fun();)
      • 构造函数:this指向new创建的对象(new Person();)
  • 函数第四种执行方式:上下文模式
    • 作用: 修改函数里面的this
    • 注意点 : this只能指向引用类型(array function object)。如果指向基本数据类型
      • 基本包装类型string number boolean : 自动转成对应的对象类型 new String() new Number() new Boolean()
      • undefined与null : 修改无效,this指向window
      • 语法: 三个语法作用一致都是修改this,只是传参方式不同
        • 函数名.call(修改后的this,形参1,形参2)
          场景:用于函数原本形参 <= 1
          fn.call({name:‘张三’},10,20);
        • 函数名.apply(修改后的this,数组或伪数组)
          场景:用于函数原本形参 >= 2
          fn.apply({name:‘李四’},[10,20]);
        • 函数名.bind(修改后的this,形参1,形参2)(如果这里给了形参将永远是该形参,调用赋值无效)
          let newFn = fn.bind({name:‘王五’});
          newFn(66,88);//this指向修改后的对象

          不会立即执行函数体,而是返回一个修改this之后的新函数
          场景: 用于这个函数不需要立即执行,而是过一会儿执行(事件处理函数,定时器函数)

伪数组 : 有数组三要素(下标,元素,长度),但是不能调用数组的API
伪数组本质是一个对象 : (属性名恰好就是下标, 但是对象原型不是指向Array.prototype,所以无法调用数字方法)
越过原型链查找访问机制
在这里插入图片描述
call应用(伪数组排序)

	<script>
        /* 伪数组排序 */
        let weiArr = {
            0:88,
            1:50,
            2:99,
            3:20,
            4:35,
            length:5
        };
        //1.第一种方式: 先把伪数组转成真数组,然后调用sort
        // let arr = Array.prototype.slice.call(weiArr);
        // arr.sort(function(a,b){return a-b});
        // console.log(arr);

        //2.  Array.prototype.sort.call(weiArr, function(a,b){return a-b});
        //思路:越过原型链查找机制,直接调用数组原型的sort,并且把this修改为伪数组
        Array.prototype.sort.call(weiArr,function(a,b){return a-b});
        console.log(weiArr);
    </script>

apply应用(求数组最大值–擂台思想)

	<script>
        /* 求数组最大值 */
        let arr = [100,88,90,20,50,66];
        /* 擂台思想 */
        // //1.擂台思想
        // let max = -Infinity;
        // //2.遍历挑战者
        // for(let i = 0;i<arr.length;i++){
        //     //3.依次和擂主PK
        //     if( arr[i] > max ){
        //         max = arr[i]
        //     };
        // };
        // console.log(max);
        /* Math.max */
        // let max1 =  Math.max( arr[0],arr[1],arr[2],arr[3],arr[4]);
        //利用apply自动遍历数组传参的特点(底层与上面代码一致,只是apply会自动遍历数组逐一传参)
        let max1 = Math.max.apply(Math,arr);
        console.log(max1);   
    </script>

bind应用
在js中,定时器中的this默认一定是window (要想修改定时器中的this,只能用bind)


        let test = function(){
            console.log('我是具名函数');
            console.log(this); 
        };
        //具名函数
        setTimeout(test.bind({name:'1111'}),3000);
        //匿名函数
        setTimeout(function(){
            console.log('我是定时器');
            console.log(this);
        }.bind({name:'222'}),3000);

万能检测数据类型

  • typeof关键字检测数据类型 : typeof 数据
    • 弊端:无法检测 null 和 array 这两种数据类型,返回值都是object
    • typeof null : ‘object’
    • typeof []] : ‘object’
  • 对象的toString()方法检测数据类型
    • Object.prototype.toString() : 返回固定格式字符串 [object 数据类型]
    • Array.prototype.toString() : 返回数组,join拼接的字符串.底层原理是调用join()方法
  • 万能检测数据类型
    • 语法:Object.prototype.toString.call(数据)
    • 越过原型链,直接调用Object.prototype里面的toString
      在这里插入图片描述
    <script>  
        //值类型(基本数据类型)
        let str = 'test';
        let num = 18;
        let bol = true;
        let und = undefined;
        let nul = null;

        //引用类型(复杂数据类型)
        let arr = [10,20,30];
        let fn = function(){};
        let obj = {name:'abc'};

        console.log(  obj.toString() );//[object Object]
        console.log( arr.toString() );//[object Array]   
        console.log(  Object.prototype.toString.call(str) );// [object String]
        console.log(  Object.prototype.toString.call(num) );//  [object Number]
        console.log(  Object.prototype.toString.call(bol) );// [object Boolean]
        console.log(  Object.prototype.toString.call(und) );// [object Undefined]
        console.log(  Object.prototype.toString.call(nul) );// [object Null]
        console.log(  Object.prototype.toString.call(arr) );// [object Array]
        console.log(  Object.prototype.toString.call(fn) );// [object Function]
        console.log(  Object.prototype.toString.call(obj) );// [object Object]

        // console.log( typeof str );// 'string'
        // console.log( typeof num );// 'number'
        // console.log( typeof bol );// 'boolean' 
        // console.log( typeof und );// 'undefined'
        // console.log( typeof nul );// 'object'
        // console.log( typeof arr );// 'object'
        // console.log( typeof fn );// 'function'
        // console.log( typeof obj );// 'object'
        
    </script>

递归函数

递归函数 : 一个函数在函数体中调用自己
递归函数作用 :函数体代码重复执行
递归函数做的事和循环一样的,能用递归做的就可以用循环做。只是语法简洁性不同。
递归注意点 :一定要有结束条件,否则会导致死循环

  • 单函数递归
 function fn() {
            fn();
        };
        fn();

  • 双函数递归
 function fn() {
            fn2();
        }
        function fn2() {
            fn1();
        }

案例:

// 写一个函数,打印三次hello
        let i = 0;
        function test() {
            console.log('hello');
            i++
            if (i <= 3) {
                test();
            }
        }
  • 递归遍历document元素
let box = document.querySelector('#box');
        let arr = [];//存储所有的后代元素
        function find(ele){
            for(let i = 0;i<ele.children.length;i++){
                console.log(ele.children[i]);
                arr.push(ele.children[i]);
                //继续递归调用:获取子元素的子元素
                getHouDai(ele.children[i]);
            }
        };
        find(document);
        console.log(arr);

闭包(沟通全局作用域与局部作用域的一座桥梁)

  • js 有两种作用域
    • 全局作用域(全局变量): 在函数外面声明,可以在页面任何地方访问
      • 全局变量生命周期 : 页面打开时声明, 页面关闭时销毁
      • 生命周期: 从变量声明 到 变量销毁
    • 局部作用域(局部变量): 在函数里面声明,只能在函数体里面使用
      • 局部变量生命周期 : 执行函数体开始声明, 函数调用结束销毁
  • 如何实现在函数外部可以访问函数里面的变量呢?
    • 使用return
 function fn(){
            let person = {
                name:'小米',
                age:19
            };
            return person;
        };
        let p1 =  fn();
        console.log(p1);
        let p2 = fn();
        console.log(p2);
        // p1和p2虽然里面的数据相同,但是这是两个不同的对象(地址不同)
        console.log( p1 == p2);//false

在这里插入图片描述

  • 如何实现 在函数外部访问函数里面的变量,并且保证是同一个
    解决方案: 使用闭包
    闭包作用: 在函数外面访问函数里面的变量
    闭包语法: 语法不固定,但是主要分为三个步骤
    (1)在外部函数中声明一个闭包函数
    (2)在闭包函数中返回你想要访问的变量
    (3)返回闭包函数
    闭包本质 : 沟通全局作用域与局部作用域的一座桥梁
	function outer(){
            let person = {
                name:'小米'
            };
            //(1)在外部函数的里面声明一个内部函数(闭包函数)
            function closure(){
                //(2)在闭包函数中返回你想要访问的变量
                return person
            };
            //(3)返回这个闭包函数
            return closure;
        };
        //调用外部函数 得到闭包函数
        let bibao =  outer();
        //调用闭包函数,得到局部变量
        let p1 = bibao();
        let p2 = bibao();
        console.log( p1,p2);
        console.log( p1 == p2);//true

在这里插入图片描述

  • 沙箱模式
    沙箱模式:值得是一个独立的内存空间(局部作用域).通常是一个匿名函数自调用
    • 沙箱模式作用:
      1. 避免全局变量污染
      2. 模块化开发 : 一个功能对应一个沙箱
    • 沙箱注意点 :不要在沙箱内部访问全局变量,应该使用参数传递
      (1)沙箱外面的变量,代码可能会压缩出错
      (2)破坏封装性
 	(function(w){
           let a = {
                eat:function(){
                    console.log('吃饭');
                },
                play:function(){
                    console.log('玩');
                }
           };
           //暴露接口
           w.a = a;
       })(window);

class关键字作用

  • 声明类函数 (相当于以前的构造函数)
    • class语法作用和以前的构造函数语法作用一致,只是写法不同
    1. 将构造函数与原型方法 写在一个大括号中,提高代码阅读性
    2. calss类函数必须要使用new来调用,语法更加规范
    • 语法:
	class 构造函数名{
                //构造函数
                constructor(){
                };
                //原型中的方法
                eat(){
                };
            };

对比:


        //ES5继承 : 原型链

        //(1)构造函数
        function Person(name,age){
            this.name = name;
            this.age = age;
        };

        //(2)原型方法
        Person.prototype.eat = function(){
            console.log('吃饭');
        };

        Person.prototype.learn = function(){
            console.log('学习');
        };

        //(3)实例对象
        let p1 = new Person('ikun',30);

        console.log(p1);

        //ES6继承: class类函数
        class Person1{
            //(1)构造函数:  固定语法 constructor(){}
             constructor(name,age){
                this.name = name;
                this.age = age;
            };

            //(2)原型中的方法
            eat(){
                console.log('吃饭');
            };

            learn(){
                console.log('学习');   
            }
        };

        /* ES6语法的本质其实还是以前的原型语法,只是写法不同.还是可以用以前的语法给原型添加成员 */
        Person1.prototype.type = '人类';

        //(3)实例对象
        //class类函数必须要使用new调用,否则会报错
        let p2 = new Person1('班长',18);
        console.log(p2);
       

extends关键字 : 用于继承

  • 底层原理: 替换原型
    • Student.prototype.__proto__ = Person.prototype
    • s1.__proto__.__proto__ === Person.prototype
/* 父对象 */
        class Person{
            //构造函数
            constructor(name,age){
                this.name = name;
                this.age = age;
            };
            //原型方法
            eat(){
                console.log('人类要吃饭');
            }
        };
        Person.prototype.type = '人类';
        let p1 = new Person('xiuer',18);

        /* 子对象 继承与父对象 
        extends关键字底层原理:  Student.prototype.__proto__ = Person.prototype
        */
        class Student extends Person{
            //构造函数
            // constructor(name,age,score){
            //     this.name = name;
            //     this.age = age;
            //     this.score = score
            // };

            //原型方法
            work(){
                console.log('好好学习天天向上');
            }
        };
        let s1 = new Student('班长',20);
        console.log(s1);

        console.log(s1.type);//人类
        s1.eat();//吃饭

super关键字:在子类函数中调用父类的方法

  • 如果重写了子类的构造函数construc,必须要调用父类的方法super()
/* 父对象 */
        class Person{
            //构造函数
            constructor(name,age){
                this.name = name;
                this.age = age;
            };

            //原型方法
            eat(){
                console.log('人类要吃饭');
            }
        };
        Person.prototype.type = '人类';
        let p1 = new Person('ikun',30);

        /* 子对象 继承与父对象 
        extends关键字底层原理:  Student.prototype.__proto__ = Person.prototype
        */
        class Student extends Person{
            //构造函数
            constructor(name,age,score){
                //在子类构造函数中 去调用父类的构造函数
                /* 细节:如果重写了子类的构造函数construc,必须要调用父类的方法super()
                 */
                super(name,age);
                this.score = score
            };

            //原型方法
            work(){
                /* 调用父类的方法 */
                //super.eat()底层原理:  Person.prototype.eat();
                super.eat();
                console.log('好好学习天天向上');
            }
        };

        let s1 = new Student('班长',20,99);
        console.log(s1);

        s1.work();

        // console.log(s1.type);//人类
        // s1.eat();//吃饭
        

猜你喜欢

转载自blog.csdn.net/weixin_44757417/article/details/107868973
今日推荐