We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
类似于现实生活中的工厂,可产生大量相似的商品、实现同样的效果
const CreatePerson = (name, age, sex) => { const obj = new Object() obj.name = name obj.age = age obj.sex = sex obj.sayName = function () { return this.name } return obj } const personA = new CreatePerson('琚致远', '21', '男') const personB = new CreatePerson('晨阳', '18', '女') console.log(personA.name) // 琚致远 console.log(personA.age) // 21 console.log(personA.sex) // 男 console.log(personA.sayName()) // 琚致远 console.log(personB.name) // 晨阳 console.log(personB.age) // 18 console.log(personB.sex) // 女 console.log(personB.sayName()) // 晨阳 // 类型均为 object,无法识别对象类型、无法识别它们属于哪个对象的实例 console.log(typeof personA) // object console.log(typeof personB) // object console.log(personA instanceof Object) // true
将其成员对象的实例化推迟到子类中,子类可以重写父类接口方法以便创建的时候指定自己的对象类型
// 父类 const BicycleShop = () => {} BicycleShop.prototype = { constructor: BicycleShop, /** * 卖自行车 * @param {model} 自行车型号 */ sellBicycle: function (model) { const bicycle = this.createBicycle(model) // 执行 A 业务逻辑 bicycle.A() // 执行 B 业务逻辑 bicycle.B() return bicycle }, createBicycle: function (model) { throw new Error('父类是抽象类,不可直接调用,需要子类重写该方法') } }
// 自行车构造函数 function BicycleShop (name) { this.name = name this.method = () => { return this.name } } BicycleShop.prototype = { constructor: BicycleShop, /** * 卖自行车 * @param {model} 自行车型号 */ sellBicycle: function (model) { const bicycle = this.createBicycle(model) // 执行 A 业务逻辑 bicycle.A() // 执行 B 业务逻辑 bicycle.B() return bicycle }, createBicycle: function (model) { throw new Error('父类是抽象类,不可直接调用,需要子类重写该方法') } } /** * 实现原型继承 * @param { Sub } 子类 * @param { Sup } 超类 */ const extend = (Sub, Sup) => { // 定义空函数 const F = function () {} // 设置空函数原型为超类的原型 F.prototype = Sup.prototype // 实例化空函数,并将超类原型引用传递给子类 Sub.prototype = new F() // 重置子类原型的构造器为子类本身 Sub.prototype.constructor = Sub // 在子类中保存超类原型,避免子类与超类耦合 Sub.sup = Sup.prototype // 检查超类原型的构造器是否为原型自身 if (Sup.prototype.constructor === Object.prototype.constructor) Sup.prototype.constructor = Sup } function BicycleChild (name) { this.name = name // 继承构造函数父类中的属性与方法 BicycleShop.call(this.name) } // 子类继承父类原型方法 extend(BicycleChild, BicycleShop) // BicycleChild 子类重写父类方法 BicycleChild.prototype.createBicycle = () => { const A = () => { console.log('执行 A 业务逻辑') } const B = () => { console.log('执行 B 业务逻辑') } return { A, B } } const childClass = new BicycleChild('飞鸽') console.log(childClass)
结果如下
产生一个“类”的唯一实例(注意:JavaScript 中并没有真正的类)
有如下场景:点击某个按钮的时候,弹出遮罩层,比如web.qq.com点击登录的时候
// 生成灰色背景遮罩层 var createMask = function() { return document.body.appendChild(document.createElement("div")); } $('button').click(function() { var mask = createMask(); mask.show(); })
上方代码的问题是:这个遮罩层是全局唯一的,每次调用createMask都会创建一个新的div,虽然可以在遮罩层隐藏的时候将它移除掉,但这是不合理的。
createMask
div
在页面一开始就创建好这个div,然后用一个变量引用它。
var mask = document.body.appendChild(document.createElement('div')); $('button').click(function() { mask.show() })
这样确实在页面只会创建一个遮罩层div,但是问题是:我们或许永远也不会使用它,因此就会浪费一个div,对dom节点的任何操作应当是吝啬的。
dom
如果借助一个变量,来判断是否已经创建过div呢?
var mask; var createMask = function() { if (mask) return mask; mask = document.body.appendChild(document.createElement('div')); return mask; }
上方代码完成了一个产生单例对象的函数,不过我们分析下它有什么不妥:
首先,这个函数存在一定副作用,函数体内改变了外界mask的引用,在多人协作时,createMask是一个不安全的函数;此外,mask这个全局变量并不是必须非要不可,修改如下:
mask
var createMask = (function() { var mask; return function() { return mask || (mask = document.body.appendChild(document.createElement('div'))); } })()
用 闭包 将变量mask包起来,至少对函数createMask来讲,它是封闭的。上方的单例模式还是有缺点的,它只能用于创建遮罩层,加入我又写了一个函数,用来创建唯一的XHR对象,能不能找到一个通用的单例包装器?
JavaScript 中,函数是第一型,意味着函数可以当做参数来传递,以下为最终代码:
var wrapper = function(fn) { var result; return function() { return result || (result = fn.apply(this, arguments)); } } var createMask = wrapper(function() { return document.body.appendChild(document.createElement('div')); })
用一个变量来保存第一次返回值,如果它已经被赋值过了,那么在以后的调用中优先返回该变量,而真正创建遮罩层的代码是通过回调函数的方式传入到wrapper包装器的,这种方式叫桥接模式。
wrapper包装器
然而wrapper函数还是不完美,它始终需要一个变量result来寄存div的引用,遗憾的是JavaScript 的函数式特性不足以完全消除声明与语句。
wrapper函数
result
The text was updated successfully, but these errors were encountered:
No branches or pull requests
简单工厂模式
优点
缺点
复杂工厂模式
场景:开了多个自行车店,每个点有几种型号的自行车出售
结果如下
单例模式
有如下场景:点击某个按钮的时候,弹出遮罩层,比如web.qq.com点击登录的时候
方案一
上方代码的问题是:这个遮罩层是全局唯一的,每次调用
createMask
都会创建一个新的div
,虽然可以在遮罩层隐藏的时候将它移除掉,但这是不合理的。方案二
在页面一开始就创建好这个
div
,然后用一个变量引用它。这样确实在页面只会创建一个遮罩层
div
,但是问题是:我们或许永远也不会使用它,因此就会浪费一个div
,对dom
节点的任何操作应当是吝啬的。如果借助一个变量,来判断是否已经创建过
div
呢?上方代码完成了一个产生单例对象的函数,不过我们分析下它有什么不妥:
首先,这个函数存在一定副作用,函数体内改变了外界
mask
的引用,在多人协作时,createMask
是一个不安全的函数;此外,mask
这个全局变量并不是必须非要不可,修改如下:方案三
用 闭包 将变量
mask
包起来,至少对函数createMask
来讲,它是封闭的。上方的单例模式还是有缺点的,它只能用于创建遮罩层,加入我又写了一个函数,用来创建唯一的XHR对象,能不能找到一个通用的单例包装器?JavaScript 中,函数是第一型,意味着函数可以当做参数来传递,以下为最终代码:
方案四
用一个变量来保存第一次返回值,如果它已经被赋值过了,那么在以后的调用中优先返回该变量,而真正创建遮罩层的代码是通过回调函数的方式传入到
wrapper包装器
的,这种方式叫桥接模式。然而
wrapper函数
还是不完美,它始终需要一个变量result
来寄存div
的引用,遗憾的是JavaScript 的函数式特性不足以完全消除声明与语句。The text was updated successfully, but these errors were encountered: