今天继续巩固基础,看下创建对象的几种方法和优缺点,以及继承的几种方法和优缺点吧
创建对象的多种方法和优缺点
工厂函数
function createPersn(name) {
var o = new Object()
o.name = name
o.getName = function () {
console.log(this.name)
}
return o
}
var person1 = createPerson("randy")
从我们创建的过程可以看到这种方式非常的简单,但是有一定的缺陷,那就是对象无法识别,因为所有的实例都指向了一个原型
- 优点:简单
- 缺点:所有的实例都指向了一个原型
构造函数模式
function Person(name) {
this.name = name
this.getName = function () {
console.log(this.name)
}
}
var person1 = new Person("randy")
- 优点:实例可以识别为一个特定的类型
- 缺点:每次创建实例时,每个方法都要创建一次
优化
function Person(name) {
this.name = name
this.getName = getName
}
function getName() {
console.log(this.name)
}
var person1 = new Person("randy")
解决了每个方法都要被重新创建的问题,但是每次都要返回一个新的实例
原型模式
function Person(name) {}
Person.prototype.name = "randy"
Person.prototype.getName = function () {
console.log(this.name)
}
var person1 = new Person()
- 优点:方法不会被重新创建
- 缺点:
- 所有的属性和方法都被共享
- 不能初始化参数
原型优化1
function Person(name) {}
Person.prototype = {
name: "randy",
getName: function () {
console.log(this.name)
}
}
var person = new Person()
- 优点:封装了清晰点
- 缺点:重写了原型 constructor属性丢失
原型优化2
function Person(name) {}
Person.prototype = {
constructor: Person,
name: "randy",
getName: function () {
console.log(this.name)
}
}
var person = new Person()
- 优点:实例可以通过constructor属性找到所属的构造函数
- 缺点:
- 所有的属性和方法都共享
- 不能初始化参数
- 原型链断了
组合模式
function Person(name) {
this.name = name
}
Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name)
}
}
var person = new Person()
- 优点:该共享的共享,该私有的私有
- 缺点:希望写在一个地方,即更好的封装性
动态原型模式
function Person(name) {
this.name = name
if (typeof this.getName != "function") {
Person.prototype.getName = function () {
console.log(this.name)
}
}
}
var person = new Person()
注意: 使用动态原型模式时,不能用对象字面量重写原型
function Person(name) {
this.name = name
if (typeof this.getName != "function") {
Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name)
}
}
}
}
var person1 = new Person("randy")
var person2 = new Person("mick")
person1.getName() // 报错,并没有该方法
new的执行步骤:
- 首先创建一个对象
- 将对象的原型指向Person.prototype
- Person.apply(obj)
- return 对象
执行 apply 的时候,会执行 obj.Person,if 判断的时候,构造函数的 prototype 属性指向了实例的原型,使用字面量方式覆盖 Person.prototype 并不会更改实例的原型的值,person1 依然指向了以前的原型
继承的多种方式&优缺点
原型链继承
function Parent() {
this.name = "randy"
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child() {}
Child.prototype = new Parent()
var child1 = new Child()
child1.getName()
问题:
- 引用类型的属性被所有的实例共享
- 不能给父构造函数传参
function Parent() {
this.names = ["randy", "mick"]
}
function Child() {}
Child.prototype = new Parent()
var child1 = new Child()
child1.names.push("hahah")
console.log(child1.names) //[ 'randy', 'mick', 'hahah' ]
var child2 = new Child()
console.log(child2.names) // [ 'randy', 'mick', 'hahah' ]
借用构造函数
function Parent() {
this.names = ["randy", "mick"]
}
function Child() {
Parent.call(this)
}
var child1 = new Child()
child1.names.push("hahah")
console.log(child1.names) // [ 'randy', 'mick', 'hahah' ]
var child2 = new Child()
console.log(child2.names) // [ 'randy', 'mick' ]
- 避免了引用类型的所有属性被所有实例共享
- 可以在Child中向Parent传参
function Parent(name) {
this.name = name
}
function Child(name) {
Parent.call(this, name)
}
var child1 = new Child("randy")
console.log(child1.name) // randy
var child2 = new Child("mick")
console.log(child2.name) // mick
缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法
组合继承
function Parent(name) {
this.name = name
this.colors = ["red", "blue", "green"]
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child(name, age) {
Parent.call(this, name)
this.age = age
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
var child1 = new Child("randy", 18)
child1.colors.push("black")
console.log(child1.name) //randy
console.log(child1.age) // 18
console.log(child1.colors) //[ 'red', 'blue', 'green', 'black' ]
var child2 = new Child("mick", 20)
console.log(child2.name) //mick
console.log(child2.age) // 20
console.log(child2.colors) //[ 'red', 'blue', 'green']
组合继承融合了原型链继承和构造函数的优点
原型继承
function createObj(o) {
function F() {}
F.prototype = o
return new F()
}
缺点:包含引用类型的属性值始终都会共享相应的值
var person = {
name: "randy",
friends: ["mick", "qin"]
}
var person1 = createObj(person)
var person2 = createObj(person)
person1.name = "person1"
console.log(person2.name) // randy
person1.friends.push("ran")
console.log(person2.friends) // [ 'mick', 'qin', 'ran' ]
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象
function createObj(o) {
var clone = Object.create(o)
clone.sayName = function () {
console.log("hi")
}
return clone
}