Web前端-JS高级闭包 寄生式创建对象 改变this指向方法,立即执行函数,缓存,沙箱

闭包

闭包从字面意思理解就是闭合, 包起来.
        所谓闭包就是一个闭合包起来的空间

        在js中, 根据作用域规则,
        只允许函数访问外部的数据, 外部无法访问函数内部的数据,


        闭包要解决什么问题? 既然js一个函数就是一个闭包 我们为什么要学习闭包?
            闭包内的数据不允许外界访问
                要解决的问题就是间接访问该数据

            我们要学习的就是如何间接的在外部访问内部数据   这个是有弊端的 要慎用

            闭包的弊端:  打破了封装

            容易产生内存泄漏(内存溢出)

闭包的使用步骤:
                在下级作用域  利用return 返回一个内部函数
                该函数内部的代码 对外暴露 下级作用域的变量
                外部接收到该函数  调用该函数 就接收到了 这个函数对外暴露的变量
                这个变量也就是下级作用域的变量

                从而实现了闭包  间接的访问内部数据


   function wai() {
       var obj={
           name:"ps4游戏机",
           age:1
       };
       function show() {

           return obj;
       }

       return show;
   }

   var show=wai();
   var o1=show();
   var o2=show();
   var o3=show();

   console.log(o1==o2);

闭包返回多条数据

function f() {
        var obj={
            name:"小强",
            age:20
        }
        var per={
            name:"茄子",
            age:1
        }
        return {
            obj_show:function () {
                return obj;
            },
            per_show:function () {
                return per;
            }
        }
    }

    var show=f();
    var obj=show.obj_show();
    console.log(obj);
    var per=show.per_show();
    console.log(per);

闭包在开放中常用

  //用闭包构成一个完整的\存储空间  对外暴露 指定的操作方法 没暴露的 不能用

    function func() {
        var data={
            name:"默认值",
            age:0

        }

        return{
            setName:function(name){
                data.name=name;

            },
            getName:function () {
                return data.name;

            },
            setAge:function (age) {
                data.age=age;

            },
            getAge:function () {
                return data.age;
            },
            addAttr:function (attrName,attrValue) {
                data[attrName]=attrValue;
                return data[attrName];
            }


        }

    }

//    这个data只能调用func一次  data只能有一个 然后反复调用data里面的方法就好
   /* var control=func();
    console.log(control.getName());
    control.setName("小砌墙")
    console.log(control.getName());
    
    console.log(control.addAttr("haha","吼吼"));*/

 寄生式创建对象

构造函数的return语句补充说明:
    *       构造函数中不需要return, 就会默认的return this

            如果手动的添加return, 就相当于 return this

            如果手动的添加return 基本类型; 无效, 还是保留原来 返回this

            如果手动添加return null; 或return undefiend, 无效

            如果手动添加return 对象类型; 那么原来创建的this就会被丢掉, 返回的是 return后面的对象

寄生式创建对象

 function Person(name, age, gender){
//        这个o就是寄生式对象
        var o = new Object();
        o.name = name;
        o.age = age;
        o.gender = gender;
        return o;
    }

//    var o=Person("小砌墙",16,"男");  //把Person函数 当做工厂函数调用

//用new 就是寄生创造函数
    var per=new Person("小砌墙",16,"男");
    console.log(per instanceof Person);//false
    console.log(per instanceof Object);//true
    
    console.log(per.name);
    console.log(per.gender);

函数调用四种方式  第四种上下文调用 改变this指向

 1.函数模式
            函数名();
            this在函数中表示全局对象,在浏览器中是window对象

        2.方法模式
            对象.方法名();
                通过对象调用就是方法模式

              这里面的this就是当前调用方法的对象

        3.构造函数调用模式
            var per=new Person();
            this指向的是当前new的这个对象

        4.函数的第四种调用模式:
            上下文调用  可以改变函数内部的this指向问题

          有两个方法  call和apply

          函数名.apply(this将要指向的对象,[实参1,实参2,...实参n])
            表示 调用这个函数 并把这个函数内部的this指向apply方法的第一个参数对象

 function show(a,b) {
        console.log(this,a+b);
        console.log(this.num1+this.num2)
    }
    var o={
       num1:1,
        num2:2
    };
    show.apply(o,[1,3])

           函数名.call(this将要指向的对象, 实参1,实参2...实参n)

  function show(a,b) {
        console.log(this,a+b);
        console.log(this.num1+this.num2)
    }
    var o={
       num1:3,
        num2:2
    };
  
    show.call(o,1,5)

  bind 是预处理this,不会直接调用

 var 新函数名=函数名.bind(this将要指向的对象,参数1,参数2......);

     新函数名();

