最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 高级JS 详解面向对象、构造函数、原型、实例与原型链、原型链继承

    正文概述 掘金(ALOKKA)   2021-01-19   467
    写在前面:

    面向对象的概念:

    在js的j世界里一般会把构造函数看成一个面向对象,其实面向对象从表面意思理解是面向全局的一个对象,构造函数就是那个面向全局的对象(函数本身就是一个对象)

    说到构造函数 不得不先说下创建对象的方式:

    1. 最简单的方式就是通过new Object()或者直接用简写形式 对象字面量 直接创建就好:
    var person = new Object()
    person.name = 'Jack'
    person.age = 18
    
    person.sayName = function () {
      console.log(this.name)
    }
    

    或者直接 ↓ (推荐)

    var person = {
      name: 'Jack',
      age: 18,
      sayName: function () {
        console.log(this.name)
      }
    }
    

    对于上面的写法固然没有问题,但是假如我们要生成两个 person 实例对象呢?

    var person1 = {
      name: 'Jack',
      age: 18,
      sayName: function () {
        console.log(this.name)
      }
    }
    
    var person2 = {
      name: 'Mike',
      age: 16,
      sayName: function () {
        console.log(this.name)
      }
    }
    

    通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。

    2. 简单方式的改进——工厂模式:

    我们可以写一个函数,解决代码重复问题:

    function createPerson (name, age) {
      return {
        name: name,
        age: age,
        sayName: function () {
          console.log(this.name)
        }
      }
    }
    

    然后生成实例对象:

    var p1 = createPerson('Jack', 18)
    var p2 = createPerson('Mike', 18)
    

    这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题。

    3. 更优雅的工厂函数:构造函数

    一种更优雅的工厂函数就是下面这样,构造函数:

    function Person (name, age) {
      this.name = name
      this.age = age
      this.sayName = function () {
        console.log(this.name)
      }
    }
    
    var p1 = new Person('Jack', 18)
    p1.sayName() // => Jack
    
    var p2 = new Person('Mike', 23)
    p2.sayName() // => Mike
    

    构造函数、原型、实例之间的关系

    高级JS 详解面向对象、构造函数、原型、实例与原型链、原型链继承

    下面的话浓缩的都是精华

    • 这里的 function Person(){} 就是构造函数
    • 构造函数 new 出来的 person就是它的实例(对象)
    • 构造函数都有一个 prototype 属性,指向另一个对象(就是它的原型(对象)),这个对象的所有属性和方法,都会被构造函数的实例继承——就是说构造函数的原型和构造函数的实例 属性和方法可以共享
    • 这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上

    拿Person举例

    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.getName = function() {
            console.log('this is lokka.');
        }
    }
    
    var p1 = new Person('tom', 5);
    console.log(p1.name); // tom
    console.log(p1.age); // 5
    p1.getName(); // this is lokka.
    
    构造函数
    1. Person 就是构造函数(其实就是一个函数)
    
    实例
    1. 构造函数Person通过 var person = new Person(); 生成的person就是构造函数的实例 实例继承原型上的属性和方法
    
    2. 实例上有个属性__proto__ (双下划线) 指向原型
    person.__proto__指向的就是构造函数Person的原型,即: person.__proto__ === Person.prototype
    
    
    原型
    1. 每写一个构造函数 就会同时生成个原型(没有构造函数中的属性或者方法,可以理解为空对象)
    2. 构造函数都有一个 `prototype` 属性,指向另一个对象(就是它的原型),这个对象的所有属性和方法,都会被`构造函数的实例继承`。——`就是说构造函数的原型和构造函数的实例 属性和方法可以共享`
    3. 原型上有constructor属性指向构造函数 原型.constructor === 构造函数
    
    构造函数和原型的关系:
    1. 每个构造函数都有一个 prototype 属性,指向另一个对象(就是它的原型),用白话讲就是每写一个构造函数 就会同时生成个原型(没有构造函数中的属性或者方法,可以理解为空对象) 通过 构造函数.prototype可以设置和访问原型中的属性和方法
    2. 同时原型中也有个constructor属性可以指向构造函数
    构造函数和实例的关系:

    用 new 关键字创建 Person 实例时,内部执行了4个操作: 1. 创建一个新对象 2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) 3. 执行构造函数中的代码 4. 返回新对象

    所以就把构造函数中的指针this 指向了新创建的实例中 实例中就可以调用构造函数中的属性和方法了

    实例和原型的关系:

    实例上有个属性__proto__ (双下划线) 指向原型 person.__proto__指向的就是构造函数Person的原型,即: person.proto === Person.prototype

    实例继承原型上的属性和方法

    更简单的原型写法

    根据前面例子的写法,如果我们要在原型上添加更多的方法,可以这样写:

    function Person() {}
    
    Person.prototype.getName = function() {}
    Person.prototype.getAge = function() {}
    Person.prototype.sayHello = function() {}
    ... ...
    
    function Person() {}
    
    Person.prototype = {
        constructor: Person,
        getName: function() {},
        getAge: function() {},
        sayHello: function() {}
    }
    

    这种字面量的写法看上去简单很多,但是有一个需要特别注意的地方。Person.prototype = {}实际上是重新创建了一个{}对象并赋值给Person.prototype,这里的{}并不是最初的那个原型对象。因此它里面并不包含constructor属性。为了保证正确性,我们必须在新创建的{}对象中显示的设置constructor的指向。即上面的constructor: Person

    完整的原型链:

    高级JS 详解面向对象、构造函数、原型、实例与原型链、原型链继承

    1. 首先我们先看左上角的 Function ,Function 比较特殊

    1. 我们要知道所有函数都是 Function 的实例,所有函数的原型都指向 Function.prototype, Function 即是构造函数 同时也是 Function 的实例,就是 Function 实例化了自己本身,所以我们可以得出:
    Function.__proto == Function.prototype
    
    1. 因为 Object 也是函数,所以我们可以得出:
    Object.__proto == Function.prototype
    
    1. 一切对象都最终继承自Object对象,Object对象直接继承自根源对象null

    其中 personPerson 构造函数对象的实例。而 Person 的原型对象同时又是构造函数对象 Object 的实例。这样就构成了一条原型链。原型链的访问,其实跟作用域链有很大的相似之处,他们都是一次单向的查找过程。因此实例对象能够通过原型链,访问到处于原型链上对象的所有属性与方法。这也是 person1 最终能够访问到处于 Object 构造函数对象上的方法的原因。

    基于原型链的特性,我们可以很轻松的实现继承。

    2. 再说一下 Funtion 和 Object 的关系

    从图上我们可以知道 Function 和 Object 是互相继承关系,因为 Function 通过原型链继承了 Object.prototype,而 Object 又继承了 Function.prototype 所以说 Function 和 Object 是互相继承关系

    3. 我们再看这个等式

    Function.constructor == Function
    

    刚开始看到这个等式我也很疑惑,看了 MDN 也没看明白,后来看了好多文档才理解 因为 .constructor 属性是原型上的属性,构造函数上没有就去原型链上查找,所以:

    Function.constructor == Function.prototype.constructor
    

    又由于

    Function.prototype.constructor == Function
    

    所以

    Function.constructor == Function
    

    所以我们可以得出结论 Function 继承自己

    原型链继承

    有关原型链继承的内容可以参考我的这篇文档——ES5中的继承和ES6中的类继承模式详解

    写在后面:

    面向对象中的知识点一直都是很难理解的,这篇文章是我很用心写的一篇文章,用我自己能理解的话术来解释构造函数 原型 实例 原型链 继承等 可能涉及到的底层原理没有那么深刻 不过通过仔细理解文章中的重点 就应该可以很好地使用面向对象了 大家都加油 文章中有什么不对的 也请大家帮我指正出来 大家共同进步!ღ( ´・ᴗ・` )比心


    起源地下载网 » 高级JS 详解面向对象、构造函数、原型、实例与原型链、原型链继承

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元