原型模式
上一次讲到了构造函数模式,遗留了一个问题:
如果同一个方法被多个实例对象使用,那么创建的多个实例中
都存在相同的方法,而其实这个方法完全可以作为公共的方法
被某个类的所有实例对象共享。为此,我们创建了全局方法。
随之而来的问题是,如果这个方法是全局方法,那么岂不是所有
的对象都可以方法这个方法了吗?这有损封装的特性。最好是由
具体的某一类对象使用这个方法,或者这个方法是专门为这一类
实例对象准备的。还有一点问题,那就是如果这个类涉及到很多
方法,那岂不是要在全局作用域中定义很多方法吗?这不就是对
全局作用域的一种污染吗?如果学习过Java的同学,对于这个问
题应该很容易就想到解决方案,那就是继承。从继承的这个角度
去类比理解JavaScript中的原型模式比较简单。
构造函数模式
(实例中的相似方法完全可以实现共享)
<script type="text/javascript">
function Person() {
this.username = 'logic',
this.password = '12345',
this.login = function() {
console.log("hello, welcome!");
}
this.register = function() {
console.log("hello, register!");
}
}
</script>
从构造函数模式到原型模式的中间过渡版本
(将共享的方法转换成全局函数,解决了共享问题,但是过于暴露)
<script type="text/javascript">
function login() {
console.log("hello, welcome!");
}
function register() {
console.log("hello, register!");
}
function Person() {
this.username = 'logic',
this.password = '12345',
this.login = login;
this.register = register;
}
</script>
原型模式
<script type="text/javascript">
function Person() {};
Person.prototype.username = 'luohao';
Person.prototype.password = '123456';
Person.prototype.register = function() {
console.log("hello " + this.username + "!");
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.register == person2.register);
console.log(person1 instanceof Person);
</script>
其实原型相当于是父对象,父对象中的方法和属性可以被子对象
继承以及重写。prototype关键字在理解层面比较类似于Java中的
extends。
刚才一不小心说漏了嘴,提到了重写,其实这个也很好理解。
person1和person2中使用的username其实都是原型的属性。
如果在person1中自定义一个username属性,又会怎样呢?
此时使用username属性使用的到底是原型的username还是person1
自定义的username呢?
function Person() {};
Person.prototype.username = 'luohao';
Person.prototype.password = '123456';
Person.prototype.register = function() {
console.log("hello " + this.username + "!");
}
var person1 = new Person();
var person2 = new Person();
person1.username = "logic";
console.log(person1.username);
console.log(person2.username);
person1.username最终显示的不是luohao而是logic。
这其实就是重写的意思。子对象重写了父对象中重名的属性。
但是父对象中的属性并没有改变。只是影藏了。验证如下:
person2.username最终显示的是logic。
在这里需要引出一点:那就是属性搜索的问题:
首先对象会在当前作用域中搜索对象,如果存在那么就使用当前
作用域中的对象,如果不存在,那么就往上到它的原型中去搜索
以此类推,知道搜索到或者搜索不到为止。
当然我们可以使用hasOwnProperty()检测属性是在本身还是在原型中。
console.log(person1.hasOwnProperty('username'));
console.log(person1.hasOwnProperty('password'));
上一次讲到了构造函数模式,遗留了一个问题:
如果同一个方法被多个实例对象使用,那么创建的多个实例中
都存在相同的方法,而其实这个方法完全可以作为公共的方法
被某个类的所有实例对象共享。为此,我们创建了全局方法。
随之而来的问题是,如果这个方法是全局方法,那么岂不是所有
的对象都可以方法这个方法了吗?这有损封装的特性。最好是由
具体的某一类对象使用这个方法,或者这个方法是专门为这一类
实例对象准备的。还有一点问题,那就是如果这个类涉及到很多
方法,那岂不是要在全局作用域中定义很多方法吗?这不就是对
全局作用域的一种污染吗?如果学习过Java的同学,对于这个问
题应该很容易就想到解决方案,那就是继承。从继承的这个角度
去类比理解JavaScript中的原型模式比较简单。
构造函数模式
(实例中的相似方法完全可以实现共享)
<script type="text/javascript">
function Person() {
this.username = 'logic',
this.password = '12345',
this.login = function() {
console.log("hello, welcome!");
}
this.register = function() {
console.log("hello, register!");
}
}
</script>
从构造函数模式到原型模式的中间过渡版本
(将共享的方法转换成全局函数,解决了共享问题,但是过于暴露)
<script type="text/javascript">
function login() {
console.log("hello, welcome!");
}
function register() {
console.log("hello, register!");
}
function Person() {
this.username = 'logic',
this.password = '12345',
this.login = login;
this.register = register;
}
</script>
原型模式
<script type="text/javascript">
function Person() {};
Person.prototype.username = 'luohao';
Person.prototype.password = '123456';
Person.prototype.register = function() {
console.log("hello " + this.username + "!");
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.register == person2.register);
console.log(person1 instanceof Person);
</script>
其实原型相当于是父对象,父对象中的方法和属性可以被子对象
继承以及重写。prototype关键字在理解层面比较类似于Java中的
extends。
刚才一不小心说漏了嘴,提到了重写,其实这个也很好理解。
person1和person2中使用的username其实都是原型的属性。
如果在person1中自定义一个username属性,又会怎样呢?
此时使用username属性使用的到底是原型的username还是person1
自定义的username呢?
function Person() {};
Person.prototype.username = 'luohao';
Person.prototype.password = '123456';
Person.prototype.register = function() {
console.log("hello " + this.username + "!");
}
var person1 = new Person();
var person2 = new Person();
person1.username = "logic";
console.log(person1.username);
console.log(person2.username);
person1.username最终显示的不是luohao而是logic。
这其实就是重写的意思。子对象重写了父对象中重名的属性。
但是父对象中的属性并没有改变。只是影藏了。验证如下:
person2.username最终显示的是logic。
在这里需要引出一点:那就是属性搜索的问题:
首先对象会在当前作用域中搜索对象,如果存在那么就使用当前
作用域中的对象,如果不存在,那么就往上到它的原型中去搜索
以此类推,知道搜索到或者搜索不到为止。
当然我们可以使用hasOwnProperty()检测属性是在本身还是在原型中。
console.log(person1.hasOwnProperty('username'));
console.log(person1.hasOwnProperty('password'));