前言

我是歌谣 放弃很容易 但是坚持一定很酷 微信公众号关注前端小歌谣带你进入前端巅峰交流群 今天继续对前端知识的小结

单体模式应用弹框

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>单体模式应用弹框</title>
</head>

<body>
  <div id="Id">我是歌谣</div>
  <script>
    // 实现单体模式弹窗
    var createWindow = (function () {
      var div;
      return function () {
        if (!div) {
          div = document.createElement("div");
          div.innerHTML = "我是弹窗内容";
          div.style.display = 'none';
          document.body.appendChild(div);
        }
        return div;
      }
    })();
    document.getElementById("Id").onclick = function () {
      // 点击后先创建一个div元素
      var win = createWindow();
      win.style.display = "block";
    }
  </script>
</body>

</html>

原型和原型链

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>原型和原型链</title>
	</head>
	<body>
		<script>
			// 			对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在
			// 每个对象都有__proto__原型的存在

			//// 原型的构造器指向构造函数。

			//原型上添加方法注意的地方 构造器指向构造函数本身

			//Star.prototype = {}给原型重新赋值,此时会丢失构造器,

			//我们需要手动定义构造器,指回构造函数本身
			// 原型的构造器指向构造函数。
			function Animal(name) {
				this.name = name
			}
			let obj = new Animal('小猴')
			console.log(Animal.prototype.constructor === Animal) //true
			console.log(obj.__proto__.constructor === Animal) //true

			//原型上添加方法注意的地方 构造器指向构造函数本身
			function Star(name) {
				this.name = name
			}
			Star.prototype.dance = function () {
				console.log(this.name)
			}
			let geyao = new Star('小花')
			console.log(geyao.__proto__) //{dance: ƒ, constructor: ƒ}
			console.log(geyao.__proto__.constructor) // Star

            //赋值{}
			function Star(name) {
				this.name = name
			}
			Star.prototype = {
				dance: function () {
					console.log(this.name)
				},
			}
			let fangfang = new Star('小红')
			console.log(fangfang.__proto__) //{dance: ƒ}
			console.log(fangfang.__proto__.constructor) //  ƒ Object() { [native code] }
			Star.prototype.constructor = Star
		</script>
	</body>
</html>

原型和原型链继承方式

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>原型继承方式</title>
	</head>
	<body>
		<script>
			//ES6之前并没有给我们提供extends继承,我们可以通过构造函数+原型对象模拟实现继承。
			// 继承属性,利用call改变this指向。但该方法只可以继承属性,实例不可以使用父类的方法。
			// function Father(name) {
			// 	this.name = name
			// }
			// Father.prototype.dance = function () {
			// 	console.log('我会跳舞')
			// }
			// function Son(name, age) {
			// 	Father.call(this, name)
			// 	this.age = age
			// }
			// let son = new Son('歌谣', 100)
			// son.dance() //报错

			// 解决方法1:利用Son.prototype = Father.prototype改变原型指向,但此时我们给子类增加原型方法,同样会影响到父类。
			// function Father(name) {
			// 	this.name = name
			// }
			// Father.prototype.dance = function () {
			// 	console.log('我会跳舞')
			// }
			// function Son(name, age) {
			// 	Father.call(this, name)
			// 	this.age = age
			// }
			// Son.prototype = Father.prototype
			// //为子类添加方法
			// Son.prototype.sing = function () {
			// 	console.log('我会唱歌')
			// }
			// let son = new Son('歌谣', 100)
			// //此时父类也被影响了
			// console.log(Father.prototype) //{dance: ƒ, sing: ƒ, constructor: ƒ}

			//解决方法2:子类的原型指向父类的实例,这样就可以顺着原型链共享父类的方法了。并且为子类添加原型方法的时候,不会影响父类。
			function Father(name) {
				this.name = name
			}
			Father.prototype.dance = function () {
				console.log('我会跳舞')
			}
			function Son(name, age) {
				Father.call(this, name)
				this.age = age
			}
			Son.prototype = new Father()
			Son.prototype.sing = function () {
				console.log('我会唱歌')
			}
			let son = new Son('歌谣', 100)
			console.log(Father.prototype) //{dance: ƒ, constructor: ƒ}
		</script>
	</body>
</html>

