普通对象与函数对象
JavaScript中,万物皆是对象。但对象也有区别。分为普通对象和函数对象
var o1 = {};
function f1(){}
在上述例子中,f1为函数对象,o1为普通对象。
var o1 = {};
console.log(o1.prototype); //undefined
console.log(o1.__proto__); //Object {}
var f1 = function(){}
console.log(f1.prototype); //f1 {}
console.log(f1.__proto__); //function() {}
[[Prototype]] 机制就是存在于对象中的一个内部链接。这个链接的作用是:如果在对象上没有找到需要的属性或者方法引用,引擎就会继续在 [[Prototype]] 关联的对象上进行查找。同理,如果在后者中也没有找到需要的引用就会继续查找它的 [[Prototype]] ,以此类推。这一系列对象的链接被称为“原型链”。
换句话说,JavaScript 中这个机制的本质就是对象之间的关联关系。
利用原型链实现“继承”
JavaScript主要通过原型链实现继承。原型链的构造是通过将一个类型的实例赋值给另一个构造函数的原型实现的。
//声明foo构造函数
function foo(name){
this.name = name;
}
//定义foo原型方法
foo.prototype.myName = function() {
return this.name;
};
//声明Bar构造函数
function Bar(name,label) {
foo.call( this, name );
this.label = label;
}
//定义Bar原型为一个空对象(空对象的原型指向foo.prototype);
Bar.prototype = Object.create( foo.prototype );
Bar.prototype.constructor = foo;
Bar.prototype.myLabel = function() {
return this.label;
};
var a = new Bar( "a", "obj a" );
a.myName();//foo原型上的方法
a.myLabel();//Bar原型上的方法
对象关联
Object.create(..) 会创建一个新对象( bar )并把它关联到我们指定的对象( foo ),这样我们就可以充分发挥 [[Prototype]] 机制的威力(委托)并且避免不必要的麻烦(比如使用 new 的构造函数调用会生成 .prototype 和 .constructor 引用)。
使用对象关联风格来编写功能与上述“继承”完全相同的代码
foo = {
init: function(name,label) {
this.name = name;
this.label = label;
},
myName: function() {
return "I am " + this.name;
}
};
Bar = Object.create( foo );
Bar.myLabel = function(){
return this.label;
};
var b1 = Object.create( Bar );
b1.init("a","obj a");
b1.myName();
b1.myLabel();
这段代码中我们同样利用 [[Prototype]] 把 b1 委托给 Bar 并把 Bar 委托给 Foo ,和上一段代码一模一样。我们仍然实现了三个对象之间的关联。但是非常重要的一点是,这段代码简洁了许多,我们只是把对象关联起来,并不需要那些既复杂又令人困惑的模仿类的行为(构造函数、原型以及 new )。
对象关联(对象之前互相关联)是一种编码风格,它倡导的是直接创建和关联对象,不把它们抽象成类。对象关联可以用基于 [[Prototype]] 的行为委托非常自然地实现。
在软件架构中你可以选择使用类和继承设计模式。也可以使用更少见但是更强大的设计模式:行为委托。