zoukankan      html  css  js  c++  java
  • JavaScript学习笔记(十二) 原型

    之前看原型和原型链的时候,总是看得迷迷糊糊的,最近就趁着有空重新整理了一下,其实发现并不难

    下面我们一步一步跟着,彻底弄清楚原型和原型链吧

    1、原型

    • 每个 实例对象 都有一个 constructor 属性,指向它的构造函数
    function Person(name) {
    	this.name = name
    }
    var person = new Person('Steve') // 自定义构造函数
    var string = new String('Hello') // 原生的构造函数
    
    console.log(person.constructor === Person)
    console.log(string.constructor === String)
    
    /*
     * 执行结果:
     * true
     * true
    **/
    
    • 每个 函数对象(包括构造函数) 都有一个 prototype 属性,指向函数的原型对象

      这个 原型对象constructor 属性,指向函数本身

    function Person(name) {
    	this.name = name
    }
    var person = new Person('Steve') // 自定义构造函数
    var string = new String('Hello') // 原生的构造函数
    
    console.log(Person.prototype.constructor === Person)
    console.log(String.prototype.constructor === String)
    
    /*
     * 执行结果:
     * true
     * true
    **/
    
    • 每个 对象 都有一个 [[prototype]] 私有属性,指向它的构造函数的原型对象,但这个属性是不允许访问的

      某些浏览器(例如 Chrome)提供 __proto__ 属性用于访问 [[prototype]] 私有属性

    function Person(name) {
    	this.name = name
    }
    var person = new Person('Steve') // 自定义构造函数
    var string = new String('Hello') // 原生的构造函数
    
    console.log(person.__proto__ === Person.prototype)
    console.log(string.__proto__ === String.prototype)
    console.log(Person.__proto__ === Function.prototype)
    console.log(String.__proto__ === Function.prototype)
    
    /*
     * 执行结果:
     * true
     * true
     * true
     * true
    **/
    
    • 构造函数的 constructor 属性都是指向 Function__proto__ 属性都是指向 Function.prototype

      因为构造函数都是通过 new Function 来创建的,它们都是 Function 的实例对象,包括 Function 和 Object

    function Person(name) {
    	this.name = name
    }
    
    console.log(Person.constructor === Function)
    console.log(Person.__proto__ === Function.prototype)
    console.log(String.constructor === Function)
    console.log(String.__proto__ === Function.prototype)
    
    console.log(Function.constructor === Function)
    console.log(Function.__proto__ === Function.prototype)
    console.log(Object.constructor === Function)
    console.log(Object.__proto__ === Function.prototype)
    
    /*
     * 执行结果:
     * true
     * true
     * true
     * true
     * true
     * true
     * true
     * true
    **/
    
    • Object 外,其它构造函数的 prototype 属性的 __proto__ 属性都是指向 Object.prototype

      Objectprototype 属性的 __proto__ 属性指向 null

    function Person(name) {
    	this.name = name
    }
    
    console.log(Person.prototype.__proto__ === Object.prototype)
    console.log(String.prototype.__proto__ === Object.prototype)
    
    console.log(Function.prototype.__proto__ === Object.prototype)
    console.log(Object.prototype.__proto__ === null)
    
    /*
     * 执行结果:
     * true
     * true
     * true
     * true
    **/
    

    最后来一张图片总结一下,虽然看起来有点复杂,但是只要大家找到规律,就会很简单了

    建议大家分类来看,注意哪里出现 constructor 属性,哪里出现 prototype 属性,哪里出现 __proto__ 属性

    2、原型链

    (1)原型链

    上面我们说过,所有对象都有 __proto__ 属性,并且这个 __proto__ 属性指向一个原型对象

    因为原型对象也是对象,这个原型对象也有 __proto__ 属性,我们把这种关系称为原型链

    (2)属性访问

    当需要访问一个对象的属性时,首先从该对象开始查找,如果能够找到,那么到此返回

    如果没有找到,就在该对象的 __proto__ 属性指向的原型对象中继续查找,如果能够找到,那么到此返回

    如果没有找到,那么一直往上查找原型对象,直至 __proto__ 属性指向 null,也就是原型链的顶端

    若原型链上的所有原型对象都没有该属性,则返回 undefined

    function Person(name) { this.name = name }
    Person.prototype.getName = function() { return this.name }
    var person = new Person('Steve')
    
    var name = person.getName() // 在 Person.prototype 中找到
    var description = person.toString() // 在 Object.prototype 中找到
    var age = person.age // 在原型链中无法找到
    
    console.log(name)
    console.log(description)
    console.log(age)
    
    /*
     * 执行结果:
     * Steven
     * [object Object]
     * undefined
    **/
    

    (3)属性检测

    • in 操作符:检测属性是否在对象的原型链上
    • hasOwnProperty 方法:检测属性是否来自对象本身
    function Person(name) { this.name = name }
    Person.prototype.getName = function() { return this.name }
    var person = new Person('Steve')
    
    console.log('age' in person)
    console.log('name' in person)
    console.log('getName' in person)
    console.log('toString' in person)
    
    console.log(person.hasOwnProperty('age'))
    console.log(person.hasOwnProperty('name'))
    console.log(person.hasOwnProperty('getName'))
    console.log(person.hasOwnProperty('toString'))
    
    /*
     * 执行结果:
     * false
     * true
     * true
     * true
     * false
     * true
     * false
     * false
    **/
    

    3、为什么要使用原型

    JavaScript 中的继承是基于原型的,原型的作用在于不同实例对象之间可以共享数据,节省内存

    function Person(name) { this.name = name }
    Person.prototype.getName = function() { return this.name }
    var person1 = new Person('Steve')
    var person2 = new Person('Steven')
    
    // 不同实例对象 person1 和 person2 都可以使用在 Person.prototype 上定义的 getName(共享数据)
    // 从而避免在每个实例对象上都要重复定义 getName(节约内存)
    var name1 = person1.getName()
    var name2 = person2.getName()
    console.log(name1)
    console.log(name2)
    
    /*
     * 执行结果:
     * Steve
     * Steven
    **/
    

    【 阅读更多 JavaScript 系列文章,请看 JavaScript学习笔记

    版权声明:本博客属于个人维护博客,未经博主允许不得转载其中文章。
  • 相关阅读:
    解决KDE桌面附带文件索引框架Baloo占用资源过多问题
    [Journey with golang] 7. Traps
    [Journey with golang] 6. Reflection
    Codeforces Round #614 (Div. 2)
    [Journey with golang] 5. Concurrent
    2018-2019 9th BSUIR Open Programming Championship
    2019-2020 ACM-ICPC Pacific Northwest Regional Contest
    UFPE Starters Final Try-Outs 2020
    2019 ICPC Asia Taipei Hsinchu Regional Contest
    [Journey with golang] 4. Interface
  • 原文地址:https://www.cnblogs.com/wsmrzx/p/12199295.html
Copyright © 2011-2022 走看看