es6中类的使用

1 认识类

64.PNG
// 1.类的声明
class Person {

}

// babel



// 2.类的表达式(用的比较少)
// var Animal = class {
// }


// 研究一下类的特性
console.log(Person.prototype)
console.log(Person.prototype.__proto__)
console.log(Person.prototype.constructor)
console.log(typeof Person) // function

var p = new Person()
console.log(p.__proto__ === Person.prototype) // true

//总结
/* 

class Person{

}
就是 function Person(){

}
构造函数的语法糖。
65.PNG

2 class的构造方法

// 类的声明
class Person {
  // 类的构造方法
  // 注意: 一个类只能有一个构造函数,这个构造函数在new的时候就会被调用,然后按照以下步骤进行运行
  // 1.在内存中创建一个对象 moni = {}
  // 2.将类的原型prototype赋值给创建出来的对象 moni.__proto__ = Person.prototype
  // 3.将对象赋值给函数的this: new绑定 this = moni
  // 4.执行函数体(构造函数constructor)中的代码
  // 5.如果构造函数没有返回非空对象,则返回创建出来的新对象;
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}


var p1 = new Person("why", 18)
var p2 = new Person("kobe", 30)
console.log(p1, p2)

3 JavaScript 对象访问器(补充)

ECMAScript 5 (2009) 引入了 Getter 和 Setter。

JavaScript Getter(get 关键词)
//本例使用 lang 属性来获取 language 属性的值。
// 创建对象:
var person = {
  firstName: "Bill",
  lastName : "Gates",
  language : "en",
  get lang() {
    return this.language;
  },
  set lang(newLang) {
    console.log('setting lang');
    this.language = newLang;
  }
};

console.log(person.lang);//en
//意味着不对langeuage直接进行操作,而是通过lange这个方法进行对language的操作
person.lang = "CN";
console.log(person.lang);//CN

优点1:提供更简洁的语法

下面两个例子的区别在哪里?

例子 1

var person = {
  firstName: "Bill",
  lastName : "Gates",
  fullName : function() {
    return this.firstName + " " + this.lastName;
  }
};

// 使用方法来显示来自对象的数据:
document.getElementById("demo").innerHTML = person.fullName();

例子 2

var person = {
  firstName: "Bill",
  lastName : "Gates",
  get fullName() {
    return this.firstName + " " + this.lastName;
  }
};

// 使用 getter 来显示来自对象的数据:
document.getElementById("demo").innerHTML = person.fullName;

例子 1 以函数形式访问 fullName:person.fullName()。

例子 2 以属性形式访问 fullName:person.fullName。

第二个例子提供了更简洁的语法。

优点2: 提高数据质量

使用 getter 和 setter 时,JavaScript 可以确保更好的数据质量。

在本例中,使用 lang 属性以大写形式返回 language 属性的值:

实例

// Create an object:
var person = {
  firstName: "Bill",
  lastName : "Gates",
  language : "en",
  get lang() {
    return this.language.toUpperCase();
  }
};

// 使用 getter 来显示来自对象的数据:
document.getElementById("demo").innerHTML = person.lang;

4 class中的方法定义

Math

Math 不是构造函数。Math 的所有属性/方法都可以通过使用 Math 作为对象来调用,而无需创建它:

//1.绝对值方法
Math.abs(1);//1
Math.abs(-1)//1
Math.abs('-1')//隐士转换 会把字符串-1转换为数字型
Math.abd('pink')//NaN
//2.三个取整方法
//(1) Math.floor() 往下取整,往最小了取值
Math.PI      // 圆周率
Math.floor()     // 向下取整
Math.floor(1.6);//1
//(2)Math.ceil()    往上取整, 往最小了取值
Math.ceil()            // 向上取整
console.log(Math.ceil(7.004));//  8
//(3)Math.round()   四舍五入
Math.round()           // 四舍五入版 
四舍五入 其他数字都是四舍五入,但是.5特殊,它往大了取
Math.round(-1.5) //-1 
Math.round(1.5) //2
就近取整   注意 -3.5   结果是  -3 
 var names = ["abc", "cba", "nba"];
class Person{
  constructor(name, age){
    this.name = name;
    this.age = age;
    this._address = "广州市";
  }

  //这里的eating和running本质上就是放在Person的prototype上的
//console.log(Object.getOwnPropertyDescriptors(Person.prototype))

  eating(){
    console.log(this.name + "eating");
  }

  runnning(){
    console.log(this.name + "running");
  }

  //类的访问器方法
  get address(){
    console.log("访问器拦截操作");
    return this._address
  }

  set address(newAddress){
    console.log("拦截设置操作");
    this._address = newAddress;
  } 

    //以上eating和runnning是通过创建实例对象,让实例对象进行访问,比如var p = new Person() p.eating() 访问器方法也是通过实例对象去访问的 p.address p.address = "beijing"
    
  //类的静态方法(类方法) ,不需要去创建实例对象,通过类名直接去访问
  // Person.createPerson()
  //静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用static关键字来定义:

  static randomPerson(){
    var nameIndex = Math.floor(Math.random()* names.length);
    var name = names[nameIndex];
    var age = Math.floor(Math.random()*100);
    return new Person(name, age)
  }
}

var p = new Person("why", 18);
p.eating();
p.runnning();

console.log(p.address)
p.address = "北京市"
console.log(p.address)//北京市

var p1 = Person.randomPerson();//我们想创建出来一个对象,但是名字和年龄都是随机出来的
console.log(p1);
for (var i = 0; i < 50; i++) {
  console.log(Person.randomPerson())//创建出来50个person对象
}

//对象的访问器,想起内容看8.3
     var obj = {
        _name: "why",
        get name() {
          console.log("get name()");
          return this._name;
        },
        set name(newValue) {
          console.log("set name(), new value: ", newValue);
          this._name = newValue;
        },
      };

      obj.name = "shanghai";
      console.log(obj.name);
      /* 
      set name(), new value:  shanghai
      get name()
      shanghai
      */

