简话 prototype 和 __proto__

在js中,prototype和__proto__是很难理解的两个知识点,一是它俩名字差很少,容易混淆;二是在工做中能直接用到这两个知识点的地方可能也不是特别多,可是这两个知识点倒是很是重要的,尤为在实现继承方面。函数

1、理解这俩名字

在我学习它俩的时候,最大的障碍就是名字,第一印象就感受它俩是一个东西。若是以这种印象做为前提的话,就很容易走入一个迷宫。因此怎么理解prototype和__proto__呢?学习

我我的理解的是:prototype指的是扩展; __proto__指的链子(原型链),因此我以为若是用__chain__来替代__proto__能更好的理解这个链子,或者把proto两边的下划线想象做链条,看到链条就想起原型链。ui

2、链子__proto__链到了哪

先来个例子:this

function Dog(age){
    this.age = age;
}
Dog.prototype.name = "狗";

var dog = new Dog(3);
复制代码

此时dog是Dog的一个实例,咱们能够打印下dog看看: spa

发现dog私有属性只有age,打印下dog.name也能获得"狗"。这是为何呢?dog除了私有属性age外,还有个__proto__,点开dog的__proto__看一下:
发现dog的__proto__中有了name这个属性,那打印dog.name是否是就返回的这个name呢?

实际就是返回的这个name,js中访问一个对象的属性,会遵循一个规则:先在这个对象自己找私有属性,若是找到了就返回,若是找不到就沿着原型链往上找,直到最顶层null还找不到就会返回undefinedprototype

那dog.__proto__为何会有name这个属性呢?这是由于在构建dog实例的时候,会把dog的__proto__指向它的构造函数的prototype,简单说就是实例的链子会链到构造函数的扩展上,验证一下:3d

console.log(dog.__proto__ === Dog.prototype) //true
复制代码

一个实例的链子默认会往上连接到它的构造函数的扩展,而且能访问这个扩展上面的属性,除非手动改变这个链子连接的扩展code

function Dog(age){
    this.age = age;
}
Dog.prototype.name = "狗";

function Cat(){
    this.type = "cat";
}
Cat.prototype.say = "miao~";

var dog = new Dog(3);
//手动改变了dog的链子
dog.__proto__ = Cat.prototype;
console.log(dog.name); //undefined
console.log(dog.say); //miao~
console.log(dog.age); //3
console.log(dog.type); //undefined

//手动改变__proto__指向Cat.prototype 还会出现下面的状况
console.log(dog instanceof Dog); //false
console.log(dog instanceof Cat); //true
复制代码

虽然把dog的链子链到了Cat的扩展上了,可是打印了dog.type仍是undefined,这说明属性只能在链子所链的扩展上找,跟是谁的扩展没有很大的关联。这种状况很相似使用Object.create()来生成的实例:cdn

function Cat(){
    this.type = "cat";
}
Cat.prototype.say = "miao~";
var dog = Object.create(Cat.prototype, {
    age:{
        value:10
    }
})
console.log(dog.age); //10
console.log(dog.say); //miao~
conosle.log(dog.type); //undefined

//生成的实例的链子指向了 Object.create传入的第一个参数
console.log(dog.__proto__ === Cat.prototype); //true
复制代码

3、谁具备默认的prototype

js只有函数默认拥有prototype属性,由构造函数构造出来的实例默认是不具备扩展的,除非手动给这个实例加上扩展:对象

function Dog(age){
    this.age = age;
}
Dog.prototype.name = "狗";

var dog = new Dog(3);
console.log(dog.prototype) //undefined
//给dog添加一个叫prototype的属性
dog.prototype = {
    say:"wangwang~";
}
复制代码

手动给实例添加prototype,并无什么实际意义,就只是给一个对象添加一个属性,如上给dog添加了prototype,跟如下相似:

dog = {
    age:3,
    prototype:{
        say:"wangwang~"
    },
    prototype2:{
        say:"wangwangwang~"
    }
}
复制代码

4、prototype里面都有啥

以一个普通的函数为例,函数的prototype首先会有constructor属性,constructor指向了这个函数自己:

function test(a) {
    console.log(a)
}

console.log(test.prototype.constructor === test); //true
复制代码

还会有__proto__属性,为了方便咱们把函数的prototype称为fnPrototype,fnPrototypey也是个实例对象,它也会有构造函数,所以它的链子也会链到了构造它的函数的扩展

其实fnPrototypey是Object的一个实例,它的链子链到Object的扩展上,咱们能够验证下:

function test(a) {
    console.log(a)
}

var fnPrototype = test.prototype;
console.log(fnPrototype.__proto__ === Object.prototype); //true
复制代码

咱们平时用到prototype比较多的状况会有:给Array、String、Function或者Object等js的源生构造器重写或者添加扩展方法和属性:

Array.prototype.replaceAll = function(){
    //code...
}
String.prototype.trim = function(){
    //code...
}
Function.prototype.bindFn = function(){
    //code...
}
Object.prototype.__type = "object";
复制代码

这些js源生的构造器也会有prototype属性,每一个构造器会有本身的一些对应方法,他们构造出来的实例的链子会链到这些扩展上,所以实例就能使用这些方法和属性。 Function.prototype是个比较特殊的状况,它实际是一个源生封装的函数,可是实现了__proto__连接到了Object.prototype,Object.prototype.__proto__指向了null,因此说js中的一切皆对象(虽然Object也是一个函数,这里就不纠结究竟是函数层级高仍是对象层级高了,就认为Object层级是最高的吧):

console.log(Function.prototype.__proto__ === Object.prototype); //true
复制代码

5、总结

简单总结一下,实例的链子会连接到构造函数的扩展。访问实例的属性,先在自己找,找不到就沿着链子往上找链的扩展,再在这个扩展上找,还找不到就沿着这个扩展链子再往上找。

相关文章
相关标签/搜索