先理清proto、prototype、constructor

首先,我们需要牢记两点:
proto和constructor属性是对象所独有的;
② prototype属性是函数所独有的。 但是由于JS中函数也是一种对象,所以函数也拥有proto和constructor属性

  1. proto属性都是由一个对象指向一个对象,即指向它们的原型对象(也可以理解为父对象)

    __proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链

  2. constructor属性也是对象才拥有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数
  3. prototype属性,它是函数所独有的,它是从一个函数指向一个对象。它的含义是函数的原型对象,也就是用这个构造函数创建的实例的原型对象

每个对象都有构造函数是指每个对象都可以找到其对应的constructor,
这个constructor可能是对象自己本身显式定义的或者通过proto在原型链中找到的
而单从constructor这个属性来讲,只有prototype对象才有。
函数创建的对象.__proto__ === 该函数.prototype该函数.prototype.constructor===该函数本身

一、prototype[原型对象]

  1. 大部分函数数据类型的值都具备 prototype(原型/显式原型)属性属性值本身是一个对象
    1. 浏览器会默认为prototype开辟一个堆内存,用来存储:
      1. 可以调用的公共的 属性和方法
      2. 当前类所属实例proto
    2. 在浏览器默认开辟的这个堆内存中
      1. 「原型对象」有一个默认的属性constructor(构造函数/构造器)”,属性值是当前函数/类本身!!
      2. **fn.prototype.constructor === fn // true** ```javascript function A(){ this.name =name //(该属性,强调私有,不共享) } A.prototype.say = function(){ //定义在原型上的方法(强调复用。需要共享) console.log = (‘hello’) }