5 class中实现继承extends

5.1.父类属性的继承(super)

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}

// Student称之为子类(派生类)
/* class Student extends Person {
  constructor(name, age, sno) {
    this.name = name;
    this.age = age;
    this.sno = sno
  }

} */

//会报错
//test.html:60 Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
//子类必须调用父类的构造方法
 // JS引擎在解析子类的时候就有要求, 如果我们有实现继承
//那么子类在子类的构造方法中, 在使用this之前,或者在return之前(默认是return这个moni对象)需要调用父类的构造方法
  //使用super关键字


  class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

}

var stu = new Student("why", 18, 111);
console.log(stu);
//Student {name: 'why', age: 18, sno: 111}
67.PNG

5.2. 父类原型方法的继承

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  running() {
    console.log(this.name + " running~")
  }

  eating() {
    console.log(this.name + " eating~")
  }

}

  class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }
  studying() {
    console.log(this.name + " studying~")
  }

}

var stu = new Student("why", 18, 111);
console.log(stu);
//Student {name: 'why', age: 18, sno: 111}
stu.studying();
stu.running();
stu.eating();

//在这里,studing是在Student的原型对象上的,running和eating是在Person的原型对象上的。
console.log(Object.getOwnPropertyDescriptors(stu.__proto__))//{constructor: {…}, studying: {…}}
console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__))//{constructor: {…}, running: {…}, eating: {…}}
//Student的原型对象的__proto—__指向Person的原型对象
console.log(Student.prototype.__proto__ === Person.prototype);//ture
console.log(stu.__proto__.__proto__ === Person.prototype);//ture

5.3. 子类对父类原型方法的重写

情况1:重写

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  running() {
    console.log(this.name + " running~")
  }

  eating() {
    console.log(this.name + " eating~")
  }

}

  class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }
  studying() {
    console.log(this.name + " studying~")
  }

   // 类对父类的方法的重写
   running() {
    console.log("student " + this.name + " running")
  }

}