原型式继承

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>原型式继承</title>
	</head>
	<body>
		<script>
            //方法等同于Object.create()
            //返回原来的原型对象
			function object(obj) {
				function F() {}
				F.prototype = obj
				return new F()
			}


            //利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。
			var person = {
				name: 'Nicholas',
				friends: ['Shelby', 'Court', 'Van'],
			}

			var anotherPerson = object(person)
			anotherPerson.name = 'Greg'
			anotherPerson.friends.push('Rob')

			var yetAnotherPerson = object(person)
			yetAnotherPerson.name = 'Linda'
			yetAnotherPerson.friends.push('Barbie')

    //原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
   // 无法传递参数
			console.log(person.friends) //"Shelby,Court,Van,Rob,Barbie"
		</script>
	</body>
</html>

双飞翼布局

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>双飞翼布局220402</title>
</head>
<style>
  .float {
    float: left;
  }

  #main {
    width: 100%;
    height: 200px;
    background-color: lightpink;
  }

  #main-wrap {
    margin: 0 190px 0 190px;
  }

  #left {
    width: 190px;
    height: 200px;
    background-color: lightsalmon;
    margin-left: -100%;
  }

  #right {
    width: 190px;
    height: 200px;
    background-color: lightskyblue;
    margin-left: -190px;
  }
</style>

<body>
  <!-- 圣杯布局:为了让中间div内容不被遮挡,将中间div设置了左右padding-left和padding-right后,将左右两个div用相对布局position: 
    relative并分别配合right和left属性,以便左右两栏div移动后不遮挡中间div。
双飞翼布局:为了让中间div内容不被遮挡,直接在中间div内部创建子div用于放置内容,
在该div里用margin-left和margin-right为左右两栏div留出位置。 -->
  <div id="main" class="float">
    <div id="main-wrap">main</div>
  </div>
  <div id="left" class="float">left</div>
  <div id="right" class="float">right</div>


</body>

</html>

发布-订阅者模式例子

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>发布--订阅者模式</title>
</head>

<body>
  <script>
    ////定义一所学校(中间件,事件派发中心)
    //主要功能包括校园网(topics),以及学生订阅消息(subscribe),老师发布课程(publish)
    let School = {
      type: "hunt",
      topics: Object.create(null),
      //添加订阅的事件,当一个对象订阅事件的时候,分别传入事件名字和回调函数(当订阅事件触发时候执行的操作)
      subscribe: function (topic, fn) {
        //如果派发对象内检索不到这个对象,就说明是一个新事件,新建一个数组对象,然后把该订阅者的回调放进事件对象中
        //topics是一个对象,里面有多个事件对象,事件对象内部是一个数组,包含这个事件的订阅者
        if (!this.topics[topic]) {
          this.topics[topic] = [];
          console.log("添加订阅" + topic + "的学生到Peter老师的订阅对象中");
          this.topics[topic].push(fn);
        }

      },
      // 发布事件,在事件订阅对象中检索这个事件,有就调用该对象中所有回调,否则直接返回
      publish: function (topic, money) {
        if (!this.topics[topic]) return;
        for (let fn of this.topics[topic]) {
          console.log("校园网发布" + topic + "课程");
          fn(money);
        }
      }
    }
    //定义一个师生类
    //包括姓名,身份
    function Student(name, identity) {
      this.name = name;
      this.identity = identity;
      //师生可以在校园网上发布订阅任务  //定义一个订阅函数,这里面调用了任务中心的订阅函数,收集所有订阅者的订阅事件和回调
      this.subscribe = function subscribe(topic, fn) {
        console.log(this.identity + "的" + this.name + "在校园网订阅了" + topic + "的课程");
        School.subscribe(topic, fn);
      };
      //定义一个发布函数,这里面调用了任务中心的发布函数,调用所有订阅者的回调  
      this.publish = function publish(topic, money) {
        console.log(this.identity + "老师" + this.name + "在校园网发布了" + topic + "的课程");
        School.publish(topic, money);
      };


    }
    //案例开始,先定义学生,老师
    let studentMing = new Student("小明", "大一");
    let studentJin = new Student("小金", "大一");
    let studentZhang = new Student("小张", "大一");
    let studentPeter = new Student("Peter", "老师");

    //小明,小金,小张分别订阅了发布课程
    studentMing.subscribe("Peter老师", function (money) {
      console.log("小明表示:" + (money = "美术课" ? "不" : "") + "接取美术课");
    });
    studentJin.subscribe("Peter老师", function (money) {
      console.log("小金表示:接取任何课程");
    });
    studentZhang.subscribe("Peter老师", function (money) {
      console.log("小张表示:" + (money = "前端课" ? "我不上其他课," : "") + "我要上前端课啊");
    });
    //Peter发布了课程,美术课
    studentPeter.publish("Peter老师", "美术课");
  </script>
