首先
- JS中的对象都具有__proto__属性
- Function是特殊的对象,除了拥有__proto__属性,还有特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,而这个对象的用途包含可以由特定类型的所有实例共享的属性和方法
举个例子:
function Person( name ){
this.name = name;
};
Person.prototype.getName = function(){
return this.name;
};
var p1 = new Person('Amy');
console.log(p1.name); // 输出:"Amy"
console.log(p1.getName()); // 输出:"Amy"
console.log( Object.getPrototypeOf( p1 ) === Person.prototype ); // 输出: true
由以上例子可以看出:
- 实例 p1 可以访问到构造函数 Person 里的属性
- 实例 p1 访问到 Person.prototype 中的属性
下图为对象之间的关系:
模拟new关键字创建对象(这段代码来自《JavaScript设计模式与开发实践》):
var objectFactory = function() {
var obj = new Object(); /** 从 Object.prototype 上克隆一个空的对象 **/
var Constructor = [].shift.call(arguments); /** 取得外部传入的构造器,本例中为 Person **/
/**
让 obj.__proto__ 指向 Person.prototype, 取代原来的 Object.prototype
**/
obj.__proto__ = Constructor.prototype;
var ret = Constructor.apply(obj, arguments); /** 借用外部传入的构造器给 obj 设置属性 **/
return typeof ret === 'object' ? ret : obj; /** 确保构造器总是会返回一个对象 **/
};
var a = objectFactory( Person, 'Alex' );
console.log( a.name ); // 输出: Alex
console.log( a.getName() ); // 输出: Alex
console.log( Object.getPrototypeOf( a ) === Person.prototype ); // 输出: true
- 用new Object() 的方式新建了一个对象 obj (此时obj原型指向Object.prototype);
- 取出第一个参数,就是我们要传入的构造函数(本例中为 Person)。此外因为 shift会修改原数组,所以 arguments 会被去除第一个参数;
- 将 obj 的原型指向构造函数(Person.prototype),这样 obj就可以访问到构造函数原型中的属性;
- 使用 apply,改变构造函数 this 的指向到新建的对象(这样 obj就可以访问到构造函数中的属性),给 obj 设置属性;
- 返回 obj,并确保构造器总是会返回一个对象