var stu = new Student("why", 18, 111);
console.log(stu);
//如果子类不满意父类的running方法,可以在自己里面重新实现running方法
//到时候就会优先找自己的,而不是父类里面的

stu.running();//student why running

情况2:基于父类的逻辑重写

比如父类有下面一个方法

  personMethod() {
    console.log("处理逻辑1")
    console.log("处理逻辑2")
    console.log("处理逻辑3")
  }

但是子类基于这个方法还有更多的逻辑

  personMethod() {
    console.log("处理逻辑1")
    console.log("处理逻辑2")
    console.log("处理逻辑3")
    console.log("处理逻辑4")
    console.log("处理逻辑5")
    console.log("处理逻辑6")
  }

这时候完全像上面一样重写的话,就会出现重复代码,没有必要

这时候就需要使用super的第二个用法:

在子类的实例方法里面调用父类的方法

  // 重写personMethod方法
  personMethod() {
    // 复用父类中的处理逻辑
    super.personMethod()

    console.log("处理逻辑4")
    console.log("处理逻辑5")
    console.log("处理逻辑6")
  }

整体代码:

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  running() {
    console.log(this.name + " running~")
  }

  eating() {
    console.log(this.name + " eating~")
  }
  personMethod() {
    console.log("处理逻辑1")
    console.log("处理逻辑2")
    console.log("处理逻辑3")
  }

}

  class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }
  studying() {
    console.log(this.name + " studying~")
  }

   // 重写personMethod方法
   personMethod() {
    // 复用父类中的处理逻辑
    super.personMethod()

    console.log("处理逻辑4")
    console.log("处理逻辑5")
    console.log("处理逻辑6")
  }

}

var stu = new Student("why", 18, 111);
console.log(stu);

stu.personMethod();
/* 
处理逻辑1
test.html:64 处理逻辑2
test.html:65 处理逻辑3
test.html:84 处理逻辑4
test.html:85 处理逻辑5
test.html:86 处理逻辑6
*/

5.44. 静态方法的继承

就表示该方法不会被实例继承,而是直接通过来调用,这就称为静态方法

es5

类:

        function Person(name,age){
            this.name = name;
            this.age = age;
            this.run = function(){
                console.log(`${this.name}----${this.age}`)
            }
        }
 
    原型: 
        Person.prototype.sex='男';
        Person.prototype.work = function(){
            console.log(`${this.name}----${this.age}----${this.sex}`)
        }
        
    静态方法:
        Person.setName = function(){  
            console.log("静态方法")
        }
 
        var p = new Person('张三',11);
       类Person直接调用: Person.setName()      //输出:静态方法
       实例对象不能调用:  p.setName() 
       实例对象调用类的属性方法:p.run();    //输出:张三----11
       实例对象调用原型方法:p.work(); 

es6

类:
        class Person{    // 作为对象的模板,可以看作构造函数的另一种写法
          constructor(name,age){  /** 类的构造函数,new 命令创建对象实例时,自动调用该方法,用来接收参数 **/
            this.name = name;  
            this.age = age;
          } 
          getName(){
            console.log(this.name);
          }
          setName(name){
              this.name = name
          }
          getInfo(){
              console.log(`姓名:${this.name}---年龄:${this.age}`)
          }
      静态方法:
          static work(){
              console.log("这是es6里的静态方法")
          }
        }

        var p = new Person('李四','20')  
        p.getName()  //输出:李四
        p.setName('张三') 
        p.getName()  //输出:张三
        Person.work();  //这是es6里的静态方法
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  running() {
    console.log(this.name + " running~")
  }

  eating() {
    console.log(this.name + " eating~")
  }

  static staticMethod() {
    console.log("PersonStaticMethod");
  }

}

  class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

 

}

var stu = new Student("why", 18, 111);

//调用Person的静态方法
Student.staticMethod();//PersonStaticMethod

5.5. 子类对父类静态方法的重写

