原型链

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__属性
  1. 构造函数里的prototype的__proto__,指向Object的prototype
console.log(Fn.prototype.__proto__ === Object.prototype);//true
  1. Object里的prototype的__proto__,为null
console.log(Object.prototype.__proto__);//null
console.log(Fn.prototype.__proto__.__proto__);//null
  1. 函数对象(注意不是用户自定义的Fn)的prototype与__proto__相等
console.log(Function.prototype === Function.__proto__);//true
  1. 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 () { ... }

 

你可能感兴趣的:(原型链)