</body>

</html>

发布订阅者模式

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>发布订阅者模式</title>
</head>

<body>
  <script>
    // 创建一个对象
    // 在该对象上创建一个缓存列表( 调度中心)
    // on 方法用来把函数 fn 都加到缓存列表中( 订阅者注册事件到调度中心)
    // emit 方法取到 arguments 里第一个当做 event, 根据 event 值去执行对应缓存列表中的函数( 发布者发布事件到调度中心, 调度中心处理代码)
    // off 方法可以根据 event 值取消订阅( 取消订阅)
    // once 方法只监听一次, 调用完毕后删除缓存函数( 订阅一次)

    // 公众号对象
    // let eventEmitter = {};

    // // 缓存列表,存放 event 及 fn
    // eventEmitter.list = {};

    // // 订阅
    // eventEmitter.on = function (event, fn) {
    //   console.log(this,"this")
    //   let _this = this;
    //   // 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
    //   // 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
    //   (_this.list[event] || (_this.list[event] = [])).push(fn);
    //   return _this;
    // };

    // // 发布
    // eventEmitter.emit = function () {
    //   console.log(this,"this")
    //   let _this = this;
    //   // 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
    //   console.log(arguments,"arguments")
    //   let event = [].shift.call(arguments),
    //     fns = [..._this.list[event]];
    //   // 如果缓存列表里没有 fn 就返回 false
    //   if (!fns || fns.length === 0) {
    //     return false;
    //   }
    //   // 遍历 event 值对应的缓存列表,依次执行 fn
    //   fns.forEach(fn => {
    //     fn.apply(_this, arguments);
    //   });
    //   return _this;
    // };

    // function user1(content) {
    //   console.log('用户1订阅了:', content);
    // };

    // function user2(content) {
    //   console.log('用户2订阅了:', content);
    // };

    // // 订阅
    // eventEmitter.on('article', user1);
    // eventEmitter.on('article', user2);

    // // 发布
    // eventEmitter.emit('article', 'Javascript 发布-订阅模式');

    // /*
    //     用户1订阅了: Javascript 发布-订阅模式
    //     用户2订阅了: Javascript 发布-订阅模式
    // */
    let eventEmitter = {
      // 缓存列表
      list: {},
      // 订阅
      on(event, fn) {
        let _this = this;
        // 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
        // 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
        (_this.list[event] || (_this.list[event] = [])).push(fn);
        return _this;
      },
      // 监听一次
      once(event, fn) {
        // 先绑定,调用后删除
        let _this = this;

        function on() {
          _this.off(event, on);
          fn.apply(_this, arguments);
        }
        on.fn = fn;
        _this.on(event, on);
        return _this;
      },
      // 取消订阅
      off(event, fn) {
        let _this = this;
        let fns = _this.list[event];
        // 如果缓存列表中没有相应的 fn,返回false
        if (!fns) return false;
        if (!fn) {
          // 如果没有传 fn 的话,就会将 event 值对应缓存列表中的 fn 都清空
          fns && (fns.length = 0);
        } else {
          // 若有 fn,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可
          let cb;
          for (let i = 0, cbLen = fns.length; i < cbLen; i++) {
            cb = fns[i];
            if (cb === fn || cb.fn === fn) {
              fns.splice(i, 1);
              break
            }
          }
        }
        return _this;
      },
      // 发布
      emit() {
        let _this = this;
        // 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
        let event = [].shift.call(arguments),
          fns = [..._this.list[event]];
        // 如果缓存列表里没有 fn 就返回 false
        if (!fns || fns.length === 0) {
          return false;
        }
        // 遍历 event 值对应的缓存列表,依次执行 fn
        fns.forEach(fn => {
          fn.apply(_this, arguments);
        });
        return _this;
      }
    };

    function user1(content) {
      console.log('用户1订阅了:', content);
    }

    function user2(content) {
      console.log('用户2订阅了:', content);
    }

    function user3(content) {
      console.log('用户3订阅了:', content);
    }

    function user4(content) {
      console.log('用户4订阅了:', content);
    }

    // 订阅
    eventEmitter.on('article1', user1);
    eventEmitter.on('article1', user2);
    eventEmitter.on('article1', user3);

    // 取消user2方法的订阅
    eventEmitter.off('article1', user2);

    eventEmitter.once('article2', user4)

    // 发布
    eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
    eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
    eventEmitter.emit('article2', 'Javascript 观察者模式');
    eventEmitter.emit('article2', 'Javascript 观察者模式');

    // eventEmitter.on('article1', user3).emit('article1', 'test111');

    /*
        用户1订阅了: Javascript 发布-订阅模式
        用户3订阅了: Javascript 发布-订阅模式
        用户1订阅了: Javascript 发布-订阅模式
        用户3订阅了: Javascript 发布-订阅模式
        用户4订阅了: Javascript 观察者模式
    */
  </script>