情况1:完全重写

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  running() {
    console.log(this.name + " running~")
  }

  eating() {
    console.log(this.name + " eating~")
  }

  static staticMethod() {
    console.log("PersonStaticMethod")
  }

}

  class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

    // 重写静态方法
    static staticMethod() {
    console.log("StudentStaticMethod")
  }
}

var stu = new Student("why", 18, 111);

var p = new Person();
console.log(Person.hasOwnProperty("name")); //true

//调用Person的静态方法
Student.staticMethod();//StudentStaticMethod

情况2:基于父类的重写

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  running() {
    console.log(this.name + " running~")
  }

  eating() {
    console.log(this.name + " eating~")
  }

  static staticMethod() {
    
    console.log("PersonStaticMethod")
  }

}

  class Student extends Person {
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

    // 重写静态方法
    static staticMethod() {
    super.staticMethod()
    console.log("StudentStaticMethod")
  }

 

}

var stu = new Student("why", 18, 111);

//调用Person的静态方法
Student.staticMethod();
//PersonStaticMethod
//StudentStaticMethod

6 ES6转ES5的代码

Babel · The compiler for next generation JavaScript (babeljs.io)

IE:Edge 15可以支持 96% 的 ES6 新特性。Edge 14 可以支持 93% 的 ES6 新特性。(IE7~11 基本不支持 ES6)

打开网站以后,版本选择7.15.8

target里面设置

defaults, not ie 10, not ie_mob 11

es5

class Person{}

es5

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var Person = function Person() {
  "use strict";
//检查this是不是Person创建出来的,
//是的话,那么就可以用instanceof来判断,instanceof就是判断某个对象是不是另一个对象的实例
  _classCallCheck(this, Person);
};

//_classCallCheck的作用就是不让你把Person当成一个普通的函数去调用它
//比如Person() 这种情况下this是window或者undifined
//而是希望new Person()
//直接Person()就会报错。
//new Person()的化,就是创建一个空对象,空对象的--protp--指向Perosn.prototype,然后this= moni,执行函数体里面的内容。返回对象。所以_classCallCheck(this, Person);里面的this指向的是moni这个对象。因为moni的__proto__已经指向了Person.porototype了,所以可以说在执行函数体前moni就是Person的实例了。

es6

class Person{
  constructor(name, age){
    this.name = name;
    this.age = age;
  }
  eating(){
    console.log(this.name + "eating")
  }
}

es5

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    //获取当前对象
    var descriptor = props[i];
    //console.log(descriptor);//{key: 'eating', value: ƒ}
    //console.log(descriptor.enumerable);//undefined
    
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
   //console.log(descriptor);
      /* configurable: true
      enumerable: false
      key: "eating"
      value: ƒ eating()
      writable: true */
  }
}

/*

  相当于给Person.prototype里面加入eating方法
*/

function _createClass(Constructor, protoProps, staticProps) {
     //如果是构造函数的话,直接给Person.prototype添加属性。
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    //如果是构造函数的话,直接给Person添加方法。
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

//在这里,为什么要使用立即执行函数呢?两个原因
//1.它可以独立创建一个作用域,不会污染到全局,每个立即执行函数内的变量都是局部变量,不会有命名冲突的情况。
//2.方便进行进行tree-shaking处理
//标记这个函数为纯函数/*#__PURE__*/--》没有副作用
//纯函数被webpack做压缩的时候,是要做tree-shaking处理
//到时候对代码进行压缩的时候,发现这个函数没有被用过,在tree-shaking的时候,进行优化,直接删除掉这些代码,打包出来的包size小
var Person = /*#__PURE__*/ (function () {
  "use strict";

  function Person(name, age) {
    _classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }

  _createClass(Person, [
    {
      key: "eating",
      value: function eating() {
        console.log(this.name + "eating");
      }
    }
  ]);

  return Person;
})();

7 es6转es5的继承的代码

es6

class Person{
  constructor(name, age){
    this.name = name;
    this.age = age;
  }

  running(){
    console.log(this.name + " runnning");
  }
}

class Student extends Person{
  constructor(name, age, sno){
    super(name, age);
    this.sno = sno;
  }
  studying(){
    console.log(this.name + " studying");
  }


}

superClass && superClass.prototype

在superClass有值的时候拿到superClass.prototype

es5


function _typeof(obj) {
  "@babel/helpers - typeof";
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    _typeof = function _typeof(obj) {
      return typeof obj;
    };
  } else {
    _typeof = function _typeof(obj) {
      return obj &&
        typeof Symbol === "function" &&
        obj.constructor === Symbol &&
        obj !== Symbol.prototype
        ? "symbol"
        : typeof obj;
    };
  }
  return _typeof(obj);
}

