var o
=
new
Object
(
)
;
o
.name
=
'hang'
;
o
.age
=
21
;
o
.sayName
=
function
(
)
{
console
.
log
(
this
.name
)
;
}
;
o
.
sayName
(
)
;
var o
=
{
name
:
'hang'
,
age
:
21
,
sayName
:
funciont
(
)
{
console
.
log
(
this
.name
)
;
}
}
;
o
.
sayName
(
)
;
使用以上的方法创建对象很简单,但是缺点也是非常明显的,如果再次创建相同属性和方法的对象的时候,还要把代码复制一遍,会导致很多代码重复。
function
createObj
(name
,age
)
{
var o
=
new
Object
(
)
;
o
.name
=name
;
o
.age
=age
;
o
.sayName
=
function
(
)
{
console
.
log
(
this
.name
)
;
}
return o
;
}
var p1
=
createObj
(
'xuhang'
,
21
)
;
var p2
=
createObj
(
'Tomoya'
,
22
)
;
p1
.
sayName
(
)
;
p2
.
sayName
(
)
;
console
.
log
(
typeof p1
)
;
p1
instanceof
createObj
用工厂模式来创建对象,实现了一套属性和方法可以实现多个对象可以使用,可以传递参数,但主要缺点是无法识别对象类型。
function
CreateObj
(name
,age
)
{
this
.name
=name
;
this
.age
=age
;
this
.sayName
=
function
(
)
{
console
.
log
(
this
.name
)
;
}
;
}
var p1
=
new
CreateObj
(
'xuhang'
,
21
)
;
var p2
=
new
CreateObj
(
'Tomoya'
,
22
)
;
p1
.
sayName
(
)
;
console
.
log
(p1
instanceof
CreateObj
)
;
用构造函数模式,解决了工厂模式中不能判断对象类型的问题,还有必须要用new 关键字来创建对象。new关键字相当于执行了一下几步。
2.将构造函数的作用域赋值给这个新对象(因此this就是指向了这个新对象)
使用构造函数创建的对象都有一个constructor属性,该属性可以标识对象类型,但一般我们还是常用instanceof来判断对象类型, 因为constructor属性仅返回构造函数类型。
p1
.constructor
=== CreateObj
p1
.constructor
=== Object
p1
instanceof
createObj
p1
instanceof
Object
不使用new时,函数的作用域会是全局作用域,this就会指向window对象。
构造函数也存在问题,每个方法都要在实例上创建一遍。也就是说p1和p2的sayName()方法虽然作用相同,但这两个方法并不是同一个函数。
p1
.sayName
==p2
.sayName
原型模式解决了构造函数模式中同功能的方法的代码无法重用的问题。
我们创建的每个函数都有一个名为prototype(原型)的属性,这个属性是一个指针,指向一个对象,这个对象被称为原型对象。原型对象有一个名叫constructor的属性,这个属性是一个指针,指向构造函数。
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必再构造函数中定义对象实例的信息,而是可以将这些信息添加到原型对象中。
function
CreateObj
(
)
{
CreateObj
.prototype
.name
=
'xuhnag'
;
CreateObj
.prototype
.age
=
21
;
CreateObj
.prototype
.sayName
=
function
(
)
{
return
this
.name
;
}
;
}
var p1
=
new
CreateObj
(
)
;
p1
.name
;
var p2
=
new
CreateObj
(
)
p2
.name
;
说明一下以上例子,p1的name属性,如果自身有name属性,就会返回自身的值,如果没有的话,则查找原型对象中有没有name属性,若原型对象中有name属性,则返回其值,否则,就报错。
p1
.name
=
'许航'
;
p1
.name
;
p2
.name
;
可以通过hasOwnProperty()方法检测一个属性是存在于具体的对象中,还是存在于该对象的原型对象中。
p1
.
hasOwnProperty
(
'name'
)
;
p2
.
hasOwnProperty
(
'name'
)
;
也可以通过 in 关键字来判断一个属性是否存在于具体的对象或者该对象的原型对象中。
'name'
in p1
'name'
in p2
原型模式存在的问题,它省略了为构造函数传递初始化参数,这给程序上造成了一定的不便,另外最主要的是,当我们改变值为引用类型的对象的属性时,这个改变的结果会被其他对象共享。
function
CreateObj
(
)
{
}
CreateObj
.prototype
=
{
constructor
:CreateObj
,
name
:
'xuhang'
,
age
:
21
,
family
:
[
'爸爸'
,
'妈妈'
]
,
sayName
:
function
(
)
{
console
.
log
(
this
.name
)
;
}
}
;
var p1
=
new
CreateObj
(
)
;
var p2
=
new
CreateObj
(
)
;
p1
.family
.
push
(
'妹妹'
)
;
console
.
log
(p2
.family
)
;
构造函数模式的缺点就是无法共享方法,原型模式的方法没问题,缺点就是当原型对象属性的值引用类型时,对其进行修改会反映到所有实例中。
将两者结合起来,对象的属性使用构造函数的模式创建,对象的方法用原型模式来创建。
function
CreateObj
(name
.age
)
{
this
.name
=name
;
this
.age
=age
;
this
.family
=
[
'爸爸'
,
'妈妈'
]
;
}
CreateObj
.prototype
=
{
constructor
:CreateObj
,
sayName
:
function
(
)
{
console
.
log
(
this
.name
)
;
}
}
var p1
=
new
CreateObj
(
'xuhang'
,
21
)
;
var p2
=
new
CreateObj
(
'Tomoya'
,
22
)
;
p1
.family
.
push
(
"妹妹"
)
;
console
.
log
(p1
.family
)
;
console
.
log
(p2
.family
)
;
非常简单的继承关系,实例是子类的实例,也是父类的实例。父类新增的原型属性和原型方法,子类都能访问到。原理:是将父类的实例作为子类的原型。
function
Cat
(name
,age
)
{
this
.name
=name
;
this
.age
=age
;
this
.showName
=
function
(
)
{
console
.
log
(
"我叫"
+name
+
",你们好!"
)
;
}
}
Cat
.prototype
.showEat
=
function
(food
)
{
console
.
log
(
this
.name
+
"正在吃"
+food
)
;
}
var cat1
=
new
Cat
(
'Ami'
,
2
)
;
function
Dog
(
)
{
}
Dog
.prototype
=
new
Cat
(
)
;
var dog
=
new
Dog
(
'jod'
,
3
)
;
console
.
log
(dog
.
showName
(
)
)
;
但是缺点也是很明显,要想为子类新增属性和方法,必须要在new Cat()这样的语句之后执行,不能放到构造器中。创建子类实例时,无法向父类构造函数传参。
还有个致命的缺点,当我们改变值为引用类型的原型对象的属性时,这个改变的结果会被所有子对象共享。
解决了原型链继承中,子类实例共享父类引用属性的问题创建子类实例时,可以向父类传递参数可以实现多继承(call多个父类对象)。原理:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function
Cat
(name
,age
)
{
this
.name
=name
;
this
.age
=age
;
this
.showName
=
function
(
)
{
console
.
log
(
"我叫"
+name
+
",你们好!"
)
;
}
}
Cat
.prototype
.showEat
=
function
(food
)
{
console
.
log
(
this
.name
+
"正在吃"
+food
)
;
}
function
Dog
(name
,age
)
{
Cat
.
call
(
this
,name
,age
)
;
}
var dog1
=
new
Dog
(
'job'
,
3
)
;
var dog2
=
new
Dog
(
'job'
,
2
)
;
dog1
.
showName
(
)
;
dog1
.
showEat
(
'鱼'
)
;
console
.
log
(dog1
instanceof
Cta
)
;
console
.
log
(dog1
.showName
=== dog2
.showName
)
缺点有如下几点,实例并不是父类的实例,只是子类的实例。只能继承父类的实例属性和方法,不能继承原型属性/方法。无法实现函数复用,父类中的方法在每个子类中都会生成一遍,父类中的方法没有被复用。
不限制调用方式,返回的对象具有相同的效果。也可以继承原型属性/方法。
function
Cat
(name
,age
)
{
this
.name
=name
;
this
.age
=age
;
this
.showName
=
function
(
)
{
console
.
log
(
"我叫"
+name
+
",你们好!"
)
;
}
}
Cat
.prototype
.showEat
=
function
(food
)
{
console
.
log
(
this
.name
+
"正在吃"
+food
)
;
}
function
Dog
(name
,age
)
{
var instance
=
new
Cat
(
)
;
instance
.name
=name
;
instance
.age
=age
;
return instance
;
}
var dog
=
new
Dog
(
'job'
,
3
)
;
console
.
log
(dog
instanceof
Cat
)
;
console
.
log
(dog
instanceof
Dog
)
;
dog
.
showEat
(
'fish'
)
;
缺点是,实例是父类的实例,不是子类的实例,不支持多继承。
组合继承就是将原型链继承和借用构造方法继承组合,发挥两者之长。原理:通过调用父类构造,继承父类的属性并保留传参,然后通过将父类实例作为子类原型。
可以继承实例属性/方法,也可以继承原型属性/方法。
function
Cat
(name
,age
)
{
this
.name
=name
;
this
.age
=age
;
this
.showName
=
function
(
)
{
console
.
log
(
"我叫"
+name
+
",你们好!"
)
;
}
}
Cat
.prototype
.showEat
=
function
(food
)
{
console
.
log
(
this
.name
+
"正在吃"
+food
)
;
}
function
Dog
(name
,age
)
{
Cat
.
call
(
this
,name
,age
)
;
}
Dog
.prototype
=
new
Cat
(
)
;
var dog
=
new
Dog
(
'job'
,
3
)
;
console
.
log
(dog
instanceof
Cat
)
;
console
.
log
(dog
instanceof
Dog
)
;
dog
.
showName
(
)
;
dog
.
showEat
(
'fish'
)
;
组合继承没什么太大的问题,只是调用了两次父类构造函数,仅仅消耗多了点内存。
转载: http://xhwhen.coding.me/2017/05/07/JavaScript面向对象笔记和总结/
想学习更多IT知识 可加群:272292492