JS进阶(三)(函数进阶,apply,闭包...等)

--------------记录自JS高级视频中的学习笔记包括了函数进阶,apply,call,bind方法的使用,闭包,递归,浅拷贝与深拷贝,沙箱.

函数进阶
  • 创建函数的两种方式:
    • 函数声明:
      function f1(){
          console.log("函数声明");
      }
      f1();
      复制代码
      • 函数声明如果放在if-else语句中,在IE8中会出错(函数声明会被提升)
    • 函数表达式:
      var f2 = function(){
          console.log("函数表达式");
      };
      f2(); 
      复制代码
      • 使用函数表达式更好
  • 函数中的this指向
    • 普通模式
      • 普通函数中的this---window
      • 定时器中的this---window
      • 构造函数中的this---实例对象
      • 对象.方法中的this---当前的实例对象
      • 原型方法中的this---实例对象
    • 严格模式: "use strict"
      • 普通函数中的this得在window.函数();调用的时候才是window,若函数();则为undefined
  • 函数的不同的调用方式
    • 普通函数:
      function f1(){}
      f1();
      复制代码
    • 构造函数---通过new来调用,创建对象
      function F1(){}
      var f = new F1();
      复制代码
    • 对象的方法
      function F1(){
          this.f1 = function(){};
      }
      var f = new F1();
      f.f1(); 
      复制代码
  • 函数也是对象,对象不一定是函数
    • 对象中有__proto__原型,是对象
    • 函数中有prototype原型,是对象
    • 若一个东西里有prototype,又有__proto__,说明是函数也是对象;(Math对象中有__proto__但没有prototype)
    • 所有的函数实际上都是Function的构造函数创建出来的实例对象
  • 函数中的几个成员介绍
    • name属性: 函数的名字,name的属性是只读的,不能修改
    • arguments属性: 实参的个数
    • length属性: 函数定义的时候形参的个数
    • caller属性: 调用者(例如f1函数在f2函数中调用的,此时的调用者就是f2)
  • 函数可以作为参数使用
    • 函数作为参数的时候,如果是命名函数,那么只传入命名函数的名字,没有括号
    • 练习:
        var arr = [1, 100, 20, 200, 40, 50, 120, 10];
        //排序---函数作为参数使用,匿名函数作为sort方法的参数使用,那么此时的匿名函数中有两个参数,
        arr.sort(function (obj1,obj2) {
          if(obj1>obj2){
            return -1;
          }else if(obj1==obj2){
            return 0;
          }else{
            return 1;
          }
        });//从小到大排序
    复制代码
  • 判断数据类型
    • typeof: 返回一个字符串,表示未经计算的操作数的类型
    • 对象 instanceof Object: 返回布尔类型,判断对象是否为某个类型
    • Object.prototype.toString(): 返回Object的数据类型([object Object])
    • Object.prototype.toString().call(对象): 返回的是该对象的数据类型
    • 获取某个对象的类型是不是传入的类型
        //获取某个对象的类型是不是你传入的类型
        //[10,20,30] 是不是"[object Array]"
        //type---是变量----是参数----"[object Array]"
        //obj---是变量-----是参数----[10,20,30];
    
        //判断这个对象和传入的类型是不是同一个类型
        function getFunc(type) {
          return function (obj) {
            return Object.prototype.toString.call(obj) === type;
          }
        }
    
        var ff = getFunc("[object Array]");
        var result = ff([10, 20, 30]);
        console.log(result);//true
    
        var ff1 = getFunc("[object Object]");
        var dt = new Date();
        var result1 = ff1(dt);
        console.log(result1);//false
    复制代码