function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: { value: subClass, writable: true, configurable: true }
  });

  /* 这里的目的是让Student.__proto__ = Person 
为了实现静态方法的继承
比如Student.staticMethod()
这里staticMathod没有在Student对象里面,
我们就要去Student.__proto__里面去找
Student.__proto__默认指的是Function.prototype,但是Function.prototype里面也没这个方法
我们就通过Student.__proto__ = Person , 这里Person里面有这个方法
*/
  if (superClass) _setPrototypeOf(subClass, superClass);
}


function _setPrototypeOf(o, p) {
  _setPrototypeOf =
    Object.setPrototypeOf ||
    function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };
  return _setPrototypeOf(o, p);
}

function _createSuper(Derived) {
  var hasNativeReflectConstruct = _isNativeReflectConstruct();//判断支不支持reflect
  return function _createSuperInternal() {
      //在这里,拿到Student的隐式原型,就是Person这个Function了。Super就是Function.
    var Super = _getPrototypeOf(Derived),
      result;
    if (hasNativeReflectConstruct) {//ture
      var NewTarget = _getPrototypeOf(this).constructor;//this就是moni
        //NewTarget是Student
        //arguments:[]传进来的参数
        //Super是Person
        // Reflect.construct通过Super创建出来一个实例对象,但是这个实例对象的原型constructor指向的是newtarget
         // Reflect.construct通过Person创建出来一个实例对象,但是这个实例对象的原型constructor指向的是Student
      result = Reflect.construct(Super, arguments, NewTarget);
        //创建出来的就是Student的一个实例,里面有属性name,age,也就是Person里面的实例属性。
    } else {
      result = Super.apply(this, arguments);
    }
    return _possibleConstructorReturn(this, result);
  };
}

function _possibleConstructorReturn(self, call) {
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    return call;
  } else if (call !== void 0) {
    throw new TypeError(
      "Derived constructors may only return object or undefined"
    );
  }
  return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
  if (self === void 0) {
    throw new ReferenceError(
      "this hasn't been initialised - super() hasn't been called"
    );
  }
  return self;
}

function _isNativeReflectConstruct() {
  if (typeof Reflect === "undefined" || !Reflect.construct) return false;
  if (Reflect.construct.sham) return false;
  if (typeof Proxy === "function") return true;
  try {
    Boolean.prototype.valueOf.call(
      Reflect.construct(Boolean, [], function () {})
    );
    return true;
  } catch (e) {
    return false;
  }
}

function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf
    : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
      };
  return _getPrototypeOf(o);
}

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

var Person = /*#__PURE__*/ (function () {
  "use strict";

  function Person(name, age) {
    _classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }

  _createClass(Person, [
    {
      key: "running",
      value: function running() {
        console.log(this.name + " runnning");
      }
    }
  ]);

  return Person;
})();

