1. 背景
由于js是解释执行的语言,那么代码中出现的函数与对象,如果重复执行,会创建多个副本,例如,传统的构造方法的定义方式会影响性能,容易造成多个对象有多个方法副本,应该将方法单独抽取出来,但是会有安全隐患,例如,在开发中会引入各种框架和库,自定义成员越多,出现命名冲突的几率越大,代码繁冗不易维护,最好的解决方法应该是外面的函数不占用名字。
<script type="text/javascript">
function sayHello() {} // 函数体放在构造函数之外,构造函数中只需要引用该函数即可
function Foo() {
this.say = sayHello;
}
</script>
2. 概念:任意一个对象都会默认的连接到它的原型中。
创建一个函数,会附带的创建一个特殊的对象,该对象使用函数.prototype引用,称其为函数的原型属性;每一个由该函数作为构造函数创建的对象,都会默认的连接到该对象上;在该对象访问某一个方法或属性的时候,如果该对象中没有,就会到这个神秘对象prototype中去查找。
<script type="text/javascript">
// 由构造函数创建出来的众多函数对象共享一个对象,就是构造函数.prototype
function Foo() {}
Foo.prototype.sayHello = function() {
console.log("...");
};
var f1 = new Foo(); // Foo()里什么都没有
f1.sayHello(); // 如果f1没有sayHello,那么就会在Foo.prototype中去找
var f2 = new Foo();
f2.sayHello();
console.log(f1.sayHello === f2.sayHello); // true
</script>
3. 区分概念
神秘对象针对构造函数称为“原型属性”,神秘对象针对构造函数创建出来的对象称为“原型对象”,构造函数创建的对象继承自构造函数的原型属性,构造函数创建的对象继承自该对象的原型对象,构造函数所创建出来的对象与构造函数的原型属性表示的对象是两个不同的对象,原型中的成员,可以直接被实例对象所使用。
<script>
function Person() {}
var p = new Person(); // 执行new创建对象,执行构造函数Person初始化对象,给对象添加属性,赋值给变量p
// 现在p表示的对象默认连接到Person.prototype
Person.prototype.good = function() {alert('Karry');};
p.good();
</script>
4. 使用原型
利用对象的动态特性:构造函数.prototype.xxx = vvv;在原来的基础上添加。
直接替换:Student.prototype = {};相当于var o = {};o = {};再次赋值。
<script>
function Person() {}
Person.prototype.func = function() {
console.log('111');
};
var p1 = new Person();
Person.prototype = {
func: function() {
console.log('222');
}
};
var p2 = new Person();
p1.func(); // 111
p2.func(); // 222
</script>
利用直接替换修改,原型的指向发生了变化,构造函数所创建的对象所继承的原型不同,新增的对象默认是没有constructor属性的,所以在使用替换方式修改原型的时候,一般都会添加constructor属性。
<script>
function Person() {}
Person.prototype = {
constructor: Person; // 增加一个指向构造函数的引用
};
</script>
5. _proto_
<script>
function Person() {}
var o = new Person(); // o._proto_ === Person.prototype
// 神秘函数默认有一个属性'constructor',即构造器,表示该原型是与什么构造函数联系起来的
// _proto_可以访问原型,但在开发中除非有特殊要求,不要使用实例去修改原型的成员,因此属性开发时使用很少
// 但是调试时非常方便,可以轻易访问原型查看成员,否则无法通过实例访问原型
o.constructor.prototype // 实例对象访问构造器,使用构造器访问原型
</script>
6. constructor
有时候根据需要在构造函数内部还会调用构造函数,在这种情况下,不要使用构造函数的名字,而应该使用this.constructor()来表示构造函数。
7. 原型实例三角形
<script>
function Person(name) {
this.name = name;
}
var p = new Person('Karry');
</script>
8. 原型继承案例
<script>
// 实现一个自定义的集合(数组)
function myCollection () {}
myCollection.prototype = []; // 提供数组的方法为其添加成员,Array.prototype
// myCollection.prototype = Array.prototype;
// myCollection.prototype.sum = function() {}; // 污染系统Array
var arr = new myCollection();
arr.push( 123, 456 ); // push、pop、shift、unshift
</script>