《JavaScript 设计模式》阅读笔记之第一章:灵活的js

最近发现了一种新的学习方式(或者写笔记的方式),感觉很方便。

在 html 中记录,主要是方便写 js 代码,然后以注释的方式写知识点。

因为会有很多同名函数和变量,所以测试代码文件另写另测试。

这样可以一边记录,一边有针对性的测试。测试不会打断记录,测试中,测试一个注释一个。后面回顾的时候,只看记录的 html 即可。

 

下面就整理下我读 张容铭 的 《JavaScript 设计模式》的笔记吧,只是第一章的内容。

这本书最大的特点就是从工作实际需求出发吧,不会干讲理论,理论结合实践,也是我喜欢的方式。

 

《JavaScript 设计模式》阅读笔记之第一章:灵活的js

这章主要是以一个简单的登录验证做需求,不断优化代码实现。

优化路径如下:

全局函数=》局部变量函数=》对象=》类=》原型定义=》链式定义和调用

<script>
  //登录的需求
  //1.1 定义函数实现,导致新创建3个全局变量
  function checkName(){
    console.log("验证姓名")
  }

  function checkEmail(){
    console.log("验证邮箱")
  }

  function checkPassword(){
    console.log("验证密码")
  }

  //1.2 函数的另一种形式
  //优化:局部变量代替全局变量(函数也是变量),避免全局同名方法覆盖。
  var checkName=function(){
    console.log("验证姓名")
  }

  var checkEmail=function(){
    console.log("验证邮箱")
  }

  var checkPassword=function(){
    console.log("验证密码")
  }

  //1.3用对象收编变量
  //减少覆盖或被覆盖的风险,一旦被覆盖,所有功能都会失效,出错现象比较明显,方便错误定位。
  var checkObject={
    checkName:function(){
      console.log("验证姓名")
    },

    checkEmail:function(){
      console.log("验证邮箱")
    },

    checkPassword:function(){
      console.log("验证密码")
    }
  }

  //对象使用
  checkObject.checkName()

  //1.4对象的另一种形式
  //函数也是对象,给对象添加方法。下面这种方式 checkObject 在使用 new 关键字创建新对象时,新创建的对象不能继承 checkName 这些方法。即对象不能复用。
  //var newFn=new checkObject()
  //newFn.checkName() //newFn.checkName is not a function

  var checkObject=function(){}

  checkObject.checkName=function(){
    console.log("验证姓名")
  }

  checkObject.checkEmail=function(){
    console.log("验证邮箱")
  }

  checkObject.checkPassword=function(){
    console.log("验证密码")
  }

  //函数使用
  checkObject.checkName()

  //1.5真假对象
  //对象复用 将方法放在一个函数对象中,每次调用返回一个新对象,互不影响。
  var checkObject=function(){
    return{
      checkName:function(val){
        console.log("验证姓名")
        console.log(val)
      },

      checkEmail:function(){
        console.log("验证邮箱")
      },

      checkPassword:function(){
        console.log("验证密码")
      }
    }
  }

  //使用
  var a=checkObject()
  a.checkName(val);

  //1.6类也可以
  // ES 5 没有 class 关键字,所以使用函数来模拟类。生成实例的时候,使用new关键字。类的属性和方法,还可以定义在构造函数的prototype对象之上。

  //构造函数使用全局作用域
  var scope = "global";
  function constructFun(){
      var scope = "local";
      //构造函数可以是匿名的,可以传入任意数量的字符串实参,最后一个实参是函数体。
      return new Function("return scope");//无法捕捉局部作用域
  }

  var rel = constructFun()();
  console.log(rel);//global 全局没有定义scope,则返回scope is not defined

  // ES 6 中的类,使用class关键字,是原型继承的一种语法糖。

  var checkObject=function(){
    this.checkName=function(){
      console.log("验证姓名")
    }

    this.checkEmail=function(){
      console.log("验证邮箱")
    }

    this.checkPassword=function(){
      console.log("验证密码")
    }
  }

  //使用
  //用关键字 new 创建类
  var a= new checkObject()
  a.checkName();

  //1.7 一个检测类
  //通过 this 定义函数内部的方法,使用new新创建的对象都会对类上的this上的属性进行复制,所以新创建的对象都有自己的一套方法。
  //这么做比较消耗,可以优化,将方法绑定在 checkObject 对象的原型上,这样,新创建的对象所拥有的方法就都是一个了,因为它们都依赖 prototype 原型依次寻找,找到的方法都是同一个。
  var checkObject=function(){}

  //写法一

  checkObject.prototype.checkName=function(){
      console.log("验证姓名")
  }

  checkObject.prototype.checkEmail=function(){
      console.log("验证邮箱")
  }

  checkObject.prototype.checkPassword=function(){
      console.log("验证密码")
  }

  //写法优化
  //写法二

  checkObject.prototype={
    checkName:function(){
      console.log("验证姓名")
    },

    checkEmail:function(){
      console.log("验证邮箱")
    },

    checkPassword:function(){
      console.log("验证密码")
    }
  }

  //注意:以上两种方式不能混写
  //如下混写:后定义的 checkObject.prototype 会整个覆盖先定义的 checkObject.prototype
  var checkObject=function(){}

  checkObject.prototype.checkName=function(){
      console.log("验证姓名 先定义")
  }

  checkObject.prototype.checkEmail=function(){
      console.log("验证邮箱 先定义")
  }

  checkObject.prototype={
    checkEmail:function(){
      console.log("验证邮箱 后定义")
    },

    checkPassword:function(){
      console.log("验证密码")
    }
  }

  //测试
  var a =new checkObject();
  a.checkName();//a.checkName is not a function
  a.checkEmail();//验证邮箱 后定义
  a.checkPassword();

  //使用
  var a =new checkObject();
  a.checkName();
  a.checkEmail();
  a.checkPassword();

  //1.8方法还可以这样用
  //以上使用方法,调用了3个方法,对象a书写了3遍。
  //优化:每个方法的末尾,返回当前对象 this ,这样可以使用链式调用。

  //优化对象
  var checkObject={
    checkName:function(){
      console.log("验证姓名")
      return this
    },

    checkEmail:function(){
      console.log("验证邮箱")
      return this
    },

    checkPassword:function(){
      console.log("验证密码")
      return this
    }
  }

  //使用
  checkObject.checkName().checkEmail().checkPassword()

  //优化类
  var checkObject=function(){}

  checkObject.prototype={
    checkName:function(){
      console.log("验证姓名")
      return this
    },

    checkEmail:function(){
      console.log("验证邮箱")
      return this
    },

    checkPassword:function(){
      console.log("验证密码")
      return this
    }
  }

  //使用类 需要先创建一下
  var a =new checkObject();
  a.checkName().checkEmail().checkPassword();

  //1.9函数的祖先
  //prototype.js 
  //扩展原生对象 
  Function.prototype.checkEmail=function(){
    console.log("验证邮箱")
  }

  //使用
  //函数形式
  var f=function(){}
  f.checkEmail();

  //类的形式
  var f=new Function()
  f.checkEmail();

  //这样做会污染原生对象 Function
  //优化:抽象出一个统一添加方法的功能
  Function.prototype.addMethod=function(name,fn){
    this[name]=fn;
  }

  //使用
  var methods=function(){};
  //或
  var methods=new Function();

  methods.addMethod('checkName',function(){console.log("验证姓名")})
  methods.addMethod('checkEmail',function(){console.log("验证邮箱")})

  methods.checkName();
  methods.checkEmail();

  //1.10链式添加和调用
  //函数式调用
  Function.prototype.addMethod=function(name,fn){
    //注意
    this[name]=fn;
    return this;
  }

  //使用 
  var methods=function(){};

  methods.addMethod('checkName',function(){
    console.log("验证姓名")
    return this;
  }).addMethod('checkEmail',function(){
    console.log("验证邮箱")
    return this;
  })

  //函数式调用
  methods.checkName().checkEmail();

  //1.11换一种方式使用方法
  //为了类式调用改造方法
  Function.prototype.addMethod=function(name,fn){
    //注意
    this.prototype[name]=fn;
    return this;
  }

  //使用
  var methods=function(){};

  methods.addMethod('checkName',function(){
    console.log("验证姓名")
    return this;
  }).addMethod('checkEmail',function(){
    console.log("验证邮箱")
    return this;
  })

  //类式调用
  var m=new methods();
  m.checkName().checkEmail();

  //课后作业
  //1、真假对象(1.5)中如何实现方法的链式调用

  var checkObject=function(){
    return{
      checkName:function(){
        console.log("验证姓名")
        return this
      },

      checkEmail:function(){
        console.log("验证邮箱")
        return this
      },

      checkPassword:function(){
        console.log("验证密码")
        return this
      }
    }
  }

  //使用
  var a=checkObject()
  a.checkName().checkEmail().checkPassword();

  //2、定义一个可以为函数添加多个方法的 addMethod 方法

  var arr=[
    {
      name:'checkName',
      fn:function(){
        console.log("验证姓名")
        return this;
      }
    },
    {
      name:'checkEmail',
      fn:function(){
        console.log("验证邮箱")
        return this;
      }
    }
  ];

  Function.prototype.addMethod=function(arr){
    arr.forEach(item => {
      this[item.name]=item.fn;
    });
    return this;
  }

  //使用 
  var methods=function(){};

  methods.addMethod(arr)

  //函数式调用
  methods.checkName().checkEmail();


  //3、定义一个既可以为函数原型添加方法,又可以为其自身添加方法的 addMethod 方法
  var arr=[
    {
      name:'checkName',
      isPrototype:true,
      fn:function(){
        console.log("验证姓名")
        return this;
      }
    },
    {
      name:'checkEmail',
      isPrototype:false,
      fn:function(){
        console.log("验证邮箱")
        return this;
      }
    }
  ];

  //为了方便测试,引入fn2参数,实际使用去掉即可。
  Function.prototype.addMethod=function(arr,fn1,fn2){
    arr.forEach(item => {
      if(item.isPrototype){
        //为函数原型添加方法
        this[item.name]=item.fn;
      }else{
        //为其自身添加方法
        fn1[item.name]=item.fn;
      }
    })
  }

  //使用 
  var fn1=function(){};
  var fn2=function(){};

  fn1.addMethod(arr,fn1,fn2);
  fn2.addMethod(arr,fn1,fn2);

  //函数式调用
  fn1.checkName().checkEmail();
  fn2.checkName();
  fn2.checkEmail();//fn2.checkEmail is not a function
