Tracy JS 小笔记 - Call/apply,This, 继承模式,命名空间,对象枚举

call/apply: 改变 this 指向

  • call 和 apply 的作用是 改变 this 指向
    区别就是传参列表不同
  • Call 的两种用法: 任何一个方法都可以 .call()

    • 执行函数

      function test(){}
      test();//执行函数
      test.call();//执行函数的本来面目,和上面 test(); 是一模一样的
    • 改变函数里 this 指向

      function Person(name, age){
      this.name = name;
      this.age = age;
      }
      var person = new Person('deng', 10);
      var obj = {}

      Person.call(obj, 'cheng', 300); //这个 call 改变得东西就多了
      • 它把 Person 里的 this 指向了 obj
      • 注意: Person 里的 this 在没有一个对象来 new 的时候,是指向 Window 也就是说 Person 只当一个普通方法来调用的时候 this 是没有固定指向的
        当新建了一个对象的时候 var person = new Person('deng', 10); 对于这个具体的对象来说 this 指向了它自己,隐性生成 this{__prpto__:Person.prototype}
      • 后面的(, 'cheng', 300) 是传参, 此时输出 obj 结果为 {name:cheng, age: 300}
      • Person.call(obj)这个方法相当于 把 obj 传到了 Person 方法里了,方法变成了
        function Person(name, age){
        //this == obj
        obj.name = name;
        obj.age = age;
        }
      • 相当于 obj 临时借用 Person 的方法,实现了自己的功能
      • 它就是在执行函数这一次的时候改变了指向,执行完了 Person 里的 this 该指向 window 还指向 window

    企业级开发的实际应用,借用别人的函数实现自己的功能

    call 改变 this 指向,借用人家的方法的一个实际应用, 比如其他人写了一个类, 你的类和他的一模一样,就是多了一些功能而已,那么你就可以不用重复的去写了,直接 call 借用人家的方法就行了
    function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    }

    function Student(name,age,sex,grade,call){
    Person.call(this, name, age, sex);//借用人家的方法 让人家方法里的 this 指向我们的 this, 想当与我们的 this 被加上了 name, age, sex 属性方法之类的
    this.grade = grade;
    this.call = call;
    }

    var student1 = new Student("Tracy", 10, "girl", 2, "1234");

  • Apply

    • 和 call 改变 this 指向的同样的作用
    • 但是不像 call, call 了之后实参可以一个一个传进去, apply 只能传一个实参,并且这个实参必须是数组的形式

    function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    }

    function Student(name,age,sex,grade,call){
    Person.apply(this, [name, age, sex]);//注意这里后边的一个参数是一个数组,和 call 的唯一区别
    this.grade = grade;
    this.call = call;
    }

    var student1 = new Student("Tracy", 10, "girl", 2, "1234");

  • This 4 条知识点

    • 函数预编译过程 this -> windows (GO 对象)
      func() 里的 this 指向 window; obj.func() 里的 this 指向 obj
      function test (c)
      {
      var a = 123;
      function b(){}
      }
      test(1);

      预编译
      AO{
      this:window,
      a:undefined;
      c:1;
      arguments : [1];//注意,我们函数里的参数 arguments 预编译的时候,也就存在了
      b:function(){}
      }


      情形 2

      function test (c)
      {
      //new 的时候 隐式改变 this 指向,但是预编译的时候还仍然指向 window
      //var this = Object.creat(test.prototype);
      //相当于 this = {__proto__:test.prototype}
      var a = 123;
      function b(){}
      //return this;
      }

      var obj = new test(1);

      预编译
      AO{
      this:window,
      a:undefined;
      c:1;
      arguments : [1];//注意,我们函数里的参数 arguments 预编译的时候,也就存在了
      b:function(){}
      }
    • 全局作用域里 this -> windows
      控制台 直接打印 this 显示出 window 对象
    • call/apply 可以改变"函数运行时" this 指向
    • obj.func(); 里面的 this 指向 obj;
      func() 里的 this 指向 window 因为没有人调用这个方法的时候相当于 window.func();
      aa.action(); // action 方法里面的this : 谁调用的这个方法,this 就指向谁
      因此 Demo:

      Person.prototype = {
      name : "a",
      sayName : function(){console.log(this.name);}
      }

      function Person(){
      this.name = "b";
      }

      var person1 = new Person();
      person1.sayName(); //值为 b, this 值向的是 person1
    • 经典题
                                       
      var name = "222";
      var a = {
          name : "111",
          say: function(){
              console.log(this.name);
          }
      }
      
      var fun = a.say; //相当于函数体,并没有执行函数
      fun(); // 222 相当于把 function say() 直接拿到全局来执行, 没有对象调用 say, 此时 this 指向 windows;
      a.say(); //111 这时是 a 对象在调用 say();
      
      var b = {
          name:"333",
          say: function(fun){
              fun();
          }
      }
      b.say(a.say); //222,b.say 调用这个方法的时候 b 里的 this 确实指向了 b
      //但是, 注意了 b.say() 里面调用的 是 fun(),这个 fun() 是走的全局的预编译环节,因为没有 对象调用这个 fun(), 比如说 obj.fun() 
      b.say = a.say;
      b.say(); //333, this 仍然指向 b
                                      
       
      var obj = {
      
      info:"abc",
      
      action: function(){
      var self = this;
          console.log("outside " + this.info);
          console.log("outside " + self.info);
      
          (function(){
          console.log("inside this " + this.info);
          console.log("inside self " + self.info);
          })();
      }
      
      }
      
      obj.action();
      
      //最终结果
      
      outside abc //action 函数有对象来调用,因此它里面的 this 是指向对象 obj 的
      outside abc //自己的 AO 对象里有 self 值
      inside this undefined //立即执行函数 因为没有对象调用,所以 this 是指向 windows 的,走的是预编译路线
      inside self abc   //立即执行函数里的 self 在自己本身的 AO 里找不到值,就会去上一层 action 里的 AO 找,因此它有值 
      

继承模式

  • 传统模式 : 原型链 prototype

    缺点: 过多的继承了没有用的属性

    Grand.prototype.lastName = "Deng";
    function Grand(){}

    var grand1 = new Grand();

    Father.prototype = grand1;
    function Father(){}

    var father1 = new Father();

    Son.prototype = father1;
    function Son(){}

    var son1 = new Son(); // 这个 son1 可以有它上面的链儿上的所有属性, 原型链的链接点就是 prototype

  • 借用构造函数 Call, Apply
    缺点: 只能用人家构造函数里已存在的方法,不能继承借用够着函数的原型
    每次构造函数都要多走一个函数
    这个继承,只限定在别人的函数完全涵盖你的方法,并且你只需要比别人多一些方法或属性而已

    function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
    }

    function Student(name,age,sex,grade,call){
    Person.apply(this, [name, age, sex]); //注意这里后边的一个参数是一个数组,和 call 的唯一区别
    Person.call(this, name, age, sex);
    this.grade = grade;
    this.call = call;
    }

    var student1 = new Student("Tracy", 10, "girl", 2, "1234");

  • 共享原型
    缺点: 不能随便改动自己的原型

    Father.prototype.lastName = "Deng"
    function Father(){}

    Son.prototype = Father.prototype; //此时 Father 和 Son 共享一个原型,但是这样 Son 想给自己的原型加东西, Father 也会跟着变,因为对象指向了同一个地方。
    function Son(){}

    var son1 = new Son();
    // 这个 son1 可以有 Father.prototype 所有属性, 但是又不继承 Father 里自带的属性方法
  • 共享原型 圣杯模式 重点,背下来

    它现在就是继承的一个标准模式了,
    那么为了解决共享原型出现的弊端,想要Son 继承 Father 的原型,但是Son 自己的原型还需要有自己独特的属性和方法
                                     
        Father.prototype.lastName = "Deng"
        function Father(){}
        function Son(){}       
    
        function inherit(Target, Origin){
             function Middle(); //加了这么一个中间商
             Middle.prototype = Origin.prototype;
             Target.prototype = new Middle();
             Target.prototype.constructor = Target;  //把Target的构造函数指向归位
             Target.prototype.uber= Origin.prototype; //为了让我们知道Target真正继承自谁
         }
    
         inherit(Son, Father);
    
         var son1 = new Son();  
                                    
                                
    • Target.prototype = new Middle(); //相当于 var middle = new Middle(); Target.prototype = middle; 这样 Son 的原型,只是和中间商的一个对象 middle 发生了联系,而并不会影响 Father.prototype.
    • Target.prototype.constructor = Target; //把Target的构造函数指向归位, 让 Son 的 constuctor 指向正确的 自己,否则它会根据上一条语句而指向 middle.constructor 进而指向 Father.prototype.constructor 这条也不是必须的,就是为了继承的更加完美
    • Target.prototype.uber= Origin.prototype; //为了让我们知道Target真正继承自谁, 这个可写可不写,这个只是以防将来调用,如果有一天你想知道自己真正继承自哪个函数,就自定义了一个 uber 的属性来方便调用
    • var son1 = new Son(); // 这个 son1 可以有 Father.prototype 所有属性, 但是又不继承 Father 里自带的属性方法; Son.prototype.sex = "male"; //这样随便给 Son 加属性也不会影响其他人


    圣杯模式更专业的写法: 立即实行函数,将中间函数 F 在闭包里变量私有化
       var inherit = (function(){
                var F =function (){} ;
                return function(Target,Origin){
                    F.prototype = Origin.prototype;
                    Target.prototype = new F();
                    Target.prototype.constructor =  Target;
                    Target.prototype.uber = Origin.prototype;
                }
            }())
    
        inherit(Son,Father);//调用

