//G类继承F类
function G(){
F.call(this);
}
//原型继承
function Fn(){};
Fn.prototype = F.prototype;
G.prototype = new Fn();
G.prototype.constructor = G;
原型模式就是创建一个指定原型的对象。如果我们需要重复创建某个对象,那么就可以使用原型模式来实现。
结构型模式
1. 装饰器模式
装饰器模式:为对象添加新功能,不改变其原有的结构和功能。
适配器模式是原有的不能用了,要重新封装接口。装饰器模式是原有的还能用,但是需要新增一些东西来完善这个功能。
比如手机壳,手机本身的功能不受影响,手机壳就是手机的装饰器模式。
class Circle {
draw() {
console.log(‘画一个圆形’);
}
}
class Decorator {
constructor(circle) {
this.circle = circle;
}
draw() {
this.circle.draw();
this.setRedBorder(circle);
}
setRedBorder(circle) {
console.log(‘设置红色边框’)
}
}
// 测试
let circle = new Circle();
let client = new Decorator(circle);
client.draw();
输出结果:
画一个圆形
设置红色边框
如今都2021了,es7也应用广泛,我们在es7中这么写(ES7装饰器):
1、安装 yarn add babel-plugin-transform-decorators-legacy
2、新建.babelrc文件,进行下面的配置
{
“presets”: [“es2015”, “latest”],
“plugins”: [“transform-decorators-legacy”]
}
3、上代码
@testDec
class Demo {
// …
}
function testDec(target) {
target.isDec = true
}
console.log(Demo.isDec)
//输出true
打印出来了true,说明@testDec这个装饰器已经成功了,函数是个装饰器,用@testDec给Demo装饰了一遍。这个target其实就是class Demo,然后给她加一个isDec。
拆解后就是下面的内容:
// 装饰器原理
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
装饰器参数的形式
@testDec(false)
class Demo {
}
function testDec(isDec) {
return function (target) {
target.isDec = isDec
}
}
console.log(Demo.isDec);
验证是否是一个真正的装饰器模式需要验证以下几点:
1.将现有对戏那个和装饰器进行分离,两者独立存在
2.符合开放封闭原则
2. 适配器模式
适配器模式:旧接口格式和使用者不兼容,中间加一个适配转换接口。
比如国外的插座跟国内的插座不一样,我们需要买个转换器去兼容。
上代码:
class Adaptee {
specificRequest() {
return ‘德国标准的插头’;
}
}
class Target {
constructor() {
this.adaptee = new Adaptee();
}
request() {
let info = this.adaptee.specificRequest();
return ${info} -> 转换器 -> 中国标准的插头
}
}
// 测试
let client = new Target();
client.request();
结果:
德国标准的插头 -> 转换器 -> 中国标准的插头
场景上可封装旧接口:
// 自己封装的ajax,使用方式如下:
ajax({
url: ‘/getData’,
type: ‘Post’,
dataType: ‘json’,
data: {
id: ‘123’
}
}).done(function(){
})
// 但因为历史原因,代码中全都是:
// $.ajax({…})
这个时候需要一个适配器:
// 做一层适配器
var $ = {
ajax: function (options) {
return ajax(options)
}
}
3. 代理模式
代理模式:使用者无权访问目标对象,中间加代理,通过代理做授权和控制。
明星经纪人:比如有个演出,要请明星,要先联系经纪人。
或者理解为:为一个对象提供一个代用品或者占位符,以便控制对它的访问。例如图片懒加载、中介等。
/**
-
pre:代理模式
-
小明追求A,B是A的好朋友,小明比不知道A什么时候心情好,不好意思直接将花交给A,
-
于是小明将花交给B,再由B交给A.
*/
// 花的类
class Flower{
constructor(name){
this.name = name
}
}
// 小明拥有sendFlower的方法
let Xioaming = {
sendFlower(target){
var flower = new Flower(“玫瑰花”)
target.receive(flower)
}
}
// B对象中拥有接受花的方法,同时接收到花之后,监听A的心情,并且传入A心情好的时候函数
let B = {
receive(flower){
this.flower =flower
A.listenMood(()=>{
A.receive(this.flower)
})
}
}
// A接收到花之后输出花的名字
let A = {
receive(flower){
console.log(A收到了${flower.name}
)
// A收到了玫瑰花
},
listenMood(func){
setTimeout(func,1000)
}
}
Xioaming.sendFlower(B)
虚拟代理用于图片的预加载
图片很大,页面加载时会空白,体验不好,所以我们需要个占位符,来短暂替代这个图片,等图片加载好了放上去。
let myImage = (function(){
let img = new Image
document.body.appendChild(img)
return {
setSrc:(src)=>{
img.src = src
}
}
})()
let imgProxy =(function(){
let imgProxy = new Image
// 这个地方我使用了setTimeout来增强演示效果,否则本地加载太快,根本看不到。
imgProxy.οnlοad=function(){
setTimeout(()=>{
myImage.setSrc(this.src)
},2000)
}
return (src)=>{
myImage.setSrc(“…/…/img/bgimg.jpeg”)
imgProxy.src=src
}
})()
imgProxy(“…/…/img/background-cover.jpg”)
ES6 Proxy
其实在ES6中,已经有了Proxy,这个内置的函数。我们来用一个例子来演示一下他的用法。这是一个明星代理的问题。
let star={
name : “张XX”,
age:25,
phone : “1300001111”
}
let agent = new Proxy(star,
{
get:function(target,key){
if(key === “phone”){
return “18839552597”
}else if(key === “name”){
return “张XX”
}else if(key === “price”){
return “12W”
}else if(key === “customPrice”){
return target.customPrice
}
},
set:function(target,key,value){
if(key === “customPrice”){
if(value < “10”){
console.log(“太低了!!!”)
return false
}else{
target[key] = value
return true
}
}
}
}
)
console.log(agent.name)
console.log(agent.price)
console.log(agent.phone)
console.log(agent.age)
agent.customPrice = “12”
console.log(agent)
console.log(agent.customPrice)
设计原则验证
代理类和目标类分离,隔离开目标类和使用者
符合开放封闭原则
行为型模式
1. 策略模式
策略模式是一种简单却常用的设计模式,它的应用场景非常广泛。我们先了解下策略模式的概念,再通过代码示例来更清晰的认识它。
策略模式由两部分构成:一部分是封装不同策略的策略组,另一部分是 Context。通过组合和委托来让 Context 拥有执行策略的能力,从而实现可复用、可扩展和可维护,并且避免大量复制粘贴的工作。
策略模式的典型应用场景是表单校验中,对于校验规则的封装。接下来我们就通过一个简单的例子具体了解一下:
/**
- 登录控制器
*/
function LoginController() {
this.strategy = undefined;
this.setStrategy = function (strategy) {
this.strategy = strategy;
this.login = this.strategy.login;
}
}
/**
- 用户名、密码登录策略
*/
function LocalStragegy() {
this.login = ({ username, password }) => {
console.log(username, password);
// authenticating with username and password…
}
}
/**
- 手机号、验证码登录策略
*/
function PhoneStragety() {
this.login = ({ phone, verifyCode }) => {
console.log(phone, verifyCode);
// authenticating with hone and verifyCode…
}
}
/**
- 第三方社交登录策略
*/
function SocialStragety() {
this.login = ({ id, secret }) => {
console.log(id, secret);
// authenticating with id and secret…
}
}
const loginController = new LoginController();
// 调用用户名、密码登录接口,使用LocalStrategy
app.use(‘/login/local’, function (req, res) {
loginController.setStrategy(new LocalStragegy());
loginController.login(req.body);
});
// 调用手机、验证码登录接口,使用PhoneStrategy
app.use(‘/login/phone’, function (req, res) {
loginController.setStrategy(new PhoneStragety());
loginController.login(req.body);
});
// 调用社交登录接口,使用SocialStrategy
app.use(‘/login/social’, function (req, res) {
loginController.setStrategy(new SocialStragety());
loginController.login(req.body);
});
从以上示例可以得出使用策略模式有以下优势:
-
方便在运行时切换算法和策略
-
代码更简洁,避免使用大量的条件判断
-
关注分离,每个strategy类控制自己的算法逻辑,strategy和其使用者之间也相互独立
2. 观察者模式
观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一或一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。典型代表vue/react等。
使用观察者模式的好处:
-
支持简单的广播通信,自动通知所有已经订阅过的对象。
-
目标对象与观察者存在的是动态关联,增加了灵活性。
-
目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
当然给元素绑定事件的addEventListener()
也是一种:
target.addEventListener(type, listener [, options]);
Target就是被观察对象Subject,listener就是观察者Observer。
观察者模式中Subject对象一般需要实现以下API:
-
subscribe()
: 接收一个观察者observer对象,使其订阅自己 -
unsubscribe()
: 接收一个观察者observer对象,使其取消订阅自己 -
fire()
: 触发事件,通知到所有观察者
用JavaScript手动实现观察者模式:
// 被观察者
function Subject() {
this.observers = [];
}
Subject.prototype = {
// 订阅
subscribe: function (observer) {
this.observers.push(observer);
},
// 取消订阅
unsubscribe: function (observerToRemove) {
this.observers = this.observers.filter(observer => {
return observer !== observerToRemove;
})
},
// 事件触发
fire: function () {
this.observers.forEach(observer => {
observer.call();
});
}
}
验证一下订阅是否成功:
const subject = new Subject();
function observer1() {
console.log(‘Observer 1 Firing!’);
}
function observer2() {
console.log(‘Observer 2 Firing!’);
}
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.fire();
输出:
Observer 1 Firing!
Observer 2 Firing!
验证一下取消订阅是否成功:
subject.unsubscribe(observer2);
subject.fire();
跳槽是每个人的职业生涯中都要经历的过程,不论你是搜索到的这篇文章还是无意中浏览到的这篇文章,希望你没有白白浪费停留在这里的时间,能给你接下来或者以后的笔试面试带来一些帮助。
也许是互联网未来10年中最好的一年。WINTER IS COMING。但是如果你不真正的自己去尝试尝试,你永远不知道市面上的行情如何。这次找工作下来,我自身感觉市场并没有那么可怕,也拿到了几个大厂的offer。在此进行一个总结,给自己,也希望能帮助到需要的同学。
面试准备
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
面试准备根据每个人掌握的知识不同,准备的时间也不一样。现在对于前端岗位,以前也许不是很重视算法这块,但是现在很多公司也都会考。建议大家平时有空的时候多刷刷leetcode。算法的准备时间比较长,是一个长期的过程。需要在掌握了大部分前端基础知识的情况下,再有针对性的去复习算法。面试的时候算法能做出来肯定加分,但做不出来也不会一票否决,面试官也会给你提供一些思路。