
灵活的js
函数声明
// 劣势:创建了很多全局变量
function checkname() {
// 验证姓名
}
function checkage() {
// 验证年龄
}
function checkpassword() {
// 验证密码
}
函数表达式
// 函数表达式在使用前需要提前声明。
var checkname = function() {
// 验证姓名
}
var checkage = function() {
// 验证年龄
}
var checkpassword = function() {
// 验证密码
}
用对象收编变量
var checkObj = {
checkname() {
// 验证姓名
},
checkage() {
// 验证年龄
},
checkpassword() {
// 验证密码
},
}
对象的另一种形式
// 劣势:使用 new 关键字创建新对象时,不能继承这些方法
var checkObj = function() {}
checkObj.checkname = function() {
// 验证姓名
}
checkObj.checkage = function() {
// 验证年龄
}
checkObj.checkpassword = function() {
// 验证密码
}
真假对象
// 劣势:返回的新对象跟 checkObj 没有任何关系,做不到真正意义上的复制
var checkObj = function() {
return {
checkname() {
// 验证姓名
},
checkage() {
// 验证年龄
},
checkpassword() {
// 验证密码
},
}
}
var a = checkObj()
a.checkname()
类
// 劣势:每次 new 一个类的时候,都需要对类的 this 上的属性进性复制,造成资源消耗
var CheckObj = function() {
this.checkname = function() {
// 验证姓名
}
this.checkage = function() {
// 验证年龄
}
this.checkpassword = function() {
// 验证密码
}
}
var a = new CheckObj()
a.checkname()
检测类
// 劣势:prototype 属性需要写多遍
var CheckObj = function() {}
CheckObj.prototype.checkname = function() {
// 验证姓名
}
CheckObj.prototype.checkage = function() {
// 验证年龄
}
CheckObj.prototype.checkpassword = function() {
// 验证密码
}
// or
// 劣势:覆盖 prototype 对象
var CheckObj = function() {}
CheckObj.prototype = {
checkname() {
// 验证姓名
},
checkage() {
// 验证年龄
},
checkpassword() {
// 验证密码
},
}
var a = new CheckObj()
a.checkname()
a.checkage()
a.checkpassword()
链式调用
var CheckObj = {
checkname() {
// 验证姓名
return this
},
checkage() {
// 验证年龄
return this
},
checkpassword() {
// 验证密码
return this
},
}
// or
var CheckObj = function() {}
CheckObj.prototype = {
checkname() {
// 验证姓名
return this
},
checkage() {
// 验证年龄
return this
},
checkpassword() {
// 验证密码
return this
},
}
var a = new CheckObj()
a.checkname().checkage().checkpassword()
函数的祖先
Function.prototype.checkemail = function() {
// 验证邮箱
}
var f = function() {}
f.checkemail()
// or
var f = new Function()
f.checkemail()
// 缺点:污染原生对象 Function,所以别人创建的函数也会被你创建的函数所污染,造成不必要的开销
// 解决途径:抽象出一个统一添加方法的功能方法
Function.prototype.addMethods = function(name, fn) {
this[name] = fn
return this
}
// 链式添加和使用统一添加方法的功能方法
var methods = function() {}
methods
.addMethods('checkname', function() {
// 验证姓名
return this
})
.addMethods('checkage', function() {
// 验证年龄
return this
})
.addMethods('checkpassword', function() {
// 验证密码
return this
})
methods
.checkname()
.checkage()
.checkpassword()
// 当然你也可以使用类
Function.prototype.addMethod = function(name, fn) {
this.prototype[name] = fn
return this
}
var Methods = function() {}
Methods.addMethod('chencname', function() {}).addMethod('checkemail', function() {})
var m = new Methods()
m.checkname()
编程风格:面向对象编程(另外还有面向过程编程,我在初期写项目的时候一直用的是面向过程编程)
封装
类可以分为三个部分:第一部分是构造函数内部的,这是供实例化对象复制用的,第二部分是通过点语法添加的,这是供类使用的,实例化对象是访问不到的,第三部分是类的原型中的,实例化对象可以通过原型链间接地访问到,也是供所有实例化对象所共用的。
// 创建一个类
var Book = function(id, bookname, price) {
this.id = id
this.bookname = bookname
this.proce = price
}
// 通过类的原型附加属性
Book.prototype.display = function() {
// 展示这本书
}
// or
Book.prototype = {
display() {
// 展示这本书
}
}
// 使用以上两种方法之前需要知道各自方法的利弊,对于实例、原型、构造函数之间的关系可以自行度娘了解
// 通过 this 添加的属性和方法是该对象自身拥有的,所以我们每次通过类创建一个新对象时,this 指向的属性和方法都会得到相应的创建,而通过 prototype 继承的属性和方法是每个对象通过 prototype 访问到,所以我们每次通过类创建一个对象时这些属性和方法不会再次创建。
var Book = function(id, name, price) {
// 私有属性
var num = 1
// 私有方法
function checkid() {}
// 特权方法
this.getname = function() {}
this.getprice = function() {}
this.setname = function() {}
this.setprice = function() {}
// 公有属性
this.id = id
// 公有方法
this.copy = function() {}
// 构造器
this.setname(name)
this.setprice(price)
}
// 类静态公有属性(对象不能访问,只能通过类本身调用)
Book.isChinese = true
// 类静态公有方法(对象不能访问,只能通过类本身调用)
Book.resetTime = function() {
console.log('new Time')
}
Book.prototype = {
// 公有属性
isJSBook: false,
// 公有方法
display() {}
}
// 通过 new 关键字创建新对象时,由于类外面通过点语法添加的属性和方法没有执行到,所以新创建的对象中无法获取它们,但是可以通过类来使用。而类通过 prototype 创建的属性或者方法在类实例的对象中是可以通过 this 访问到的。
闭包
// 利用闭包实现类的静态变量
var Book = function() {
// 静态私有变量
var bookNum = 0
// 静态私有方法
function checkbook(name) {}
// 返回构造函数
return function(newid, newname, newprice) {
// 私有变量
var name, price
// 私有方法
function checkid(id) {}
// 特权方法
this.getname = function() {}
this.getprice = function() {}
this.setname = function() {}
this.setprice = function() {}
// 公有属性
this.id = newid
// 公有方法
this.copy = function() {}
bookNum++
if (bookNum > 100) {
throw new Error('我们仅出版100本书')
}
// 构造器
this.setname(name)
this.setprice(price)
}
}
Book.prototype = {
// 静态公有属性
isJSBook: false,
// 静态公有方法
display() {}
}
// or
var Book = function() {
// 静态私有变量
var bookNum = 0
// 静态私有方法
function checkbook(name) {}
// 返回构造函数
function _book(newid, newname, newprice) {
// 私有变量
var name, price
// 私有方法
function checkid(id) {}
// 特权方法
this.getname = function() {}
this.getprice = function() {}
this.setname = function() {}
this.setprice = function() {}
// 公有属性
this.id = newid
// 公有方法
this.copy = function() {}
bookNum++
if (bookNum > 100) {
throw new Error('我们仅出版100本书')
}
// 构造器
this.setname(name)
this.setprice(price)
}
_book.prototype = {
// 静态公有属性
isJSBook: false,
// 静态公有方法
display() {}
}
return _book
}
// 避免忘记使用 new 关键字实例化对象而造成意想不到的错误
var Book = function(title, time, type) {
if (this instanceof Book) {
this.title = title
this.time = time
this.type = type
} else {
return new Book(title, time, type)
}
}
继承
- 原型链继承
优势:
- 利用原型让一个引用类型继承另一个引用类型的属性和方法
劣势:
- 原型中包含引用类型值的属性时,该属性会被所有实例共享,那么该属性在其中一个实例中被修改后会影响所有实例。
- 在创建子类型实例时,不能向超类型的实例中传递参数。
function SuperType() {
this.property = true
}
SuperType.prototype.getSuperValue = function() {
return this.property
}
function SubType() {
this.subproperty = false
}
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function() {
return this.subproperty
}
var instance = new SubType()
instance.getSuperValue() // true
// 别忘了默认原型哦,一句话概括:SubType 继承了 SuperType,而 SuperType 继承了 Object。当调用 instance.toString() 方法时,实际上调用的是保存在 Object.prototype 中的那个方法。
- 构造函数继承
优势:
- 可以在子类型构造函数中向超类型构造函数传递参数
劣势:
- 方法都在构造函数中定义,因此函数很难复用
- 在超类型的原型中定义的方法,对子类型而言时不可见的,结果所有类型都只能使用构造函数模式
function SuperType(name) {
this.name = name
}
function SubType() {
SuperType.call(this, 'chen')
this.age = 18
}
var instance = new SubType()
instance.name // 'chen'
instance.age // 18
- 组合继承
优势:通过原型链实现对原型属性和方法的继承,通过构造函数实现对实例属性的继承。这样既通过原型上定义方法实现了函数的复用,又能够保证每个实例有自己的属性
劣势:
调用两次超类型构造函数:在创建子类型原型的时候,另一次在子类型构造函数内部。
function SuperType(name) {
this.name = name
this.colors = ['red']
}
SuperType.prototype.sayName = function() {
alter(this.name)
}
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function() {
alter(this.age)
}
var instance1 = new SubType('chen', 18)
instance1.colors.push('blue')
instance1.colors // ['red', 'blue']
instance1.sayName() // 'chen'
instance1.sayAge() // 18
var instance2 = new SubType('li', 16)
instance2.colors // ['red']
instance1.sayName() // 'li'
instance1.sayAge() // 20
- 原型式继承
function object(o) {
function F() {}
F.prototype = o
return new F()
}
var person = {
name: 'chen'
friends: ['li']
}
var person1 = object(person)
person1.name = 'chen1'
person1.friends.push('li1')
var person2 = object(person)
person2.name = 'chen2'
person2.friends.push('li2')
person1.name // 'chen1'
person1.friends // ['li', 'li1', 'li2']
person2.name // 'chen2'
person2.friends // ['li', 'li1', 'li2']
person.name // 'chen'
person.friends // ['li', 'li1', 'li2']
- 寄生式继承
function object(o) {
function F() {}
F.prototype = o
return new F()
}
function createAnother(original) {
var clone = object(original)
clone.sayHi = function() {
alter('Hi')
}
return clone
}
var person = {
name: 'chen'
friends: ['li']
}
var person1 = createAnother(person)
person1.sayHi() // 'Hi'
- 寄生组合式继承
优势:可以解决组合继承调用两次超类型构造函数的问题
function object(o) {
function F() {}
F.prototype = o
return new F()
}
function inheritPrototype(SubType, SuperType) {
var prototype = object(SuperType.prototype)
prototype.constructor = SubType
SubType.protoType = prototype
}
function SuperType(name) {
this.name = name
this.colors = ['red']
}
SuperType.prototype.sayName = function() {
alter(this.name)
}
function SubType(name, age) {
this.age = age
SuperType.call(this, name)
}
inheritPrototype(SubType, SuperType)
SubType.prototype.sayAge = function() {
alter(this.age)
}
多继承
function extend(target, source) {
for (var property in source) {
target[property] = source[property]
}
return target
}
function mix() {
var i = 1,
target = arguments[0],
len = arguments.length,
arg;
for (; i < len; i++) {
var arg = arguments[i]
for (var property in arg) {
targrt[property] = arg[property]
}
}
return target
}
当然,你也可以将它绑定到原生对象 Object 上,这样所有的对象就都可以实现多继承了
Object.prototype.mix = function () {
var i = 0,
len = arguments.length,
arg;
for (; i < len; i++) {
var arg = arguments[i]
for (var property in arg) {
this[property] = arg[property]
}
}
}
总结
这一篇文章主要讲述 JavaScript 面向对象编程的基础知识,也是后续五种设计模式的基础。接下来我会陆续总结五种设计模式,读者可以根据自己的兴趣选择性阅读。
-
结构型设计模式
-
行为型设计模式
-
技巧型设计模式
-
架构型设计模式