今天看啥  ›  专栏  ›  Sakura同志

『妈妈再也不用担心之』原型链

Sakura同志  · 掘金  ·  · 2019-11-05 15:40
阅读 2

『妈妈再也不用担心之』原型链

AKA__proto__, prototype, constructor的爱恨情仇

总结

__proto__

  • __proto__属性是对象所独有的
  • 它从一个对象指向一个对象,默认指向它的原型对象(父对象),
  • 它的作用是当访问一个对象的属性时,如果该对象内部不存在该属性时,那么就会去该对象的__proto__所指向原型对象(父对象)里找,一直找,直到找到万物之源 Object 的__proto__属性,此时 Object 的__proto__指向的是 null,此时就表示已经到尽头了,确实没有该属性。所以__proto__属性的终点是 null
  • 通过__proto__一层层将它们所指向的对象连接起来的这条链路就是我们常说的原型链

prototype

  • prototype属性是函数所独有的,任何函数在创建的时候,会默认创建该函数的prototype对象
  • 它从一个函数指向一个对象,它的含义是函数的原型对象。也可以理解为这一类对象实例(通过该函数所创建的实例)的原型对象
  • 它的作用是存放这一类对象所有实例所共享的属性和方法,本质是为了节省内存

constructor

  • constructor是对象所独有的
  • 它从一个对象指向一个函数,默认指向该对象的构造函数。每个对象都可以找到其对应的constructor,因为创建对象的前提是需要有constructor,它可能是本身拥有或继承而来。单从constructor这个属性来讲,只有prototype对象才有。
  • constructor易被更改,所以相对没那么可靠
  • Function这个对象(也是函数)比较特殊,它的构造函数就是它自己。所有函数和对象最终都是由Function构造函数得来,所以constructor属性的终点就是Function这个函数

原型公式

// Demo
function Fun() {};
let fn = new Fun();
复制代码

fn.__proto__ === Fun.prototype

fn.constructor === Fun

fn.__proto__.constructor === Fun

Fun.prototype.constructor === Fun

Fun.constructor === Function

Function.constructor === Function

Function.__proto__ === Function.prototype

Fun.prototype.__proto__ === Object.prototype

Function.prototype.__proto__ === Object.prototype

Object.prototype.__proto__ === null

Object.constructor === Function


__proto__constructor属性是对象所独有的
prototype属性是函数所独有的,但由于 JS 中函数也是一种对象,所以函数也拥有__proto__constructor属性

图解

__proto__属性图解

__proto__

prototype属性图解

prototype

constructor属性图解

constructor

constructor结合__proto__图解

constructor和__proto__

继承constructor

总结图解

总结

手写一个new试试

首先,我们需要知道new做了哪些工作:

  • 创建一个新的对象obj
  • obj对象的__proto__属性指向构造函数的原型对象prototype
  • 执行构造函数,并且传递参数,改变this的指向,指向新生成的对象obj
  • 如果构造函数本身有返回值,且这个返回值是对象类型,则return这个返回值;否则返回新对象obj(根据规范,返回 null 和 undefined,依然返回obj)
// 写法1(推荐)
// 如果第一个参数不是函数,则抛出异常,因为默认只有函数才有prototype对象
// 用 Object.create()来创建带有你想要的[[Prototype]]的新对象。
// 执行构造函数,并且传递参数,将this指向obj
// 如果构造函数本身有返回值,且这个返回值是对象类型,则return这个返回值;否则返回新对象obj
function _new(Ctor, ...arg) {
    if(typeof Ctor !== 'function') {
        throw `the first param must be a function` 
    }
    let obj = Object.create(Ctor.prototype);
    let ret = Ctor.apply(obj, arg);
    return ret instanceof Object ? ret : obj;
}


// 定义构造函数
function Person(name = 'sakura') {
    this.name = name;
    console.log(this.name);
}

let person1 = _new(Person, 'eril'); // eril
let person2 = new Person('eril'); // eril
复制代码
// 其它写法
function _new2() {
    let obj = Object.create(null); //创建一个纯的空对象
    let Constructor = [].shift.call(arguments); //将参数列表中的第一个参数截取出来作为构造函数,执行完后参数列表长度-1
    Object.setPrototypeOf(obj, Constructor.prototype); // 更值得推荐的设置对象原型的方法,执行完后,继承关系成立
    let rt = Constructor.apply(obj, arguments); //执行构造函数,并将this指向obj
    return typeof rt === 'object' ? rt : obj; //判断构造函数返回值是否为对象,是则返回构造函数返回值,否则返回新创建的对象obj
    
}

function _new3(func) {
    var res = {};
    if (func.prototype !== null) {
        res.__proto__ = func.prototype; //已经过时并且不推荐的修改原型对象的方法
    }
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1)); //先截取掉第一个参数(构造函数)
    // 根据规范,返回 null 和 undefined,依然返回obj
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return res;
}
复制代码

感谢

帮你彻底搞懂 JS 中的 prototype、__proto__与 constructor(图解)

用自己的方式(图)理解 constructor、prototype、__proto__和原型链




原文地址:访问原文地址
快照地址: 访问文章快照