原型与原型链

103 阅读2分钟

原型与原型链是一个比较抽象的东西,因为平时工作中很少用到,所以学了也容易忘。针对这种抽象的概念,最好是用我们熟悉的概念去理解它。下面,我举一个例子:

假设浙大学生的构造函数,叫function ZJUer(),JS中每个构造函数都有一个天然存在的原型对象prototype,它就像一个藏宝库一样,储存了这个构造函数的公共属性和方法,对于ZJUer()来说,即每个浙大在校学生都拥有的权利,如下图:

image.png

假如小明是浙大的学生:

    function ZJUer(name, age) {
        this.name = name;
        this.age = age;
    }

    ZJUer.prototype.studentCard = '浙江大学学生证';
    ZJUer.prototype.borrowBook = function (bookName) {
        console.log('借书成功:' + bookName);
    }

    let xiaoming = new ZJUer('小明', 18);

    console.log(xiaoming.studentCard);
    xiaoming.borrowBook('世界文明史');

代码运行结果:

image.png

上面的代码中,我们在ZJUer()这个构造函数的prototype中添加了属性studentCard和borrowBook,然后实例化了一个学生小明,然后对小明进行了访问这两个属性的操作,运行结果说明,对于小明来说,这两个属性都是能被访问到的。但是单看这个实例对象本身,是看不到这两者的,只能看到它的私有属性name和age:

image.png

那么为什么访问studentCard和borrowBook的时候没有报错呢?这里就涉及到一个很重要的知识点:如果我们要访问一个对象的某一个属性或方法,会首先在它的私有属性中查找,如果没有找到,会顺着__proto__原型链继续查找,而实例对象的__proto__就是构造函数的prototype。也就是去ZJUer().prototype里去找,果然找到了,就会返回这两个属性。(下图中[[Prototype]]就是__proto__)

image.png

如果我们当时没有在ZJUer()的属性库里设置这两个属性怎么办呢?没关系,ZJUer.prototype说白了就是一个对象,既然是对象它就有__proto__,只要有就可以一直往上查下去。那么想要知道ZJUer.prototype.__proto__是什么很简单,只要知道它的构造函数是谁就行了,很明显,对象类型的构造函数就是Object()。所以ZJUer.prototype.__proto__就等于Object.prototype:

    console.log(ZJUer.prototype.__proto__)
    console.log(ZJUer.prototype.__proto__ === Object.prototype)

image.png

那么理论上来说,如果我们把studentCard和borrowBook两个属性从ZJUer.prototype转移到Object.prototype上,小明依然可以访问到他们:

    function ZJUer(name, age) {
        this.name = name;
        this.age = age;
    }

    Object.prototype.studentCard = '浙江大学学生证';
    Object.prototype.borrowBook = function (bookName) {
        console.log('借书成功:' + bookName);
    }

    let xiaoming = new ZJUer('小明', 18);

    console.log(xiaoming.studentCard)
    xiaoming.borrowBook('世界文明史')

image.png

果然,只要原型链上有这个属性,就能访问到,区别只是查到到哪一层而已。

总结

  • 所有对象都有__proto__(隐式原型)

  • 所有构造函数都有__proto__(构造函数也是对象)和prototype(显式原型)

  • 所有实例对象的__proto__都指向其构造函数的prototype

  • 原型链指的是__proto__构成的链

image.png

image.png