前言
我是歌谣 放弃很容易 但是坚持一定很酷 微信公众号关注前端小歌谣带你进入前端巅峰交流群 今天继续对前端知识的小结
单体模式应用弹框
<!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>
总结
我是歌谣 最好的种树是十年前 其次是现在 加油 歌谣