一篇文章搞懂prototype+ __proto__

124 阅读3分钟

一篇文章搞懂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在这个链条中有很重要的角色,那就是,函数是一等公民,可以将函数作为构造函数使用,从而创建出具有一些相同属性的对象,并且可以通过这个特点完成继承的功能