或者

 var 新函数名=函数名.bind(this将要指向的对象)

  新函数名(,参数1,参数2......);

    function show(a,b) {
        console.log(this,a+b);
        console.log(this.num1+this.num2)
    }
    var o={
       num1:3,
        num2:2
    };
//第一种
  /*  var newShow=show.bind(o,1,2);
        newShow();*/
//第二种
    var newShow=show.bind(o);
        newShow(1,2)

 call和apply的区别:
            共同作用是能够调用函数 并改变函数内部的this默认指向问题
            区别在于 apply是用数组匹配实参
                call是用 多个参数匹配实参

           bind不会直接调用函数  是预处理this 参数可以再预处理的时候填写 ,也可以在调用的时候填写

 

  如果传入的是一个对象, 那么就相当于设置该函数中的 this 为参数(看上边代码)

如果不传入参数, 或传入 null. undefiend 等, 那么相当于 this 默认为 window    NaN为Number

//例子1
 var o={
        name:"抢到"
    }
    function show() {
        console.log(this);
        
    }
    show.apply(null); //函数内部的this指向window

//例子2
var per={
    name:"我是小砌墙",
    age:16,
    show:function () {
        console.log("我是show方法--->"+this.name,this);
    }
}

var o={
    name:"强盗",
    age:88
}
per.show.apply(o)
per.show.apply(null);; //this变成window

如果传入的是基本类型, 那么 this 就是基本类型对应的包装类型的引用

function f() {
    console.log(this)
}
f.apply(99)//Number

 

上下文调用模式的应用---------------------------------

 1. 将伪数组转换成真正的数组(传统做法)

var a={};
a[0]="a";
a[1]="b";
a[2]="c";
a.length="3";
a.name="xx";

// 传统方法
/*var arr=[];
for (var k in a){
    if(Number(k)||Number(k)==0){
        arr.push(a[k]);
    }
}
console.log(arr);*/

//用上下文调用模式 去借函数 转换伪数组
//    利用apply实现 伪数组转换成 真数组
    var newAr=Array.prototype.concat.apply([],a);
    console.log(newAr)

2.

function Person ( name, age, gender ) {
      this.name = name;
      this.age = age;
      this.gender = gender;
  }
  
  
  function Student(name,age,gender,skill) {
      //调用Person方法 把Person方法里面的this指向当前  Student的this 也就是Student对象
      Person.call(this,name,age,gender);
      this.skill=skill;
      
  }

  var stu=new Student("小书包",16,"男","LOL");
  console.log(stu);

立即执行函数(IIFE)

(Immediately Invoked Function Expression)

  函数本身没有名字  执行到此处代码 函数就会调用运行
            运行过后 函数自动别销毁 有点类似于 一次性函数的感觉

            优点:
                执行一次就自动销毁
                没有名字  不空占内存空间

            立即执行函数的格式:

         第一种(不常用):
            +function(形参){

            }(实参);

+function (a,b) {
       console.log("我是立即执行函数");
    }(88,66)


        第二种:
            (function(形参){


            }(实参))

(function (a) {
        console.log("我是立即执行函数"+a);

    }(80))

        第三种格式(最常用)
        (function(形参){

        })(实参)

(function (a) {
        console.log("我是立即执行函数"+a);
    })(99)

实际开发中

(function (document,window,$) {

    })(document,window,jQuery)

立即执行解决异步函数数据丢失

    for(var i=0;i<10;i++){
        var f=(function (num) {
            return function () {
                console.log(num);
            }
        })(i)
        setTimeout(f,0)
    }

