原型模式和基于原型继承的js对象系统

时间:2023-03-08 21:17:02

像同样基于原型编程的Io语言一样,javascript在原型继承方面,实现原理和Io非常类似,javascript也遵守这些原则

  • 所有数据都是对象

  • 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它

  • 对象会记住它的原型

  • 如果对象无法响应某个请求,它会把这个请求委托给它自己的原型.


一 , 所有数据都是对象

javascript模仿了java引入两套类型机制:基本型和对象类型.

基本型有:undefined,number,string,Boolean,function,object.

javascript中的根对象是object.prototype,它是一个空的对象.javascript中的每个对象都是克隆自它.object.prototype是他们的原型

var obj1 = new Object();
var obj2 = {};
console.log( Object.getPrototypeOf( obj1 ) === Object.prototype ); // 输出:true
console.log( Object.getPrototypeOf( obj2 ) === Object.prototype ); // 输出:true

二 , 要得到一个对象,不是通过实例化,而是找到一个对象作为原型并克隆它.

运用new运算符从构造器中得到一个对象,比如:

function Person(name){
this.name = name;
} person.prototype.getName = function (){
return this.name;
} var a = new Person('cat'); console.log(a.name);//cat
console.log(a.getName() );//cat
console.log(object.getPrototypeOf(a) === Person.prototype);//true

Person并不是类,而是构造器.js的函数可以作为普通函数被调用,也可以作为构造器被调用.当使用new运算符来调用函数时,此时函数就是一个构造器.

在Chrome和Firefox等向外暴露了对象_proto_属性的浏览器下,我们可以通过以下代码来理解new运算的过程

function Person(name){
this.name = name;
} person.prototype.getName = function (){
return this.name;
} var objectFactory = function(){
var obj = new Object(), //从Object.prototype上克隆一个空对象
Constructor = [].shift.call(arguments);//取得外部传入的构造器,此例是Person
obj._proto_ = Constructor.prototype; //指向正确的原型
var ret = Constructor.apply( obj, arguments );// 借用外部传入的构造器给 obj 设置属性
return typeof ret === 'object' ? ret : obj;// 确保构造器总是会返回一个对象
};
var a = objectFactory( Person, 'sven' );
console.log( a.name ); // 输出:sven
console.log( a.getName() ); // 输出:sven
console.log( Object.getPrototypeOf( a ) === Person.prototype );// 输出:true

三 , 对象会记住它的原型

对js来说,不能说对象有原型,只能说对象的构造器有原型."对象把请求委托给它自己的原型"这句话,更好的说法是对象把请求委托给它的构造器的原型.

js给对象提供了_proto_的隐藏属性,这个属性指向它的构造器的原型对象,即{constructor}.prototype.

var a = new Object();
console.log(a._proto_ === object.prototype);//true

实际上,_proto_就是对象与"对象构造器的原型"联系起来的纽带.

obj._proto_ = Constructor.prototype;
//通过这种方式来改变默认指向的object.prototype

四 , 如果对象无法响应某个请求,它会把这个请求委托给它的构造器原型

js中对象最初都是由Object.prototype对象克隆而来,但是构造器的原型不仅限于object.prototype,而是可以动态指向其他对象的.利用这一特性,

当对象a需要借用b的能力时,可以让a的构造器原型指向对象b,从而达到继承效果.

第一个例子:

var obj = {name:'nike'};

var A = function(){};
A.prototype = obj; var a = new A();
console.log(a.name);//nike

这段代码运行的时候,引擎是这样工作的:

  1. 首先,尝试遍历对象a中所有的属性,但是没有找到name这个属性.
  2. 查找name属性的这个请求被委托给了对象a的构造器的原型,它被a._proto_记录下来并指向A.prototype,而后者刚好被设置为对象obj.
  3. 在对象obj中找到了name属性,并返回它的值.

第二个例子:

var A = function(){};
A.prototype = {name:'nike'}; var B = function(){};
B.prototype = new A(); var b = new B();
console.log(b.name);//nike

读这段代码,引擎是这样工作的:

  1. 首先,遍历对象b的属性,但是没有找到name这个属性;
  2. 查找任务被委托给b的构造器的原型B.prototype,但是还是没有;
  3. 由于B.prototype是通过A构造器构造的对象,所以这个任务被委托给构造器A的原型A.prototype;
  4. A.prototype中找到name属性,返回值.