Javascript面向对象与继续

尽人皆知,Javascript是一门面向对象的言语,假如说针对面向对象来提问的话,我会想到两个题目,在js中,类与实例对象是怎样竖立的,类与实例对象又是怎样完成继续的。

面向对象

怎样声明一个类

ES5中,还没有类的观点,而是经由过程函数来声明;到了ES6,有了class关键字,则经由过程class来声明

      // 类的声明
      var Animal = function () {
          this.name = 'Animal';
      };

      
      // es6中class的声明
      class Animal2 {
          constructor () {
              this.name = 'Animal2';
          }

怎样竖立对象

1.字面量对象
2.显现的组织函数
3.Object.create

      // 第一种体式格局:字面量
      var o1 = {name: 'o1'};
      var o2 = new Object({name: 'o2'});
      // 第二种体式格局:组织函数
      var M = function (name) { this.name = name; };
      var o3 = new M('o3');
      // 第三种体式格局:Object.create
      var p = {name: 'p'};
      var o4 = Object.create(p);

类与继续

怎样完成继续?
继续的实质就是原型链

借助组织函数完成继续

      /**
       * 借助组织函数完成继续
       */
      function Parent1 () {
          this.name = 'parent1';
      }
      Parent1.prototype.say = function () {

      };
      function Child1 () {
          Parent1.call(this); // 或Parent1.apply(this,arguments)
          this.type = 'child1';
      }
      console.log(new Child1(), new Child1().say());

重点是这句:Parent1.call(this); 在子类的组织函数里实行父类的组织函数,经由过程call/apply转变this指向,从而致使父类组织函数实行时的这些属性都邑挂载到子类实例上去。
题目: 只能继续父类组织函数中声明的实例属性,并没有继续父类原型的属性和要领

借助原型链完成继续

      /**
       * 借助原型链完成继续
       */
      function Parent2 () {
          this.name = 'parent2';
          this.play = [1, 2, 3];
      }
      function Child2 () {
          this.type = 'child2';
      }
      Child2.prototype = new Parent2();

      var s1 = new Child2();
      var s2 = new Child2();
      console.log(s1.play, s2.play);
      s1.play.push(4);

重点就是这句: Child2.prototype = new Parent2(); 就是说 new 一个父类的实例,然后赋给子类的原型 也就是说 new Child2().__proto__ === Child2.prototype === new Parent2()当我们在new Child2()中找不到属性/要领,顺着原型链就能够找到new Parent2(),如许就完成了继续。
题目: 原型链中的原型对象是共用的,子类没法经由过程父类竖立私有属性
比方当你new两个子类s1、s2的时刻,改s1的属性,s2的属性也随着转变

组合式继续

      /**
       * 组合体式格局
       */
      function Parent3 () {
          this.name = 'parent3';
          this.play = [1, 2, 3];
      }
      function Child3 () {
          Parent3.call(this); // 父类组织函数实行了
          this.type = 'child3';
      }
      Child3.prototype = new Parent3(); // 父类组织函数实行了
      var s3 = new Child3(); 
      var s4 = new Child3();
      s3.play.push(4);
      console.log(s3.play, s4.play);

组合式就是原型链+组织函数继续,处理了前两种要领的题目,但也有不足:子类实例化时,父类组织函数实行了两次,所以有了下面的组合继续的优化1

组合继续的优化1

/**
       * 组合继续的优化1
       * @type {String}
       */
      function Parent4 () {
          this.name = 'parent4';
          this.play = [1, 2, 3];
      }
      function Child4 () {
          Parent4.call(this);
          this.type = 'child4';
      }
      Child4.prototype = Parent4.prototype;
      var s5 = new Child4();
      var s6 = new Child4();
      console.log(s5, s6);

      console.log(s5 instanceof Child4, s5 instanceof Parent4);
      console.log(s5.constructor);

实在就是把原型链继续的那句 Child4.prototype = new Parent4(); 改成 Child4.prototype = Parent4.prototype; 如许虽然父类组织函数只实行了一次了,但又有了新的题目: 没法推断s5是Child4的实例照样Parent4的实例 由于Child4.prototype.constructor指向了Parent4的实例;假如直接加一句 Child4.prototype.constructor = Child4 也不可,如许Parent4.prototype.constructor也指向Child4,就没法辨别父类实例了。

若要推断a是A的实例 用constructor

a.__proto__.constructor === A

用instanceof则不正确, instanceof 推断 实例对象的__proto__ 是否是和 组织函数的prototype 是同一个援用。若A 继续 B, B 继续 C 在该原型链上的对象 用instanceof推断都返回ture

组合继续的优化2(引荐)

/**
       * 组合继续的优化2
       */
      function Parent5 () {
          this.name = 'parent5';
          this.play = [1, 2, 3];
      }
      function Child5 () {
          Parent5.call(this);
          this.type = 'child5';
      }
      //注重此处,用到了Object.creat(obj)要领,该要领会对传入的obj对象举行浅拷贝
      //这个要领作为一个桥梁,到达父类和子类的一个断绝
      Child5.prototype = Object.create(Parent5.prototype);
      //修正组织函数指向
      Child5.prototype.constructor = Child5

组织函数属性继续和竖立子类和父类原型的链接

ES6完成继续

引入了class、extends、super关键字,在子类组织函数里挪用super()要领来挪用父类的组织函数。
在子类的组织函数中,只要挪用super以后,才够运用this关键字,不然会报错。这是由于子类实例的构建,是基于对父类实例加工,只要super要领才返回父类实例。

class Child6 extends Parent6 {
      constructor(x, y, color) {
        super(x, y); // 挪用父类的constructor(x, y)
        this.color = color;
      }
      toString() {
        return this.color + ' ' + super.toString(); // super代表父类原型,挪用父类的toString()
      }
    }

class完成道理

Class充当了ES5中组织函数在继续完成过程当中的作用
有prototype属性,有__proto__属性,这个属性在ES6中的指向有一些主动的修正。
同时存在两条继续链:一条完成属性继续,一条完成要领继续。

class A extends B {}
A.__proto__ === B;  //继续属性
A.prototype.__proto__ === B.prototype;  //继续要领

ES6的子类的__proto__是父类,子类的原型的__proto__是父类的原型。
但是在ES5中 A.__proto__是指向Function.prototype的,由于每个组织函数实在都是Function这个对象组织的,ES6中子类的__proto__指向父类能够完成属性的继续。

只要函数有prototype属性,只要对象有__proto__属性 ;但函数也有__proto__属性,由于函数也是一个对象,函数的__proto__即是 Function.prototype。

extends完成道理

//原型衔接
Man.prototype = Object.create(Person.prototype); 
// B继续A的静态属性
Object.setPrototypeOf(Man, Person);
//绑定this
Person.call(this);

前两句完成了原型链上的继续,末了一句完成组织函数上的继续。

    原文作者:hellocassiell
    原文地址: https://segmentfault.com/a/1190000014206021
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