zoukankan      html  css  js  c++  java
  • 一文让你对js的原型与原型链不再害怕、迷惑

    原型与原型链的详细剖析

    写在最前: 希望各位看完这篇文章后,再也不用害怕JS原型链部分的知识! -- by Fitz

    一起努力,加油吧!

    原型

    原型分为两种显式原型prototype隐式原型__proto__

    显式原型prototype

    显式原型prototype存在于函数中,是函数的一个属性,它默认指向一个Object空对象(原型对象)
    注意: Object空对象(原型对象)只是内容为空, 并不是真正意义上的空对象Object.create(null), 还是能够通过原型链看到Object.prototype上的各种属性、方法,例如: toString()

    console.log(Object.prototype)
    console.log(typeof Object.prototype)    // object
    

    函数原型对象上的constructor属性对应的函数对象自身

    console.log(Object.prototype.constructor === Object)    // true
    /* 
        例如:
            我定义了一个叫Test的函数
            Test这个函数拥有它自己的prototype原型对象
            原型对象上的constructor属性对应的就是Test函数自己
    */
    console.log(test.prototype.constructor === test)    // true
    

    实例都会自动拥有其函数(构造函数)原型对象上的方法、属性

    function Person () {}
    Person.prototype.sayHello = function () {
        console.log('Hello')
    }
    
    var fitz = new Person() // fitz是Person构造函数的一个实例
    fitz.sayHello() // 'Hello'
    

    隐式原型__proto__

    每个实例对象都拥有隐式原型属性__proto__

    function Student () {
        // 构造函数Student
    }
    let fitz = new Student()    // fitz是Student的实例对象
    console.log(fitz.__proto__) // {constructor: ƒ}
    

    显式原型prototype与隐式原型__proto__的关系

    1. 构造函数的显式原型prototype默认指向一个空的(没有我们自己定义的属性、方法)Object对象
    2. 构造函数的每个实例上都有的隐式原型__proto__, 都指向着构造函数的显式原型prototype

    实例对象的隐式原型属性就是其构造函数的显式原型属性

    function Student () {
        // 构造函数Student
    }
    let fitz = new Student()    // fitz是Student的实例对象
    console.log(fitz.__proto__ === Student.prototype) // true
    

    关于原型对象创建整体的流程

    function Person () {}   // 函数创建的时候,JS引擎为Person函数自动添加prototype对象属性, 属性指向一个空的Object对象
    
    let fitz = new Person() // 实例对象创建的时候, JS引擎自动添加__proto__对象属性, 同时将这个__proto__指向该实例对象的构造函数的prototype
    
    /* 
        JS引擎自动做了的事:
            1.   Person.prototype = new Object()
            2.   fitz.__proto__ = Person.prototype
    */
    

    原型链(隐式原型链)

    原型链指的是: 在访问一个对象中的属性时,会先在自身中寻找,如果没有找到就会沿着__proto__向上寻找,如果找到就返回属性,没有就返回undefined

    function Student () {
        this.sayName = function () {
            console.log('Fitz')
        }
    }
    // 向Student的显示原型对象上添加sayAge()方法
    Student.prototype.sayAge = function () {
        console.log(21)
    }
    
    var a = new Student()
    a.sayName()     // 'Fitz'
    a.sayAge()      // 21
    console.log(a.toString())   // [Object object]
    

    探寻原型链的尽头

    首先,理清从自定义构造函数Object构造函数的prototype的关系

    // Object构造函数是JS引擎定义、生成的
    console.log(Object)
    // 查看Object的显示原型对象
    console.log(Object.prototype) // 能够看到toString()等方法
    
    // 自定义一个Student构造函数
    function Student () {}
    const stu = new Student()   // 创建一个Student实例对象
    
    /* 
        因为原型链就是隐式原型链,本质上是沿着隐式原型属性__proto__
        向上寻找属性、方法的一个过程
    */
    
    // 所以我们通过stu实例对象探寻原型链的尽头
    console.log(stu.__proto__) // 实例stu的隐式原型
    // 实例对象的__proto__ 指向  它构造函数的prototype
    console.log(stu.__proto__ === Student.prototype) // true
    // 构造函数的prototype默认是一个空的Object实例对象
    console.log(Student.prototype)
    // 空的Object实例对象的构造函数一定是Object构造函数
    console.log(Student.prototype.__proto__ === Object.prototype) //true
    /* 
        到这里,暂时总结一下此时的原型链状态:
        stu.__proto__ => Student.prototype => object.__proto__ => Object.prototype
    */
    

    然后就是最关键的部分: 着重理清Object构造函数的prototype往后部分的所有内容

    // Object构造函数是JS引擎定义、生成的
    console.log(Object)
    // 查看Object的显示原型对象
    console.log(Object.prototype) // 能够看到toString()等方法
    
    // 最为关键的一步, 这一步直接揭示了原型链的尽头在哪
    console.log(Object.prototype.__proto__) // null
    

    由此我们就能知道,原型链的尽头就是: Object.prototype

    // Object构造函数是JS引擎定义、生成的
    console.log(Object)
    // 查看Object的显示原型对象
    console.log(Object.prototype) // 能够看到toString()等方法
    
    // 自定义一个Student构造函数
    function Student() { }
    const stu = new Student()   // 创建一个Student实例对象
    
    /* 
        因为原型链就是隐式原型链,本质上是沿着隐式原型属性__proto__
        向上寻找属性、方法的一个过程
    */
    
    // 所以我们通过stu实例对象探寻原型链的尽头
    console.log(stu.__proto__) // 实例stu的隐式原型
    // 实例对象的__proto__ 指向  它构造函数的prototype
    console.log(stu.__proto__ === Student.prototype) // true
    // 构造函数的prototype默认是一个空的Object实例对象
    console.log(Student.prototype)
    // 空的Object实例对象的构造函数一定是Object构造函数
    console.log(Student.prototype.__proto__ === Object.prototype) //true
    /* 
        到这里,暂时总结一下此时的原型链状态:
        stu.__proto__ => Student.prototype => object.__proto__ => Object.prototype
        */
    
    // 最为关键的一步, 这一步直接揭示了原型链的尽头在哪
    console.log(Object.prototype.__proto__) // null
    
    /* 
        到这里,我们就能总结出原型链的尽头就是Object.prototype的结论:
        stu.__proto__ => Student.prototype => object.__proto__ => Object.prototype => null
    */
    

    完整详尽的分析原型链

    基于这一张图,我们就能够比较全面的掌握JavaScript中原型链的概念,在分析前,小伙伴们可以看这张图先自己思考一遍

    接下来是全面总结、分析原型链知识的部分

    /* 
        根据上面这张图由易入难,完整分析原型链
    */
    
    // ===============第一部分: 自定义的构造函数及其实例============
    function Foo () {}  // 1. 构造函数Foo
    var f1 = new Foo()  // 2. 实例对象f1
    // 3. 实例对象的隐式原型 指向 其构造函数的显示原型
    console.log(f1.__proto__ === Foo.prototype) // true
    // 4. 构造函数的显式原型是一个空的object对象
    // 5. 这个空的object对象是Object构造函数的实例
    console.log(Foo.prototype.__proto__ === Object.prototype) // true
    // 6. 自定义构造函数是 Function构造函数的实例
    //    换句话说: Foo这个构造函数,是new Function()出来的
    console.log(Foo.__proto__ === Function.prototype) // true
    // ===============第一部分: 自定义的构造函数及其实例============
    
    // =============第二部分: Object构造函数及原型链的尽头============
    console.log(Object) // 1.  ƒ Object() { [native code] }
    // 2. 实例对象o1、o2
    var o1 = new Object()
    var o2 = {}
    // 3. Object构造函数也是Function构造函数的实例
    //    换句话说: Object这个构造函数,也是new Function()出来的
    console.log(Object.__proto__ === Function.prototype) // ture
    // 4. Object构造函数的显式原型(Object.prototype)就是原型链的尽头
    console.log(Object.prototype.__proto__) // 5. null
    // =============第二部分: Object构造函数及原型链的尽头============
    
    
    // =================第三部分: 特殊Function构造函数================
    console.log(Function) // 1.  ƒ Function() { [native code] }
    // 2. Function构造函数的原型对象跟其他普通的构造函数一样   隐式原型指向空object对象
    console.log(Function.prototype.__proto__ === Object.prototype) // true
    // 3. 重点特殊的地方: Function构造函数是自己的实例
    //    换句话说: Function构造函数,是new Function()自己出来的, 即我生出我自己
    console.log(Function.__proto__ === Function.prototype) // true
    // 4. Function.prototype是一个函数,而不是像其他函数一样是一个空的Object对象
    console.log(typeof Function.prototype) // function
    // =================第三部分: 特殊Function构造函数================
    

    这张图配合上面的代码

    关于原型链的补充总结

    所有函数都是 Function构造函数 的实例对象,包括Function构造函数自己

    标题换句话表达, 所有函数的__proto__都指向Function.prototype
    Function.__proto__指向Function.prototype

    // 所有函数都是 Function构造函数 的实例对象
    /* 换句话说: 无论是 
                    普通函数、方法
                    自定义构造函数
                    Object等一些JS引擎内置的构造函数
                    Function构造函数本身(我生我自己)
                都是Function构造函数的实例对象
    */
    const sayHello = function () {console.log('hello')} // 自定义函数
    const Student = function (name) {    // 自定义构造函数
        this.name = name
    }
    console.log(sayHello.__proto__=== Function.prototype)
    console.log(Student.__proto__=== Function.prototype)
    console.log(Object.__proto__=== Function.prototype)
    console.log(Date.__proto__=== Function.prototype)
    // 最为特殊的Function(我生我自己)
    console.log(Function.__proto__=== Function.prototype)
    

    Function.prototype是一个函数对象

    console.log(typeof Function.prototype) // function
    console.dir(Function.prototype)
    

    所以前面部分包括总结所说的: 所有函数(构造函数),包括Function构造函数自身,都是Function构造函数的实例或者说是new Function()出来的。感觉不够严谨,更准确的说法应该是: 所有函数(构造函数),包括Function构造函数自身,都是Function.prototype函数的实例

    所有函数的显式原型prototype,都指向Object.prototype,Object构造函数的显式原型除外

    console.log(Function.prototype instanceof Object) // true
    console.log(Function.prototype.__proto__ === Object.prototype) // true
    console.log(Date.prototype instanceof Object) // true
    console.log(Date.prototype.__proto__ === Object.prototype) // true
    // object构造函数的原型对象除外的理由, Object.prototype是原型链的尽头
    console.log(Object.prototype instanceof Object) // false
    console.log(Object.prototype.__proto__ === Object.prototype) // false
    console.log(Object.prototype.__proto__) // null
    

    原型链的应用

    读取实例对象的属性值时,会先在自身中寻找,如果自身没有会到原型链中找

    function Student () {}
    Student.prototype.person = 'Fitz'
    var f = new Student()
    // person属性是原型对象上的,而不是实例本身的
    console.log(f.person)   // 'Fitz'
    

    对实例的属性进行操作时,不会影响(查找)原型链,如果实例中没有当前属性,会自动添加

    function Student () {}
    Student.prototype.person = 'Fitz'
    var f = new Student()
    // 如果实例中没有当前属性,会自动添加
    f.person = 'Lx'
    f.age = 21
    /* 
        属性只会在实例上,与原型链无关
        可以运用前面的 引用数据类型的知识理解
    */
    

    利用原型链,将实例的方法添加在原型对象上,实例的属性添加在实例自身

    好处: 避免了每次实例化对象时,都创建出一模一样的方法,节省内存

    function Person (name, age){
        this.name = name
        this.age = age
    }
    // 实例的方法统一放在构造函数的原型对象上
    // 这样实例在调用方法时,可以通过原型链顺利找到该方法
    Person.prototype.printInfo = function () {
        console.log(`name: ${this.name}`)
        console.log(`age: ${this.age}`)
    }
    
    var fitz = new Person('fitz', 21)
    fitz.printInfo()
    

    原型链继承

    尝试使用原型链来模拟类的继承

    实现的关键是: 子类的原型是父类的实例

    思路来源于: 既然所有的实例对象都能调用toString()方法那就看看为什么,

    1. toString()方法在Object.prototype显式原型对象上
    2. 实例对象的隐式原型__proto__ 指向其 构造函数的显式原型prototype
    3. 而关键就是,构造函数的显式原型是Object.prototype的实例对象
    // 模拟父类
    function Father() {
        _Fathername = '我是父类'
        this.name = 'Father'
    }
    Father.prototype.getFathername = function () {
        console.log(_Fathername)
    }
    Father.prototype.getName = function () {
        console.log(this.name)
    }
    
    // 模拟子类
    function Son() {
        _SonName = '我是子类'
        this.name = 'Son'
    }
    
    
    // 实现子类继承父类
    Son.prototype = new Father()
    
    
    Son.prototype.getSonName = function () {
        console.log(_SonName)
    }
    
    // 需要实现的目标
    var son = new Son()
    // 能在子类使用父类的方法
    son.getFathername() // '我是父类'
    son.getName() // 'Son'
    
    console.log(son)
    

  • 相关阅读:
    .NET逻辑分层架构总结
    ASP.NET MVC 4 的JS/CSS打包压缩功能-------过滤文件
    c#实现浏览器端大文件分块上传
    fckeditor如何能实现直接粘贴把图片上传到服务器中?
    web编辑器直接粘贴图片实现
    富文本编辑器直接粘贴图片实现
    百度ueditor编辑器直接粘贴图片实现
    百度编辑器直接粘贴图片实现
    fckeditor直接粘贴图片实现
    wangEditor直接粘贴图片实现
  • 原文地址:https://www.cnblogs.com/fitzlovecode/p/jsadvanced1-prototype.html
Copyright © 2011-2022 走看看