命名空间

  • 解决 JS 命名冲突
  • 利用闭包进行 模块化开发,防止污染全局变量
    公司合作开发常用
    
        var name = "abc";
        var initLiu = (function(){ //最开始的入口函数,初始化的函数,我们叫作 init
            var name = "bcd"; //这里的 name 是私有的,不会污染全局变量,并且外部调用不到它
            function callName(){####}
    
            return fuction(){ //这个函数被保存到了外面
            callName(); 
            }
        }());
    
        var initDeng = (function(){ 
            var name = "bcd"; 
            function callName(){####}
    
            return fuction(){ 
            callName(); 
            }
        }());

对象枚举

  • 链式调用 模仿 jQuery
    
    function deng = {
        smoke: function() {
            console.log('smoking');
            // 如果不加return 默认 return undefined
            return this;
        },
        drink: function() {
            console.log('drinking');
            return this;
        }
    }
     
    deng.smoke().drink().smoke();
                        
  • 属性拼接
    
    var obj = {
        name1: 'xiaozhang',
        name2: 'xiaoliu',
        name3: 'xiaowang'
        sayName: function(num) {
            return this['name' + num];
        }
    }
     
    obj.sayName(1); // 'xiaozhang'
    //利用的是  obj.name1 == obj['name1']
    
                        
  • 枚举: 遍历 hasOwnProperty instanceof

    hasOwnProperty 判断该值是否自己的属性或者方法,不要原型中的属性和方法 经常和 for in 搭配

    判断数据类型
    aa 是不是数组, aa 是不是对象,返回 boolen 值
    aa instanceof Array
    aa instanceof Object

    
    // 1. for 
    var arr = [1, 2, 3, 4, 5, 6];
    for(var i = 0; i < arr.length; i++) {
        console.log(arr[i]);
    }
     
    // 2. for in // 默认会访问到原型 系统自带的原型不会打印出来 手动设置的会打印出来
    var obj = {
        name: 'xiaozhang',
        age: 18,
        sex: 'male',
        height: 170,
        weight: 75,
        prop: 1,
        __proto__: { //for in 只会打印这里我们自己手动设置的原型,系统自带的不会打印的
            lastName: 'wang'
        }
    }
    obj.prototype.abc = 123; //这里也三手动自己设置的原型
     
    for(var prop in obj) { //for in 循环,专门遍历对象属性的
        console.log(prop + ' ' + typeof prop);
        console.log(obj[prop]);
        // console.log(obj.prop); 这么写输出 undefined,因为它相当于 obj['prop'] 访问的是 obj 里的 prop 属性 
    }
     
    // hasOwnProperty 判断该值是否自己的属性或者方法,不要原型中的属性和方法
    for(var prop in obj) {
        if(obj.hasOwnProperty(prop)) { // filter
            console.log(obj[prop]); // 不会打印出原型的属性
        }
    ]
     
    // 3. in 运算符
    // 返回 Boolean 判断能不能找到该属性 一般不用,就算是你原型上的属性,也认为你自己也有该属性
    var obj = {
        name: 'zhangsan',
        age: 20
    }
     
    console.log('name' in obj); // true, 注意 name 外边要有引号
     
    // instanceof 操作符
    // A instanceof B  
    // 它的本意是看 A 对象是不是 B 构造函数构造出来的 很常用
    // 根本的意思是: 看 A 对象的原型链上 有没有 B 的原型
    function Person() {}
    var obj = {}
    var person = new Person();
    console.log(person instanceof Person); // true
    console.log(person instanceof Object); // true
    console.log(obj instanceof Person ); // false
    console.log([] instanceof Object); // true 
    console.log([] instanceof Array); // true 空数组是数组构造出来的
    
    
                        
  • Demo 查看传来的一个变量是数组还是对象
    
        var obj = "外部会传来一个值,也许是数组,也许是对象" // {} || []
    
        function checkObj(value)
        {
            if (value instanceof Array) { return "数据是数组";}
            else if (value instanceof Object) { return "数据是对象";}
            return "数据既不是数组也不是对象";
    
            //也可以 利用 toString 方法
            // Object.prototype.toString.call(value);
    
       }

具体 this 指向可以查看 coderwhy 老师的文章:  https://mp.weixin.qq.com/s/hYm0JgBI25grNG_2sCRlTA 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值