视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
常见模式与原型链的理解
2020-11-27 20:19:23 责编:小采
文档
 内容:

  • 创建对象的几种模式以及创建的过程

  • 原型链prototype的理解,以及prototype__proto__[[Prototype]])的关系

  • 继承的几种实现


  • 1.常见模式与原型链的理解

    a.构造函数创建

    function Test() {// 
    }

    流程

  • 创建函数的时候会默认为Test创建一个prototype属性,Test.prototype包含一个指针指向的是Object.prototype

  • prototype默认会有一个constructor,且Test.prototype.constructor = Test

  • prototype里的其它方法都是从Object继承而来


  • 示例
    // 调用构造函数创建实例
    var instance = new Test()

    此处的instance包含了一个指针指向构造函数的原型,(此处的指针在chrome里叫__proto__,也等于[[Prototype]]


    示例

    b.原型模式
    由上我们可以知道,默认创建的prototype属性只拥有constructor和继承至Object的属性,原型模式就是为prototype添加属性和方法

    Test.prototype.getName = ()=> {
     alert('name')
    }

    此时的instance实例就拥有了getName方法,因为实例的指针是指向Test.prototype的

    instance.__proto__ === Test.prototype

    如下图所示


    7RVF]E5@IX$)`IVJ3BOSY.png

    这里我们可得知:实例instance与构造函数之间是通过原型prototype来相关联的。

    c.组合模式
    这种模式我们用的最多,其实也是原型模式的另一种写法,只不过有一点小区别而已

    function Test() {}
    
    Test.prototype = {
     getName() {
     alert('name')
     }
    }

    我们经常会这么直接重写prototype方法,由上我们可知,prototype会默认自带constructor属性指向构造函数本身,那么重写以后呢?

    Test.prototype.constructor === Object 
    // 而并不等于Test了// 因为重写以后相当于利用字面量方式创建一个实例对象,这个实例的构造函数是指向Object本身的

    当然我们也可以手动赋值constructor

    Test.prototype = {
     constructor: Test,
     getName() {
     alert('name')
     }
    }

    那么又会有疑问了constructor要不要有何意义?我觉得constructor意义仅仅是为了来鉴别原型所属的构造函数吧。

    当需要获取某个属性的时候,会先从实例中查找,没有就根据指针所指向的原型去查找,依次向上,直到实例的指针__proto__指向为null时停止查找,例如:

    // 1 读取nameinstance.name 
    
    // 2 instance.__proto__ === Test.prototypeTest.prototype.name
    
    // 3 Test.prototype.__proto__ === Object.prototypeObject.prototype.name
    
    // 4Object.prototype.__proto__ === null

    当找到了这个属性就会直接返回,而不会继续查找,即使这个属性值为null,想要继续查找,我们可以通过delete操作符来实现。

    由这里我们自然可以想到Array, Date, Function, String,都是一个构造函数,他们的原型的指针都是指向Object.prototype,它们就像我这里定义的Test一样,只不过是原生自带而已

    d.几个有用的方法

  • Object.getPrototypeOf() 获取某个实例的指针所指向的原型

    Object.getPrototypeOf(instance) === Test.prototype
  • hasOwnProperty 判断一个属性是存在于实例中还是存在于原型中,如图所示:


    NY~N}CNR`}8W%4QA$M8LFE4.png
  • in操作符,无论该属性是否可枚举

    'name' in instance // true
    'getName' in instance // true

    无论属性是在实例中,还是在原型中都返回true,所以当我们需要判断一个属性存在与实例中,还是原型中有2种办法

    // 一种就是使用hasOwnProperty判断在实例中
    // 另一种判断在原型中
    instance.hasOwnProperty('getName') === false && 'getName' in instance === true
  • for ... in操作符也是一样的,但只会列出可枚举的属性,ie8版本的bug是无论该属性是否可枚举,都会列出


    D(%S__GN8404{H9X6PW$DVK.png


    name是在实例中定义的,getName是在原型中定义的

  • Object.keys()则不一样,它返回一个对象上所有可枚举的属性,仅仅是该实例中的

    Object.keys(instance)// ["name"]
  • e.总结
    以上讨论了构造函数,原型和实例的关系:

  • 每个构造函数都有原型对象

  • 每个原型对象都有一个constructor指针指向构造函数

  • 每个实例都有一个__proto__指针指向原型

  • 2.继承

    其实是一个道理,这里我们不难想到,将Child.prototype指向parent实例,就是利用原型实现的继承,而为了每个实例都拥有各自的colors和name,也就是基础属性,在Child的构造函数中call调用了Parent的构造函数,相当于每次实例化的时候都初始化一遍colors和name,而不是所有实例共享原型链中的colors和name。

    继承的实质是利用构造函数的原型 = 某个构造函数的实例,以此来形成原型链。例如

    // 定义父类function Parent() {}Parent.prototype.getName = ()=> {
     console.log('parent')
    }// 实例化父类let parent = new Parent()// 定义子类function Child() {}
    Child.prototype = parent 
    // 实例化子类let child = new Child()
    
    child.getName() // parent// 此时
    child.constructor === parent.constructor === Parent

    a.最经典的继承模式

    function Parent(name) {this.name = namethis.colors = ['red']
    }
    Parent.prototype.getName = function() {console.log(this.name)
    }// 实例化父类let parent = new Parent()function Child(age, name) {
     Parent.call(this, name)this.age = age
    }
    Child.prototype = parent 
    // 实例化子类let child = new Child(1, 'aaa')
    child.getName() // parent

    这里会让我想到ES6中的class继承

    class Parent {
     constructor(name) {this.name = namethis.colors = ['red']
     }
     getName() {
     console.log(this.name)
     }
    }class Child extends Parent {
     constructor(age, name) {super(name)
     }
    }
    
    let child = new Child(1, 'aaa')
    child.getName() // parent

    下载本文
    显示全文
    专题