var Student = /*#__PURE__*/ (function (_Person) {
  "use strict";

  _inherits(Student, _Person);
  /* 
  
  和以前寄生时候实现的方法是一样的,让Student继承Person,但是babel里面多了一个静态方法继承
  就是让Student.__proto__=Person
   if (superClass) _setPrototypeOf(subClass, superClass);

function inheritPrototype(SubType, SuperType) {
    SubType.prototype = Object.create(SubType.prototype);
    Object.defineProperty(SuperType.prototype, "constructor", {
      enumerable: false,
      configurable: true,
      writable: true,
      value: SubType
    })
  }

  */

  var _super = _createSuper(Student);
    //在这里,通过_createSuper创建了另外的一个函数,返回值是函数,赋值给了_super

  function Student(name, age, sno) {
    var _this;

    
    _classCallCheck(this, Student);
    //以前直接Person.call(this, name, age, friend)来实现的
    //但是Person不能直接调用
    //Person()在es6里面
    //这个this指向的是实例moni
    _this = _super.call(this, name, age);
      //返回的这个_this就是一个Person创建出来的实例对象,但是隐式原型指向的是Student。
      //可以理解为这个_this,和我们以前的mini,也就是this没有任何关系了。它是由Reflect.construct创建出来的。把我们的moni给换了,但是本质没有变,就是它的隐式原型指向Student的prototype。然后它还执行力Person函数里面的代码体。这个代码体里面就是给实例对象赋值。
      //后续的操作也只使用_this
    _this.sno = sno;
    return _this;
  }

  _createClass(Student, [
    {
      key: "studying",
      value: function studying() {
        console.log(this.name + " studying");
      }
    }
  ]);

  return Student;
})(Person);


// Super: Person
// arguments: ["why", 18]
// NewTarget: Student
// 会通过Super创建出来一个实例, 但是这个实例的原型constructor指向的是NewTarget
// 会通过Person创建出来一个实例, 但是这个实例的原型constructor指向的Person
//Reflect.construct(Super, arguments, NewTarget);

8 继承内置类

我们也可以让我们的类继承自内置类,比如Array

一个类默认是继承自Object类

class Foo extends Object{}和Class Foo是一样的

直接写的话FOO的prototype是自己的,里面的隐式原型就是执行Object.prototype。

你写extends的化,就是让类里面让Foodeprototype指向Object的prototype。


// class Person {

// }

// class Student extends Person {

// }

class HYArray extends Array {
  firstItem() {
    return this[0]
  }

  lastItem() {
    return this[this.length-1]
  }
}

var arr = new HYArray(1, 2, 3)
console.log(arr.firstItem())//1
console.log(arr.lastItem())//3
//如果子类没有传constructor,就会return _super.apply(this, arguments);
//它和以前一样,就是返回一个对象,这个对象是Person的实例对象,但是隐式原型却指向Student。比较计算没有实例属性的代码执行,student函数体内也是要有代码的,可以看先是有了一个包装后的this对象,子类有单独的属性赋值就赋值,没有就算了,然后返回这个对象。

9 类的混入mixin

JavaScript的类只支持单继承:也就是只能有一个父类

  1. 那么在开发中我们我们需要在一个类中添加更多相似的功能时,应该如何来做呢?
  2. 这个时候我们可以使用混入(mixin);就是创建一个函数。
class Person {

}

/* class Runner{
running(){

}
}

*/
function mixinRunner(BaseClass) {
  class NewClass extends BaseClass {
    running() {
      console.log("running~")
    }
  }
  return NewClass
}


// 在JS中类只能有一个父类: 单继承
class Student extends Person {

}
//我有个对象,想给使用Student这个类,又想使用running这个方法,这个时候使用混入,返回一个新的class,这个class有自己的原型方法,也继承了Student这个类
var NewStudent = mixinRunner(Student)
var ns = new NewStudent()
ns.running()//running~
68.PNG

混入两个类

class Person {

}

/* class Runner{
running(){

}
}

class Eater{
eatting(){

  }

} */
function mixinRunner(BaseClass) {
  class NewClass extends BaseClass {
    running() {
      console.log("running~")
    }
  }
  return NewClass
}

function mixinEater(BaseClass) {
  return class extends BaseClass {
    eating() {
      console.log("eating~")
    }
  }
}

// 在JS中类只能有一个父类: 单继承
class Student extends Person {

}

var NewStudent = mixinEater(mixinRunner(Student))
var ns = new NewStudent()
ns.running()
ns.eating()

