原型链,__proto__和prototype

453 阅读2分钟

构造函数,原型和实例的关系:

每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针。

__proto__和prototype的区别

prototype 只有函数才有的属性 __proto__是每一个对象都有的属性

原型链

由于 __proto__是任何对象都有的属性,而js,万物皆对象,所以会形成一条__proto__连起来的链条,递归访问__proto__必须最终到头,并且值为null。

  • 一般来说,__proto__ === constructor.prototype
  • 当访问一个属性,会首先检索自身的属性,若自身没有,则会沿着__proto__向上寻找,一层一层地寻找,直到寻到 null ,这里的原型链就是实例对象的__proto__属性。
function A(name){
  this.test = name;
};
A.prototype.getName = function () {
  return console.log(this.test);
}
var a = new A('a');
function B() {};
// B.prototype.constructor = B; // 注意这个
B.prototype = new A('b');
var c = new B();

这是没有设置B.prototype.constructor = B的各种情况的结果;

c.constructor ===  function A(name) {
  this.test = name;
}
B.prototype.constructor ===  function A(name) {
  this.test = name;
}
B.constructor ===  function Function() { [native code] }
a.__proto__ ===  A { getName: [Function] }
new B().__proto__ ===  A { test: 'b' }
B.prototype ===  A { test: 'b' }
c.__proto__ ===  A { test: 'b' }
B.__proto__ ===  function () { [native code] }
A.__proto__ ===  function () { [native code] }
B.prototype.__proto__ ===  A { getName: [Function] }

设置B.prototype.constructor = B的各种情况的结果; (之所以要设置B.prototype.constructor = B,是为了使B的实例在原型链上不混乱)

c.constructor ===  function B() {}
B.prototype.constructor ===  function B() {}
B.constructor ===  function Function() { [native code] }
a.__proto__ ===  A { getName: [Function] }
new B().__proto__ ===  B { test: 'b', constructor: [Function: B] }
B.prototype ===  B { test: 'b', constructor: [Function: B] }
c.__proto__ ===  B { test: 'b', constructor: [Function: B] }
B.__proto__ ===  function () { [native code] }
A.__proto__ ===  function () { [native code] }
B.prototype.__proto__ ===  A { getName: [Function] }

修改构造函数原型,对实例的影响

  1. 在new之前修改,无影响

  2. 在new之后修改

    function Foo() {}
    Foo.prototype.flag = 123;
    function fn() {}
    //先new实例
    var foo = new Foo();
    var f = new fn();
    //先再改变原型指向
    Foo.prototype = f;
    console.dir(foo);//Foo
    console.log(foo.__proto__ === f);//false foo的原型链不会因为构造函数原型的变化而变化。
    console.log(Foo.prototype === f);//true
    console.log(foo instanceof Foo);//false
    console.log(foo.__proto__ === Foo.prototype);//false
    

    a. 实例产生之后,修改构造函数的原型,对实例不造成影响,因为实例化之后,修改构造函数原型,并不会改变实例对象指向的原来的构造函数的原型,也就是说,原来的构造函数原型还存在内存里面。修改后的构造函数原型可视为一个新的构造函数,不对以前实例化的对象产生影响。

    b. instanceof 实在原型链上查找,看是否能找到构造函数的原型,即判断__proto__属性与构造函数的prototype属性是否相等。从a可看出,很明显是不等。