一篇文章搞懂prototype+ __proto__
引文
考虑下面的场景
// 创建一个函数
function Foo(){
this.a=1;
this.b=2;
}
let foo=new Foo();
现在来考虑这几个问题
foo.__proto__,Foo.prototype,Function.prototype,Object.__proto__
a few moments later...
怎么样,如果你对这个很明白了,那么恭喜你,后面的内容duck不必看了,如果你还半知半解,那么请你一定要认真读完,一定会有很多的收获
基本概念
1.JavaScript万物皆对象
要说起原型和原型继承,就不得不提到JavaScript万物皆对象的理念,其实这个思想在很多语言中都得到了实现,但是,js中体现得尤为明显
// 1
let obj={
a:1,
b:2,
}
// 2.
function Foo(){
this.a=1;
this.b=2;
}
let foo=new Foo();
console.log(foo.__proto__ === Foo.prototype)
js中创建一个对象是很简单的事情,可以直接用json格式创建一个对象,也可以通过new方法,通过函数创建一个对象
1.new
虽然大家每天都在用,但是可能很少深入了解其中的原理,其实说穿了也不难
看下面的示例
function Foo(){
this.a=1;
}
let foo=new Foo();
这个语句中的new,相当于下面的操作
let foo;
foo.__proto__=Foo.prototype
Foo.call(foo);
也就是说,通过new创建一个对象,首先要将隐式原型属性指向其constructor的原型,然后,才可以通过这个原型对象的,来确定所要创建的对象的属性,最后创建出来实际的对象。 如果了解vue和其它的mvvm模型的话,vue中的virtual dom就是真实的dom的model,也就是设计图纸和实际的建筑的关系,同样,只有有了原型对象这个图纸,才能创建出实际的对象
因此,一个对象的__proto__属性,就是其构造函数的prototype属性,前者是对象的隐式原型属性,后者是一个对象作为构造函数的时候的显示原型属性,注意到这里是作为构造函数,才会具有显示原型属性,而函数本身,是作为原型对象的constructor
js中万物皆对象,那么,原型也不过是对象而已,那么,一个对象肯定是有自己的构造函数的,原型对象的构造函数是什么呢?答案是Object,也就是说,原型对象只不过是一些属性的描述,通过new Object直接创建出来
2.函数是一等公民
在编程语言中,一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量。
大多数的编程语言中,字符串都是一等公民,字符串可以做为函数参数,字符串可以作为函数返回值,字符串也可以赋值给变量。
函数作为一等公民,将js的灵活的特性发挥地淋漓尽致
在js中,可以如果要创建一个对象,是通过new XXX(...args)的方式创建的,而这个XXX本身是可以作为普通的函数存在的,但是,如果和new结合在一起,就成为了构造函数
接下来就很好理解了,正因为函数是一等公民,函数是直接由Object创建出来的,而通过new Foo() 这样的方式创建出来的对象,其实是间接通过Object创建的,也就存在下面的关系链:
foo <= Foo <= Function <= Object
因此,就有了下面的关系链
图中的foo,Function foo,Function都有自己的构造函数,那么,他们的__proto__属性也就和构造函数的prototype属性绑定起来了,而对于prototype对象,他们是直接由Object创建出来,用以描述所要新创建的对象的属性的,Function在这个链条中有很重要的角色,那就是,函数是一等公民,可以将函数作为构造函数使用,从而创建出具有一些相同属性的对象,并且可以通过这个特点完成继承的功能