缓存

 什么是缓存:
            能够提高访问速度的一种存储方式

            缓存的目的:就是为了提高访问速度


            为什么要用缓存:
                因为可以提高访问速度


          如果我们每次访问 都从数据来源处要数据  那么 速度相对很慢


        我们学习过的缓存功能:
            cookie 专门用来做缓存的

            session和local是偏向一些临时数据车存储于传递的

 function data() {
        var cache={
            
        }
//        每一个key同时在数组里面存储一份就可以了
        var arr=[];
        return function (key,value) {
            if(value){
//                有value的时候必然有key
                //表示新增或者修改
                //判断是否是新增或者修改
                if(arr.indexOf(key)!=-1 && cache[key]!=undefined){
//                    修改
                    cache[key]=value; //改成新的值
//                    数组得更新一下 把新改的这个key变成数组最后一位
                    arr.push(arr.splice(arr.indexOf(key),1));
                }else{
//                    新增
//                    新增之前 先判断 缓存满没满
                    if(arr.length>2){
//                        表示已经到3个了 需要删除第一个 然后再存新的
/*//                        删除数组中这第一个key
                       var firstKey= arr.shift()
//                        再拿这第一个key 去缓存对象中是删除这个属性
                        delete cache[firstKey];*/
//                       简化写法:
                        delete cache[arr.shift()];
                    }
//                    存储新的
                    arr.push(key);
                    cache[key]=value;
                }

                return cache[key];

            }else{

                return cache[key];

            }
       
        }
    }

    var cache=data();

    console.log(cache("name","小砌墙"));
    console.log(cache("age",16));
    console.log(cache("location","文化大厦"));

 用delete关键字删除一个对象属性

        注意:  无法删除 该对象的原型属性
                只能删除属于对象本身的属性

可以删除隐式全局变量,但不可已删除显示全局变量。 
全局变量其实是global对象(window)的属性

  var per={
        name:"小砌墙",
        age:16,
        location:"文化大厦"
    }

    per.__proto__.color="黄色";
    
//    console.log(per.location);
    console.log(Object.keys(per));

    console.log(per.color);


    //    返回值 布尔值 表示是否删除成功
    console.log(delete per.location);
    console.log(delete per.color); //无法删除
//    console.log(per.location);

沙箱

什么是沙箱?
               火车站或者飞机场的防爆沙箱
               360的隔离沙箱


            这些沙箱都是起到 隔离作用

            能够把一些指定的程序 封闭到一个箱子里面运行 不会影响到外面
            这就是沙箱的作用


            那么我们由这个 可以联想到 之前的闭包  闭合起来的空间
            但是不够   因为闭包不会自动运行
            我们需要一个闭合空间  不对外界直接操作

            这就是 jQuery的封装原理 只对外暴露 $和jQuery  不直接操作外部数据

        jQuery的沙箱模式就是 立即执行函数+闭包

(function (window,document) {
//        沙箱模式中 潜规则: 所有的变量声明 都要放到沙箱模式的第一行
        var data="";
        var cache="";

        var itszt={

            setStyle:{


            },
            setDom:{


            },
            getStyle:{


            },
            event:{

            }



        }




        window.$=window.jQuery=itszt;

    })(window,document)

callee   caller

callee是函数中arguments对象的一个属性
            返回当前函数本身  也是代表当前函数 可以直接调用的
            一旦在函数中 用callee调用  那就变成了  递归(函数自己调用自己)
            递归要小心使用 必须有结束条件 否则就报错 调用死循环

        caller: 是当前函数名调用caller
            如果当前函数的调用 放在了全局
                那么  函数名.caller返回null

            如果当前函数的调用 放到了 另一个函数里面
                那么函数名.caller返回当前调用代码的外层函数本身
                可以理解为一种上下文函数环境的获取

       总结:
              当前方法在哪调用 就返回当前代码所在的外部函数
              如果外部没有函数 返回null

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>


<script>

  /*  function show() {
        console.log("我是show函数");
        console.log(arguments.callee);
        console.log(arguments.callee==show);//true

    }

    show();*/
    function demo() {
        console.log(demo.caller);
    }


    function test() {

       function qq() {
           demo();
       }
      qq();
    }

  test();

//    demo();
</script>
</body>
</html>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值