像同样基于原型编程的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
这段代码运行的时候,引擎是这样工作的:
- 首先,尝试遍历对象
a
中所有的属性,但是没有找到name
这个属性. - 查找
name
属性的这个请求被委托给了对象a
的构造器的原型,它被a._proto_
记录下来并指向A.prototype
,而后者刚好被设置为对象obj. - 在对象
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
读这段代码,引擎是这样工作的:
- 首先,遍历对象
b
的属性,但是没有找到name
这个属性; - 查找任务被委托给
b
的构造器的原型B.prototype
,但是还是没有; - 由于
B.prototype
是通过A
构造器构造的对象,所以这个任务被委托给构造器A的原型A.prototype
; - 在
A.prototype
中找到name属性,返回值.