apply,call与bind的使用
apply,call
  • 作用: 都可以改变this的指向
        function f1(x,y){
            console.log((x + y) + ":===>" + this);
        }
        //apply 和 call调用
        f1.apply(null,[1,2]);//3:===>[object Window]
        f1.call(null,1,2);//3:===>[object Window]
        //改变this的指向
        var obj = {
        
        };
        f1.apply(obj,[1,2]);//3:===>[object Object]
        f1.call(obj,1,2);//3:===>[object Object]
    复制代码
  • apply的使用语法
    • 函数名字.apply(对象,[参数1,参数2,...]);
    • 方法名字.apply(对象,[参数1,参数2,...]);
  • call的使用语法
    • 函数名字.call(对象,参数1,参数2,...);
    • 方法名字.call(对象,参数1,参数2,...);
  • 不同的地方 : 参数传递的方式不一样
  • 只要是想使用别的对象的方法,并且希望这个方法是当前对象的,那么就可以使用apply或者是call的方法改变this的指向
  • apply和call方法实际上并不在函数这个实例对象中,而是在Function的prototype中
bind方法
  • bing方法是复制的意思,参数可以在复制的时候传进去,也可以在复制之后调用的时候传入进去
        function A(age){
            this.age = age;
        }
        A.prototype.a = function(){
            console.log(this + "===>" + this.age);
        }
        function B(age){
            this.age = age;
        }
        var aObj = new A(10);
        var bObj = new B(20);
        //复制了一份
        var c = aObj.a.bind(bObj);
        c();//[object Object]===>20
    复制代码
    • 使用语法:
      • 函数名字.bind(对象,参数1,参数2,...)--->返回值是复制之后的这个函数
      • 方法名字.bind(对象,参数1,参数2,...)--->返回值是复制之后的这个方法
    • 应用实例
          function ShowRandom(){
              //1-10的随机数
              this.number = parseInt(Math.random()*10 + 1);
          }
          //添加原型方法
          ShowRandom.prototype.show1 = function(){
              //改变了定时器中的this的指向了,本来应该是window,现在为实例对象
              window.setInterval(this.show2.bind(this),1000);
          };
          //添加原型方法
          ShowRandom.prototype.show2 = function(){
              //显示随机数--
              console.log(this.number);
          };
          //实例对象
          var sr = new ShowRandom();
          //调用方法,输出随机数字
          //调用这个方法一次,可以不停的产生随机数字
          sr.show1();
      复制代码
作用域和作用域链及预解析
  • 变量--->局部变量和全局变量
    • 局部变量是在函数中,函数使用结束后,局部变量就会被自动的释放,闭包后,里面的局部变量的使用作用域链被自动的延长
  • 作用域:就是变量的作用范围--->局部作用域和全局作用域
    • js中没有块级作用域--一对括号中定义的变量,这个变量可以在大括号外面使用,函数中定义的变量是局部变量
  • 作用域链:变量的使用,从里向外,层层的搜索,搜索到了就可以直接使用了
    • 层层搜索,搜索到0级作用域的时候,如果还是没有找到这个变量,结果就是报错
  • 预解析:就是在浏览器解析代码之前,把变量的声明和函数的声明提前(提升)到该作用域的顶部(函数声明优先于变量声明)
闭包
  • 闭包的概念:函数A中,有一个函数B,函数B中可以访问函数A中定
    • 模式:函数模式的闭包,对象模式的闭包
    • 作用:缓存数据,延长作用域链
    • 优点和缺点:缓存数据(没有及时的释放 )
    • 应用:
          //函数模式的闭包
          function f1(){
              var num = 10;
              //函数的声明
              function f2(){
                  console.log(num);
              }
              //函数调用
              f2();
          }
          f1();//10
              
          //对象模式的闭包:函数中有一个对象
          function f3(){
              var num = 10;
              var obj = {
                  age : num 
              }
              console.log(obj.age);//10
          }
      复制代码
    • 案例:产生三个相同的随机数
          function f1(){
              var num = parseInt(Math.random()*10 + 1);
              return function (){
                  console.log(num);
              }
          }
          
          var ff = f1();
          ff();//3(随机的)
          ff();//3
          ff();//3
      复制代码
    • 总结:如果想要缓存数据,就把这个数据放在外层的函数和里层的函数的中间位