</body>

</html>

命令模式

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>命令模式的第二种应用</title>
</head>

<body>
  <button id="button1">刷新菜单目录</button>
  <button id="button2">增加子菜单</button>
  <button id="button3">删除子菜单</button>
  <button id="button4">更新子菜单</button>
  <script>
    // 如下代码上的四个按钮 点击事件
    var b1 = document.getElementById("button1"),
      b2 = document.getElementById("button2"),
      b3 = document.getElementById("button3"),
      b4 = document.getElementById("button4");
    /*
     bindEnv函数负责往按钮上面安装点击命令。点击按钮后,会调用
     函数
     */
    var bindEnv = function (button, func) {
      button.onclick = function () {
        func();
      }
    };
    // 现在我们来编写具体处理业务逻辑代码
    var Todo1 = {
      test1: function () {
        alert("我是来做第一个测试的");
      }
    };
    // 实现业务中的增删改操作
    var Menu = {
      add: function () {
        alert("我是来处理一些增加操作的");
      },
      del: function () {
        alert("我是来处理一些删除操作的");
      },
      update: function () {
        alert("我是来处理一些更新操作的");
      }
    };
    // 调用函数
    bindEnv(b1, Todo1.test1);
    // 增加按钮
    bindEnv(b2, Menu.add);
    // 删除按钮
    bindEnv(b3, Menu.del);
    // 更改按钮
    bindEnv(b4, Menu.update);
  </script>
</body>

</html>

命令模式的例子

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>命令模式</title>
</head>

<body>
  <button id="button1">刷新菜单目录</button>
<button id="button2">增加子菜单</button>
<button id="button3">删除子菜单</button>
  <script>
    var b1 = document.getElementById("button1"),
      b2 = document.getElementById("button2"),
      b3 = document.getElementById("button3");

    // 定义setCommand 函数,该函数负责往按钮上面安装命令。点击按钮后会执行command对象的execute()方法。
    var setCommand = function (button, command) {
      button.onclick = function () {
        command.execute();
      }
    };
    // 下面我们自己来定义各个对象来完成自己的业务操作
    var MenuBar = {
      refersh: function () {
        alert("刷新菜单目录");
      }
    };
    var SubMenu = {
      add: function () {
        alert("增加子菜单");
      },
      del: function () {
        alert("删除子菜单");
      }
    };
    // 下面是编写命令类
    var RefreshMenuBarCommand = function (receiver) {
      this.receiver = receiver;
    };
    RefreshMenuBarCommand.prototype.execute = function () {
      this.receiver.refersh();
    }
    // 增加命令操作
    var AddSubMenuCommand = function (receiver) {
      this.receiver = receiver;
    };
    AddSubMenuCommand.prototype.execute = function () {
      this.receiver.add();
    }
    // 删除命令操作
    var DelSubMenuCommand = function (receiver) {
      this.receiver = receiver;
    };
    DelSubMenuCommand.prototype.execute = function () {
      this.receiver.del();
    }
    // 最后把命令接收者传入到command对象中,并且把command对象安装到button上面
    var refershBtn = new RefreshMenuBarCommand(MenuBar);
    var addBtn = new AddSubMenuCommand(SubMenu);
    var delBtn = new DelSubMenuCommand(SubMenu);

    setCommand(b1, refershBtn);
    setCommand(b2, addBtn);
    setCommand(b3, delBtn);
  </script>
</body>

</html>

总结

我是歌谣 最好的种树是十年前 其次是现在 加油 歌谣