• 详解Javascript中的原型与原型链


    知识点

    面向对象编程

    • 我们熟悉的Java和C#里,面向对象的两个基本概念是类class和实例instance,而ES6以前的Javascript并没有设计class。
    • Javascript里的class是通过原型来实现的,通过将某个对象的__proto__属性指向另外一个对象来实现继承,如classA.__proto__ = classB,看上去就像B是A的父类,A继承自B类。

    隐式原型、显式原型、原型链

    • 通常我们说的隐式原型指的是__proto__,而显式原型指的是prototype。
    • 所有对象都有__proto__,只有function对象拥有prototype,因此function对象既有__proto__又有prototype。
    • __proto__在低版本的IE里不提供,因此考虑到兼容性的话可以通过Object.getPrototypeOf(对象)来获得。
    var obj = {};
    console.log(obj.__proto__.constructor);//ƒ Object() { [native code] }
    console.log(obj.prototype);//undefined 
    
    var foo = function () { return 'hello world';};
    console.log(foo.__proto__.constructor);//Function
    console.log(foo.prototype.construtor);//foo
    console.log(Object.getPrototypeOf(foo));//与foo.__proto__一样
    
    • 原型链通常指的是隐式原型链,JavaScript里任何东西都是对象,任何一个对象内部都有一个内置对象叫__proto__,即隐式原型,它可以包含任何东西让对象继承。当然__proto__本身也是一个对象,它自己也有自己的__proto__,这样一级一级向上,就构成了一个__proto__链,即原型链。原型链不会无限向上,它有个终点,可以称为原型链的顶端,或者root,它是一个特殊的对象,它的__proto__为null,如下所示:
    obj.__proto__.__proto__......__proto__ === null;
    

    第一个特殊对象:原型链的顶端root_prototype

    • 天地洪荒,宇宙玄黄,Javascript世界诞生了第一个特殊对象root_prototype,它是[native code],也就是内部实现。
    • 所有的孩子都是它创造的,也就是说所有的原型链最终都指向它。
    obj.__proto__.__proto__......__proto__ === root_prototype;
    
    • root_prototype作为万物的爸爸,没有比他更大的了(也就是说他没有爸爸),所以root_prototype.__proto__ = null。
    obj.__proto__.__proto__......__proto__.__proto__ === null;
    
    • 它定义了一些最基本的field和method,如toString之类的,所以它的孩子不用自己再实现就自动都拥有这些field和method。
    console.log(obj.toString());//[object Object]
    console.log(obj.godNotDefined());//obj.godNotDefined is not a function
    
    • 系统将root_prototype挂载在Object函数的prototype上,注意只是挂载在上面,而并不是说Object.prototype创造了所有对象,Object.prototype只是提供了一个指针让我们访问到root_prototype而已。
    console.log(Object.prototype);//root_prototype, ƒ Object() { [native code] }
    console.log(Object.prototype.__proto__);//root_prototype.__proto__, null
    

    第二个特殊对象:函数的顶端function_root_prototype

    • 孤阴不生,独阳不长,光有root_prototype还不够,因此世界诞生了第二个特殊对象function_root_prototype,它也是[native code],也是内部实现。
    • 作为root_prototype的孩子,它同样有function_root_prototype.__proto__ = root_prototype;
    • 系统将function_root_prototype挂载在Function函数的prototype上,与root_prototype一样,Function.prototype只是提供了一个指针让我们访问到function_root_prototype而已,并不是说Function.prototype创造了所有函数。
    console.log(Function.prototype);//function_root_prototype, ƒ () { [native code] }
    console.log(Function.prototype.__proto__);//root_prototype, ƒ Object() { [native code] }
    
    • 所有函数都是由function_root_prototype创建的,Function函数也不例外,因此:
    console.log(Function.__proto__);//function_root_prototype, ƒ () { [native code] }
    
    • 而对于Object函数来说,它也是function_root_prototype直接创建的,所以有:
    console.log(Object.__proto__);//function_root_prototype, ƒ () { [native code] }
    
    • 不管是Function函数也好,Object函数也好,自定义函数也好,他们都是由function_root_prototype创建的,所以它们的__proto__必定都是function_root_prototype。
    console.log(foo.__proto__); //function_root_prototype, ƒ () { [native code] }
    

    函数特有的属性prototype

    • function_root_prototype是所有函数的爸爸,它带有一个特殊属性prototype。
    • 这个属性一共有三个作用:对于Function函数,它指向function_root_prototype;对于Object函数,它指向root_prototype;而对于自定义函数,它指向函数的实际函数体。
    console.log(Function.prototype);//function_root_prototype, ƒ () { [native code] }
    console.log(Object.prototype);//root_prototype, ƒ Object() { [native code] }
    console.log(foo.prototype);//{constructor: ƒ}
    

    JavaScript里经常说到的蛋鸡问题:

    Object instanceof Function === true
    Function instanceof Object === true
    
    • 这个问题主要是因为instanceof操作符和系统挂靠root_prototype到Object函数上导致的。形如“a instanceof b”指的是a的原型链上有没有出现过一个b函数的prototype,如果有,则返回true,如果没有,则返回false。
    • 对于Object instanceof Function === true,其实是:
    //Object函数由function_root_prototype构建
    Object.__proto__ === function_root_prototype;
    //function_root_prototype挂靠在Function.prototype上
    function_root_prototype === Function.prototype;
    //所以满足instanceof的判断条件
    Object.__proto__ === Function.prototype;
    Object instanceof Function === true;
    
    • 对于Function instanceof Object === true,其实是:
    //Function函数由function_root_prototype构建
    Function.__proto__ === function_root_prototype;
    //万物皆由root_prototype构建
    function_root_prototype.__proto__ === root_prototype;
    //root_prototype挂靠在Object.prototype上
    root_prototype === Object.prototype;
    //所以满足instanceof的判断条件
    Function.__proto__.__proto__ === Object.prototype;
    Function instanceof Object === true;
    
    • 这里要强调Object函数和其它函数的不同之处。Object之所以特殊,就是因为Object的prototype被系统设定为了root_prototype,仅此而已。
    • 而所谓的Object instanceof FunctionFunction instanceof Object的蛋鸡问题,前者应该来说是自然而然、不容置疑的,可以认为Object函数是Function创造出来的;而后者说白了只是因为系统设计上,强行规定了Object函数的特殊性,而导致的一个推论,而Function并非是Object创建的。

    new操作符

    • var obj = new Object()其实相当于:
    obj.__proto__ = Object.prototype;//根据上文我们知道这里挂靠的是root_prototype,系统内部实现
    
    • var obj = new Func()其实相当于:
    //1、创建一个空对象
    var obj = {};
    //2、设置原型链
    obj.__proto__ = Func.prototype;
    //3、让Func中的this指向obj,并执行Func的函数体。
    Func.call(obj);
    
    • new操作符返回的是包装好的新对象:
    var Func = function () {}
    var obj = new Func();
    console.log(Func.__proto__);//function_root_prototype, f() [ native code]
    console.log(Func.prototype);//函数体
    console.log(obj.__proto__);//函数体
    console.log(obj.prototype);//undefined
    console.log(obj instanceof Object); //true
    console.log(obj instanceof Function); //false
    
    console.log(typeof Number(1));//number
    console.log(typeof 1);//number
    console.log(typeof new Number(1));//object
    

    参考资料

    1. Javascript原型链以及Object、Function之间的关系
    2. 廖雪峰官方网站:Javascript面向对象编程

    结束语

    无论你犯了多少错,或者进步得有多慢,你都走在了那些不曾尝试的人的前面。

  • 相关阅读:
    判断以下字符串中出现次数最多的字符,并且算出该字符出现的次数
    status的状态码
    call和apply的区别?
    闭包
    $(document).ready和window. onload的区别
    python_pandas常用操作
    python经典例题
    爬B站并保存成csv文件。提供数据
    data_analysis:初识numpy
    01爬取豆瓣网电影数据进行numpy的练习
  • 原文地址:https://www.cnblogs.com/zenronphy/p/JSProtoType.html
Copyright © 2020-2023  润新知