弊端:以上方法没办法混入属性,或者在构造函数内传入一些参数

10 JavaScript中的多态

69.PNG
// 传统的面向对象多态是有三个前提:
// 1> 必须有继承(是多态的前提)
// 2> 必须有重写(子类重写父类的方法),如果不重写,调用的都是父类的getArea()(同一种行为),认为是没有多态的体现的。重写的话,传入r的情况,调入的是Rectangle类里面的getArea()方法,
//传入c的情况下,调用的是Circle类里面的getArea()方法,
//这样,就可以认为不同的数据类型执行同一个操作时, 如果表现出来的行为(形态)不一样,
// 3> 必须有父类引用指向子类对象

// Shape形状
class Shape {
  getArea() {}
}

class Rectangle extends Shape {
  getArea() {
    return 100
  }
}

class Circle extends Shape {
  getArea() {
    return 200
  }
}

var r = new Rectangle()
var c = new Circle()

// 多态: 当对不同的数据类型执行同一个操作时, 如果表现出来的行为(形态)不一样, 那么就是多态的体现.
function calcArea(shape: Shape) {
  console.log(shape.getArea())
}

calcArea(r)
calcArea(c)

export {}
// 多态: 当对不同的数据类型执行同一个操作时, 如果表现出来的行为(形态)不一样, 那么就是多态的体现.
function calcArea(foo) {
  console.log(foo.getArea())
}

var obj1 = {
  name: "why",
  getArea: function() {
    return 1000
  }
}

class Person {
  getArea() {
    return 100
  }
}

var p = new Person()

calcArea(obj1)
calcArea(p)


// 也是多态的体现
function sum(m, n) {
  return m + n
}

sum(20, 30)
sum("abc", "cba")

11 总结

类里面的原型方法和静态方法是在使用类以前就确定好了,类里面的实例方法和属性是在new 类的时候才执行的,无论是写在constructor里面的,或者是直接写在外面的。这样一个Person函数就有了。

不调用的化,它就是这么一个存在,有自己的静态方法和自己的原型方法。

定义Student extends Person的时候,会把Person这个函数传给Student这个函数,然后再去定义Student这个函数。

先让Student的prototype这个对象的隐式原型指向Person的prototype,然后让Student的隐式原型指向Perosn.

然后往Student上面添加原型函数和静态方法。Student函数的定义就完成了。

后面new的时候,就会执行Student函数里面的内容,只要就是调用父类函数(父类函数主要就是实例的属性的赋值的语句,其他的在定义的时候都确定好了),接下来执行自己单独的属性,进行赋值。

大概就是Person函数定义以后就只是

function Person(name, age) {
   

    this.name = name;
    this.age = age;
  }

然后声明Studeng类的时候会使用上面的函数,

第一步:先让Student的prototype这个对象的隐式原型指向Person的prototype,(完成原型的继承)

第二步:然后让Student的隐式原型指向Perosn.(完成静态的继承)

然后Student就定义完了,就变成了

function Student(name, age, sno) {
    var _this;
    _this = _super.call(this, name, age);//就算子类没有constructor这个函数,这行代码也是执行的。
    _this.sno = sno;
    return _this;
  }

然后new Student的时候就先替换moni的这个this,重新通过Reflect.construct创建一个Person的实例,但是隐式原型是Student的对象,然后接下来继续往student上面添加自己独自的属性。

这也就解释了为什么this子类constructor中使用this的时候必须调用super,因为不调用super,就没有办法返回这个新包装的this,后续就返回一个_this,就是一个普通的对象,这个对象有一个子类的属性,但是里面的隐式原型就是执行Object.prototype, 和student就没啥关系了。不过这么写浏览器就直接报错了

  class Student extends Person {
        constructor() {
          this.name = "studentttt";
        }
      }  
function Student(name, age, sno) {
    var _this;
    _this.sno = sno;
    return _this;
  }
Must call super constructor in derived class before accessing 'this' or returning from derived constructor
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容