沙箱
  • 沙箱: 环境,黑盒,在一个虚拟的环境中模拟真实世界,做实验,实验结果和真实世界的结果是一样的,但是不会影响真实世界
  • 沙箱--小环境
        (function (){
            var num = 10;
            console.log(num + 10);
        }());
    复制代码
  • 小案例
        (function (){
            var str = "小白喜欢小黑";
            str = str.substr(2);
            console.log(str);//喜欢小黑
        }());
        
        (function (){
            var str = "小明喜欢小红";
            str = str.substr(2);
            console.log(str);//喜欢小红
        }());
    复制代码
  • 总结:放在沙箱内(自调用函数)可以避免全局变量冲突,不会污染外部环境
递归
  • 递归: 函数中调用函数自己,此时就是递归,递归一定要有结束的条件,一般应用在遍历上,递归轻易不要用,效率很低
  • 案例: 求n个数字的累加和
        //递归实现:求n个数字的和
        //函数的声明
        function getSum(x){
            if(x==1){
                return 1;
            }
            return x + getSum(x - 1); 
        }
        //函数的调用
        console.log(getSum(5));//15
        /*
        *
        * 执行过程:
        * 代码执行getSum(5)--->进入函数,此时的x是5,执行的是5+getSum(4),此时代码等待
        * 此时5+getSum(4),代码先不进行计算,先执行getSum(4),进入函数,执行的是4+getSum(3),等待, 先执行的是getSum(3),进入函数,执行3+getSum(2),等待,先执行getSum(2),进入函数,执行 2+getSum(1);等待, 先执行getSum(1),执行的是x==1的判断,return 1,所以,
        * 此时getSum(1)的结果是1,开始向外走出去
        * 2+getSum(1) 此时的结果是:2+1
        * 执行:
        * getSum(2)---->2+1
        * 3+getSum(2) 此时的结果是3+2+1
        * 4+getSum(3) 此时的结果是4+3+2+1
        * 5+getSum(4) 此时的结果是5+4+3+2+1
        *
        * 结果:15
        *
        *
        *
        * */
    复制代码
  • 案例: 求一个数字各个位数上的数字的和: 123 --->6
        function getEverySum(x){
            if(x<10){
                return x;
            }
            //获取的是这个数的个位数
            return x%10 + getEverySum(parseInt(x/10));
        }
        console.log(getEverySum(1364));//14
    复制代码
  • 案例: 求斐波那契数列(第三个数等于前两个数之和 第一第二个数为1)
        function getFib(x){
            if(x==1 || x==2){
                return 1;
            }
            return getFib(x-1) + getFib(x-2);
        }
        console.log(getFib(12));//144
    复制代码
浅拷贝和深拷贝
  • 浅拷贝: 拷贝结束复制,就相当于把一个对象中的所有的内容,复制一份给另一个对象,直接复制,或者说,就是把一个对象的地址给了另一个对象,他们指向相同,两个对象之间有共同的属性或者方法,都可以使用
        //作用:把一个对象的属性复制到另一个对象中,浅拷贝
        //把a对象中的所有的属性复制到对象b中
        function extend(a,b){
            for(var key in a){
                b[key] = a[key];
            }
        }
    复制代码
  • 深拷贝: 拷贝函数复制,深: 把一个对象中的所有属性或者方法,一个一个的找到,并且在另一个对象中开辟相应的空间,一个个的存储到另一个对象中
        //通过函数实现,把对象a中所有的数据深拷贝到对象b中
        function extend(a,b){
            for(var key in a){
                //先获取a对象中每个属性的值
                var item = a[key];
                //判断这个值是不是数组
                if(item instanceof Array){
                    //如果是数组,那么在b对象中添加一个新的属性,并且这个属性值也是数组
                    b[key] = [];
                    //调用这个方法,把a对象中的这个数组的属性值一个个的复制到b对象的这个数组属性中
                    extend(item,b[key]);
                }else if(item instanceof Object){//判断在这个值是不是对象类型的
               //如果是对象类型的,那么在b对象中添加一个属性,是一个空对象
               b[key] = {};
               //再次调用这个函数,把a对象中的属性对象的值一个一个复制到b对象的这个属性对象中
               extend(item,b[key]);
                }else{
                    //如果值是普通的数据,直接复制到b对象整过属性中
                    b[key] = item;
                }
            }
        }
    复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值