</script>

 

关于 this 指向性问题

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁。

推荐阅读

https://www.cnblogs.com/pssp/p/5216085.html

精华摘录

情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window(严格模式中this指向undefined)。

情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,this指向的是最后调用它的对象。

new,call,apply,bind 会改变函数中 this 的指向。

当this碰到return时
如果返回值是一个对象,那么this指向的就是那个返回的对象,否则 this 还是指向函数实例。
还有一点就是虽然 null 也是对象,但是在这里 this 还是指向那个函数的实例。

 

.prototype 的两种写法

先写 .prototype={},后写 prototype.xxx=function(){} 可以正常使用。

反之被替换报错。

<script>
  //测试 .prototype 的两种写法共存
  //先写 .prototype={},后写 prototype.getImageLength=function(){} 可以正常使用。反之被替换报错。
  //原因:先写对象,后添加对象的属性可以。
  var LoopImages=function(imgArr,container){
    this.imagesArray=imgArr;
    this.container=container;
  }

  LoopImages.prototype={
    createImage:function(){console.log("createImage")},
    changeImage:function(){console.log("changeImage")}
  } 

  LoopImages.prototype.getImageLength=function(){
    console.log(this.imagesArray.length);
    return this.imagesArray.length;
  }

  //渐隐切换类
  var FadeLoopImg=function(imgArr,container,arrow){
    //构造函数继承图片轮播类
    LoopImages.call(this,imgArr,container);
    //切换箭头
    this.arrow=arrow;
  }

  FadeLoopImg.prototype=new LoopImages();

  //重写继承的切换方法
  FadeLoopImg.prototype.changeImage=function(){
    console.log('FadeLoopImg changeImage function')
  }

  //测试用例
  //实例化一个渐隐切换图片类
  var fadeImg=new FadeLoopImg(['1.jpg','2.jpg','3.jpg','4.jpg'],'slide',['left.jpg','right.jpg']);
  fadeImg.changeImage()//FadeLoopImg changeImage function
  fadeImg.createImage()//createImage
  fadeImg.getImageLength()//4
</script>

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Irene1991

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值