• proto像c语言中的指针
    1. 除了 undefined 和 null 之外的所有值都有 proto 这个值
    2. 这个 proto 的值最终会指向同一个 Object 对象,例如 : 类似与这样查找最终会中找到 Object 对象: .proto..proto..**proto.
    3. 某一个值的每一层的 proto 中的方法都可以直接使用,例如: true.proto 中有 toString 方法,那么就可以直接使用这个 true.toString() 方法,再例如 true.proto..proto. 中拥有 hasOwnProperty 这个方法,那么就直接可以使用 true.hasOwnProperty()

    在官方的es5中,定义了一个名叫[[prototype]]的属性,每个对象都拥有这样一个属性,这个属性又是一个指针,它指向一个名叫原型对象的内存堆。而原型对象也是对象,因此又含有自己的[[prototype]]的属性,又指向下一个原型对象。 那么终点是哪? 当然是我们的Object.prototype对象。
    也就是说,js中的对象模型大概是这个样子:
    image.png
    使用字面量定义一个对象时: var foo={};
    创建一个函数对象时:function Foo();
    使用构造函数定义一个对象时: var foo=new Foo();
    以上是最基本的三个状态,其他的状态都是由这三个状态变化而成的。

    1. 先来看第一个情况: var foo={};

    此时会生成一个新的对象,而由于没有对他的[[prototype]]进行任何的操作,所以默认情况下所指向的是Object.prototype对象:

    1. var foo={};
    2. foo.proto===Object.prototype;//true
    3. Object.prototype.toString.call(foo.proto);//该方法返回对象类型的字符串:object Object
    4. foo.hasOwnProperty(‘proto’);//false

    但是,让我们来看下代码的最后一行,执行的结果却是false。 也就是proto并非在普通对象的内部,它其实是在Object.prototype上。而 foo.proto可以正常运行,是因为引擎调用了getter函数,大家可以试下执行:alert(foo.proto),你会看到输出结果为:function(){} ,意思为这个指针所访问的内容是个空函数。这个里面比较复杂,有兴趣的可以去看看:深入js这本书。但是一般情况下,我们可以简单的理解为访问proto时,就是在访问Object.prototype,如果你没把proto指向其他对象的话。

    2. 接下来看看比较复杂的第二张情况:function Foo();
    我们知道,Foo函数也是一个对象,当然他也可以访问proto内部属性。但是他的proto的内部属性并非跟普通对象一样指向Object.prototype对象,相反他指向Function.prototype对象,而Function.prototyp对象的proto再指向Object.prototype。 image.png

    1. function foo(){};
    2. Object.prototype.toString.call(foo.__proto__);//object Function
    3. foo.__proto__===Function.prototype;//ture
    4. Object.prototype.toString.call(foo.proto.proto);//object Object
    5. foo.proto.proto.===Object.prototype;//ture

    我们知道,每个Js函数对象,都拥有一个原型对象,这个原型对象是函数自己的属性,并且这个原型对象跟普通对象没有区别,因此它也可以访问proto,自然它的proto指向的是Object.prototype了。

    1. function foo(){};
    2. alert(foo.hasOwnProperty('prototype'));//ture prototype为foo的自身属性
    3. alert(foo.propertyIsEnumerable('prototype'));//false prototype不可枚举
    4. foo.prototype.__proto__===Object.prototype;// 原型对象访问__proto__属性,指向Object.prototype

    也就是说。原型对象是在函数对象下的一个属性,而普通对象是没有原型对象。这就为接下来的用new 调用函数进行面向对象编程打下了基础。
    结合起来,当我们定义一个函数时, 内部同时产生了 proto和prototype属性

    1. 最后来看看最复杂的: var foo=new Foo{};

      对于new操作,一般来讲是四步操作:1 生成一个新对象 2 将新对象执行原型链接到函数的prototype对象上,3 把新对象绑定到this上, 4 如果没有return,返回函数执行结果的新对象。
      这里的关键点在于第二步操作。很多人将其解释为,new的新对象是函数自身的prototype对象,其实根本不是如此。通过上面的第二点的代码,我们就已经知道了函数原型对象一开始就便存在,只是它是不可枚举的空对象而已。 而且,有个重点:函数原型对象没有在原型链中!也就是说引擎进行右值查询时,根本不会理会函数原型对象!

      1. function foo(){};
      2. foo.prototype.y=10;
      3. alert(foo.y);// undefined
      4. foo.proto.x=10;
      5. alert(foo.x);// 10

      从上面的我们就可以看出, 对函数对象进行原型链查询,是不会检索函数原型对象的。
      那么new所创建的是个什么对象呢? 我们知道在函数作用域里所定义的变脸,外部环境是访问不到的。这样的话,如何把内部所生成好的变量传递给外部变量呢?最主要的方式是使用return .而另一个情况:在c++这些语言中,则是创建一个匿名对象,由匿名对象保存函数所生成的数据,然后当它把数据传递给外表环境时,自身就会释放内存消失掉。
      js中正是使用的后一个方法:它创建一个匿名的对象,负责保存所有的数据,同时在没有return情况下,当对象把数据传递给外表环境后就释放内存:

      1. function Foo(){this.a=a;};
      2. var test=new Foo(10);

      上面代码的内部情况为:
      image.png
      当Foo函数执行完成后,匿名对象消失,所有的数据都被保存到了test上。此时,test的proto就指向了之前的函数原型对象了,而不再是O

    ————————
    实例化

    1. var monkey = { //声明一个对象
    2. feeds : "bananas", //定义一些属性
    3. breathes: "air"
    4. };
    5. function Human() { //创建一个构造函数 Human
    6. }
    7. Human.prototype = monkey; //将Human的原型指向 monkey
    8. var adou = new Human(); //实例化一个Human对象
    9. adou.feeds = "rice"; //对这个对象添加属性,这里覆盖原型中的同名属性,(因为对象自身属性会高于原型属性)
    10. adou.hobby = "sleep";//添加一个原型中没有的属性
    11. console.log(adou.feeds); //rice
    12. console.log(adou.hobby); //sleep

    我们在实例化adou这个对象的时候,并没有给他添加breathes这属性,为什么会输出air的值呢,有人说,是因为new的时候,继承了原型的属性和方法,对,是这样的,但是,它靠什么继承或者说靠什么找到monkey的breathes的属性呢?靠的就是这么一个神秘的链接 .proto 。我们再来想想,adou是 human 实例化出来的,human 的原型指向了monkey,也就有了monkey的方法属性,adou本身没有这个breathes属性的,在调用adou.breathes的时候,就先从adou这个对象中找,没有找到,然后就去它的原型中,欸,找到了,然后就返回了这个属性值。 也就是说 adou.proto === monkey; 返回值是 ture
    到这里,我们就可以理解这个原型链了:实例化出来一个对象,然后查找他的属性时候,会先从他的本身去查找,如果没有找到,再从他的原型中去找,找到则返回值,找不到就返回undefined。这个查找的过程就是原型链查找的过程。