function Fn() {
this.foo = ‘haha’;
} // Fn为构造函数
var f1 = new Fn(); // f1是Fn构造函数创建的对象
__proto__属性:
在创建对象的时候,都会有一个属性__proto__,它指向构造函数的原型对象prototype。
console.log(f1.__proto__ === Fn.prototype); // true
原型对象:
每个函数下都有一个子对象prototype,它称为原型对象。它就是指代该构造函数的原型。只有函数才有prototype。当通过new创建一个实例对象的时候,prototype对象的成员都会成为实例化对象的成员。
console.log(Fn.prototype.constructor === f1.constructor); // true
扩展
基础知识明白之后,继续扩展:
在prototype中,也同样有__proto__属性
- 构造函数里的prototype的__proto__,指向Object的prototype
console.log(Fn.prototype.__proto__ === Object.prototype);//true
- Object里的prototype的__proto__,为null
console.log(Object.prototype.__proto__);//null
console.log(Fn.prototype.__proto__.__proto__);//null
- 函数对象(注意不是用户自定义的Fn)的prototype与__proto__相等
console.log(Function.prototype === Function.__proto__);//true
- Object.__proto__与Function.prototype相等
console.log(Object.__proto__ === Function.__proto__);//true
console.log(Function.prototype.__proto__ === Object.prototype); // true
Object.constructor === Function; // true
原型对象的作用:
主要作用用于继承。我们可通过对prototype设置一个函数对象的属性,使得后续通过该函数创建的对象,获得这个属性。例如
var person = function(name) {
this.name = name;
}
person.prototype.getName = function() {
return this.name;
}
var zhangsan = new person(‘Zhang san’);
zhangsan.getName(); // Zhang san
原型链:
定义:原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链。
原型链如何允许对象之间继承特性、prototype 属性?如何通过它来向构造器添加方法?
1. 如何允许对象之间继承特性、prototype 属性?
function Fn() {
}
console.log(Fn.prototype);
运行上述程序,可以看到,prototype原型对象中有constructor和__proto__。
{
constructor: ƒ Fn(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
添加属性到prototype中:
function Fn() {
}
Fn.prototype.foo = 'bar';
console.log(Fn.prototype)
结果:
{
foo: "bar",
constructor: ƒ Fn(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
通过Fn函数创建一个对象,并添加新的属性:
function Fn() {
}
Fn.prototype.foo = 'bar';
var f = new Fn();
f.type = 'rangle';
console.log(f);
结果:
{
type: "rangle",
__proto__: {
foo: "bar",
constructor: ƒ Fn(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
}
__proto__指向的是Fn构造函数,在创建f对象之前,通过构造函数的prototype原型赋给了foo属性,那么如果执行f.foo,能否获得对应的值呢?
function Fn() {
}
Fn.prototype.foo = 'bar';
var f = new Fn();
f.type = 'rangle';
console.log(f.foo);
console.log(f.type)
结果:
bar
rangle
当访问一个对象的属性时,浏览器首先会查找该对象是否有这个属性,如果没有在该对象中找到,则会在它的__proto__中去找,如果这个__proto__也没有,则又在__proto__的__proto__去找,以此类推,最终的Object.__proto__是null,原型链上面的所有的__proto__都被找完了, 浏览器所有已经声明了的__proto__上都不存在这个属性,然后就得出结论,这个属性是 undefined。
示例:
function Fn() {
}
Fn.prototype.foo = 'bar';
var f = new Fn();
f.type = 'rangle';
console.log(f.foo);
console.log(f.type);
var f2 = new Fn();
console.log(f2.foo);
console.log(f2.type);
结果:
bar
rangle
bar
undefined
2. 如何通过它来向构造器添加方法?
每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。
事实上,一种极其常见的对象定义模式是,在构造器(函数体)中定义属性、在 prototype 属性上定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,代码更具可读性。我们可以通过prototype,向构造器添加方法, 例如:
// 构造器及其属性定义
function Test(a,b,c,d) {
// 属性定义
};
// 定义第一个方法
Test.prototype.x = function () { ... }
// 定义第二个方法
Test.prototype.y = function () { ... }