首先放在下博客的原文网址:http://liusihao.com/post/77483884517/javascript
##1, 原型编程的优缺点
JavaScript 是一个基于对象的原型 (Prototype) 语言。
什么是原型?我们来看看下面的代码:
function People() {};
People.prototype = {
name: "White Crow"
age: "23"
run: function () {
return name + age
};
};
var person = new People();
alert(person.run()) //White Crow23
这就是一个 JavaScript 构造原型和实例化原型的过程,乍一看和面向对象中构造类和实例化为对象的过程非常相似,但是原型编程和面向对象(OOP)编程的区别到底是什么呢?它会导致哪些问题呢?
首先原型是共享的,如果修改原型:
People.prototype = {
age: "200"
}
var person2 = new People()
再运行 person2.run(),则会返回 undefined,整个 People.prototype 都被覆盖了。 你不能给原型的属性附加不同的值。相反,OOP 中,每个对象的属性值都可以是不同的,OOP 中 person1.name 可以是 Jack,person2.name 可以是 Tom,原型编程中,person1.name 和 person2.name 都是同一个值,属性值是共享的。
这就是 JavaScript 中原型的缺点(但同时也是它最大的优点),属性和方法的共享。因此原型的数据抽象能力相比于 OOP 中的类差了很多。
有人问了,在 JS 的原型编程中,难道不能把值当做参数传给对象的属性吗?这样每个对象都可以拥有不同的属性值了。答案是不行,因为 People.prototype 的构造函数不能传参。
##2, 运用原型 + 构造函数组合模式解决内存和数据抽象问题。
为了解决传参的问题,可以组合用构造函数 + 原型模式
//不共享,使用构造函数,解决了数据抽象问题
function People(name, age){
this.name = name;
this.age = age;
};
//共享,使用原型模式,节约内存
People.prototype = {
run: function(){
return name + age
};
};
为什么 run 要使用原型模式创建,因为原型方法和属性在内存中是共享的,可以用于节约内存,不会因为 new 一个对象,就开辟一块内存用于存放相同的方法。
这种模式解决了数据抽象和节约内存的问题,是比较好的解决方法。
##3, 动态原型模式
原型模式和构造函数的组合模式解决了这两个问题后,我们又发现了一个问题,那就是它们没有 OOP 中的封装性。看起来比较乱,没有将信息都封装在函数体内。 那么我们可以这么做,解决封装问题:
function People(name, age){
this.name = name
this.age = age
People.prototype.run = function(){
return this.name + this.age
};
};
但是这么做,因为多次 new People 时,会初始化多次 run,为了不让它初始化多次,我们可以再添加一个条件判断语句解决这个问题:
function People(name, age){
this.name = name
this.age = age
if (typeof this.run != 'function'){
People.prototype.run = function(){
return this.name + this.age
};
};
};
##4, JS 中对象查找属性和方法的链条。
JS 中,一个对象首先会查找它的构造函数的属性和方法
举例:
var box1 = {name : 'a'};
box1.name
//output 'a'
box1
//output Object {name: "a"}
var box1 = {name : 'a'}; 这一段实际是创建了一个构造函数为 Object,属性为 name: 'a'的对象。所以当你运行 box1 时,它会返回 Object {name: "a"}。
如果你这时候添加 box1 的构造函数 (Object) 的原型:
Object.prototype.name = "b"
Object.prototype.age = 99
box1.name
//output "a"
box1.age
//output 99
所以我们可以发现,一个对象是先从自己的构造函数来查找自己的属性,接着从构造函数原型中查找自己的属性。Object 中找得到 name,那么用 Object 的 name,Object 找不到 age,就用 Object.prototype 里的 age。
我们总结出一条查找属性的原则,就是“就近原则”,实例里有属性,就返回,没有才去查找原型,如果还没有则找到实例的类的超类,查找超类的属性,如果超类的属性没有,就查找超类的原型,一直往上到顶层的 Object 对象。
##5, 原型链
如果连原型里都找不到自己的属性呢,则解释器会继续向构造函数的构造函数的原型,直到上升到根函数的原型(Object.prototype)也找不到为止,这时候返回 underfined。我们来举个例子:
function People(name) = {
this.name = name
}
People.prototype.age = 99
Object.prototype.height = 185
person = new People('Jack')
person.name //output 'jack'
person.age //output 99
person.height //output 185
person.weight //undefined
这个就被称为 JS 的原型链。
##6, 继承 继承在 JS 中用原型链来实现:
function Box(){
this.name = "Jack"
}
function Desk(){
this.age = 100
}
Desk.prototype = new Box(); //Desk继承了Box,通过原型形成了链条。
var desk = new Desk();
alert(desk.name); //Jack
alert(desk.age); //100
function Table(){
this.name = "Tom"
}
Table.prototype = new Desk(); //Table继承了Desk
var table = new Table();
alert(table.name); //output "Tom"
##7 对象冒充继承
待续
欢迎拍砖,如果觉得写得好,请点击下面的 喜欢 :D