// 不推荐写法: A.prototype = { say:function(){ console.log = (‘hello’)} } // 因为会丢失constructor属性

  1. ```javascript
  2. function Person () {
  3. this.name = 'John';
  4. }
  5. var person = new Person();
  6. Person.prototype = {
  7. say: function() {
  8. console.log('Hello,' + this.name);
  9. }
  10. };
  11. person.say();//person.say is not a function
  12. 原文链接:https://blog.csdn.net/kkkkkxiaofei/article/details/46474303
  1. 内置对象例如Array、Object、Boolean、String、等都可以通过new 实例化 所有都有 prototype这属性 可以用来扩展一些方法和属性;

注意:Math没有prototype 因为:

我们都知道 Boolean、Map 等都可以通过new Boolean() 调用,但是调用 new Math() 会得到 TypeError: Math is not a constructor 因为 Boolean Map 继承自 Function对象,而 Function 对象是有 Construct 的。而 Math只继承了 Object,并没有继承Function。

  • 可以看到内置类Array里面的constructor指向了Array

原型和原型链 - 图1

  1. // prototype 是内置类 Array 的一个私有属性???同时是公有的吗
  2. console.log('prototype' in Array); // true
  3. console.log(Array.hasOwnProperty("prototype")); // true
  4. Array.hasOwnProperty("__proto__") //false
  5. var arr = new Array()
  6. arr.hasOwnProperty('__proto__') //false 它不能来自链中的任何对象,因为它特定于这个对象。
  7. // __ proto __ 是一个继承自 Object.prototype 的 setter / getter,它不是拥有属性

关于是否所有函数都有prototype一说

  • 使用Function.prototype.bind创建的函数对象

    1. function abc(){console.log('abc')}
    2. var binded = abc.bind(null)
    3. binded() //abc
    4. console.log(binded.prototype) //undefined

    bind一般的用法是fn.bind(obj),然后返回的是绑定obj内容上下文(this指向obj)的fn。如果obj为空的话,则this指向window,如下:因此Function.prototype.bind()其实就是Function.prototype
    于是我现在有了一个疑问Function.prototype到底是不是函数呢

    1. Function.prototype instanceof Function //false
    2. typeof Object.prototype //object
    3. Object.prototype.toString.call(Function.prototype) //[object Function]
    4. Function.prototype.bind.call(Function.prototype) //ƒ () { [native code] }
  • 箭头函数也没有

    1. var abc = ()=>{console.log('abc')}
    2. abc() //abc
    3. console.log(abc.prototype) //undefine

    函数需要prototype主要是用来做构造函数,构造实例,否则没啥用

  1. 函数数据类型
    1. 普通函数(实名或匿名函数)
    2. 箭头函数
    3. 构造函数/类
    4. 生成器函数 Generrator
  2. 不具备prototype的函数
    1. 箭头函数 _const fn=()=>{}_
    2. 基于ES6给对象某个成员赋值函数值的快捷操作 ```javascript let obj = { fn1: function () { // 常规写法 具备prototype属性 }, fn2() { // 快捷写法 不具备prototype属性 } };

class Fn { fn() {} //这样的也不具备prototype属性 };

  1. <a name="NtZKv"></a>
  2. ## 二、__ _proto___ [原型链]
  3. 1. 每一个“**对象数据类型**”的值都具备一个**属性**“**__proto__(原型链/隐式原型)**”
  4. 1. 属性值指向“**当前对象的 构造函数的 prototype**”
  5. 1. 对象上天生具备一个属性:**constructor**,指向类本身
  6. 1. 对象数据类型值(**万物皆对象**)
  7. 1. 普通对象
  8. 1. 特殊对象:数组、正则、日期、Math、Error...
  9. 1. 函数对象
  10. 1. 实例对象
  11. 1. 构造函数**.**prototype
  12. 1. 当我们不知道当前对象是谁的实例,那么就是 Object的实例 [ Object就指向null,因为它是基类]
  13. 1. **原型链机制 **
  14. 1. 首先访问自己的私有属性,如果私有属性中存在,就直接使用私有的
  15. 1. 如果访问的成员私有属性中没有,默认惠基于__proto__找到所属类prototype上的属性/方法
  16. 1. 如果所属类的prototype也没有,就基于prototype.__prototype__向上查找,一直找到Object.prototype为止
  17. 1. 这种**成员访问机制**,被称为“**原型链机制**”
  18. 1. 公有私有
  19. 1. 公有:原型的属性
  20. 1. 私有:自己的属性
  21. <a name="YECiA"></a>
  22. ### 方法调用
  23. ```javascript
  24. // 几个访问方式
  25. arr.forEach(...) // 正常调用
  26. arr.__proto__.forEach(...) // 直接跳过私有的
  27. Array.prototype.forEach(...) // 直接找原型的
  28. // 三者区别在于forEach方法中的this
  29. // 点前面是谁就是谁

方法执行查找

  1. let arr = [10, 20, 30];
  2. console.log(arr.hasOwnProperty('forEach')); //->false
  3. 简称:验证“forEach”是否为arr对象的私有属性
  4. 全称:arr按照原型链查找机制,找到的是Object.prototype.hasOwnProperty方法「@A」,并且把找到的@A执行
  5. + 方法中的this->arr「我们要操作的对象」
  6. + 传递的实参->“forEach”「我们要验证的属性」
  7. @A方法的作用是,验证“实参”是否为当前“this”的一个私有属性
  8. 例一:
  9. 直接找到方法(可以改变this指向) ===> Object.prototype.hasOwnProperty.call(arr,'forEach')
  10. 例二:
  11. arr.push(100);
  12. 内部:Array.prototype.push.call(arr, 100)

案例

  1. function Fn() {
  2. this.x = 100;
  3. this.y = 200;
  4. this.getX = function () {
  5. console.log(this.x);
  6. };
  7. }
  8. Fn.prototype.getX = function () {
  9. console.log(this.x);
  10. };
  11. Fn.prototype.getY = function () {
  12. console.log(this.y);
  13. };
  14. let f1 = new Fn;
  15. let f2 = new Fn;
  16. console.log(f1.getX === f2.getX); // false 两个私有函数 不同堆
  17. console.log(f1.getY === f2.getY); // true 都是原型链的公有函数 相同堆
  18. console.log(f1.__proto__.getY === Fn.prototype.getY); // true 都是原型链的公有函数 相同堆
  19. console.log(f1.__proto__.getX === f2.getX); // false 一个公有,一个私有 不同堆
  20. console.log(f1.getX === Fn.prototype.getX); // false 一个私有,一个公有 不同堆
  21. console.log(f1.constructor); // Fn 获得直属类信息
  22. console.log(Fn.prototype.__proto__.constructor); // Object
  23. // 1. 确定执行的是哪个方法
  24. // 2. 确定方法中的this “点字则”
  25. f1.getX(); // 100 私有的方法 this指向f1
  26. f1.__proto__.getX(); // undefined 公有的方法 this指向f1
  27. f2.getY(); // 200 公有的方法 this指向f2
  28. Fn.prototype.getY(); // undefined 公有的方法 this指向Fn

原型和原型链 - 图2