JS高级—09—类;

一、类

 

类只是构造函数的语法糖,本质上js引擎还是会将其解析为构造函数;

类的继承只是函数原型链的语法糖,

 

 

 

1.2类的构造函数 

 

 

 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_classes#%E5%AE%9E%E4%BE%8B%E6%96%B9%E6%B3%95

 根据元素类型分:getter、setter、字段、方法

根据存储位置分:静态、实例(实例对象上)、实例(类原型对象上)

根据可见性分:私有、共有

 

可见性主要是,私有的只能在类内部使用,不能在类外部使用,即使类实例调用私有属性也不行。

存储位置:

  • 静态字段和静态方法全部放置在类这个对象上了。
  • 在class类里定义的字段、在constructor定义的字段和方法,全部都在实例自己这个对象上,自己修改不会影响其他实例。
  • 在class类里定义的方法,全部都在类的prototype这个对象上,将会被所有的实例共享,自己把这个方法修改了会影响其他实例调用。
      class Foo {
        age = '18'
        constructor(name) {
          if (name) this.name = name
          this.run = function () {
            console.log('run')
          }
        }

        eat() {
          console.log('eat')
        }
      }
      const foo1 = new Foo('tom')
      const foo2 = new Foo()
        console.log(foo1,foo2);

 

 那么,一个实例方法,在实例上定义和在类原型上定义有什么区别?

在实例上定义run(),每次创建实例时都要重新创建一次方法,两个实例的run()方法,引用地址是不一样的。

但是类原型的eat()只会创建一次,多个实例的eat(),引用地址是一样的。

 

 

 

 

 

 

1.3类的实例方法

在上面我们定义的属性都是直接放到了this上,也就意味着属性都是放到了new一个类时在堆内存创建出的新对象中;

 

类将函数声明和函数的构造函数体区分了开来; 

 当我们在类里面定义时时,本质上还是放到了Person.prototype上,我们可以通过getOwnProertyDescriptor()看到; 

 

 

1.4类的访问器方法

其实就是对象的数据劫持,可以实现私有属性或者数据劫持

//setter和getter这就是访问器
const obj = {
  _name: 'kobe',
  set name(newValue) {
    this._name = newValue;
  },
  get name() {
    return this._name;
  }
};

//setter和getter这就是访问器
const obj2 = {  };
Object.defineProperty(obj2, '_name',{
    configurable:false,
    enumerable:false,
    writable:true,
    value:'kobe'
})
Object.defineProperty(obj2, 'name', {
  configurable: true,
  enumerable: true,
  set(newValue) {
      console.log('将_name编程私有属性;');
      console.log('数据劫持操作;');
    this._name = newValue;
  },
  get() {
    return this._name;
  }
});

obj.name = 'james'
obj2.name ='james'
console.log(obj.name);  //james
console.log(obj2);  //{ name: [Getter/Setter] }
console.log(obj2.name);  //james

//setter和getter这就是访问器
class Person {
    constructor(){
        this._name = 'kobe';
    }

    set name(newValue){
        this._name = newValue;
    }
    get name(){
        return this._name;
    }
}
const obj3 = new Person();
obj3.name = 'james'
console.log(obj3.name);  //james


//类本质上就是构造函数的语法糖,访问器在猴枣函数构造函数中就是这样实现的
function PersonFn() {
    this._name = 'kobe';
    Object.defineProperty(this,'name',{
        configurable:true,
        enumerable:true,
        set(newValue){
            this._name = newValue;
        },
        get(){
            return this._name;
        }
    })
}
const obj4 = new PersonFn();
obj4.name = 'james'
console.log(obj4.name);  //james

 

 

 

1.5类的静态方法;

可以直接通过类名调用方法;

 

1.6类目前有私有访问权限了;

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_class_fields

 

 

 

 

 

 

 

二、super关键字

 

 

 

super主要有三种用法:super()继承父类的属性即子类的构造函数中使用、super.xxx()继承父类的实例方法、super.xxx()继承父类的静态方法

 

第一种,

super()就像是借用构造函数继承,都是用来调用父类构造函数的;然后在父类构造函数里就可以继承父类的属性了;

调用了super,就会把我们子类的this传过去,因为this指向的是子类实例对象,所以父类构造函数里所有的对this的操作,都是对子类实例的操作,所以这里打印‘kobe’;

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

}

class Student extends Person{
    constructor(){
        super();
    }
}


console.log(new Student().name);  //kobe

 

 

super.xxx()这种可以在子类的实例方法里调用,主要是方法的重写,但是如果有一些操作步骤子类和父类都有,那么子类在重写父类的方法时,就可以通过super.xxx()先把父类的这个方法执行一遍;

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

    eating(){
        console.log('111');   //子类和父类都有的吃饭前操作步骤
        console.log('111');
        console.log('111');
        console.log('吃ing');
    }

}

class Student extends Person{
    constructor(){
        super();
    }

    eating(){
        super.eating();
        console.log('我是学生,吃ing');
    }
}


new Student().eating();
// 111
// 111
// 111
// 吃ing
// 我是学生,吃ing

 

 

 

super.xxx()这种可以在子类的静态方法里调用,同样的道理;

 

 

 

三、类的继承

就是以前的寄生组合式继承的语法糖而已;

看老师的课,和这边文章;https://blog.csdn.net/qq_36297981/article/details/90146017

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

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

//   static staticMethod() {

//   }
// }

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

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

// babel转换后的代码    babel在线转换工具:https://babeljs.io/
'use strict'; 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) { //edgecase:superclass必须是一个函数 if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function'); } //这不就是原型式继承吗?通过Object.creat()方法,创建一个原型是Person类的对象,然后赋值给子类原型; subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); // 这个_setPrototypeOf目的是父类静态方法的继承 //调用静态方法是Student.studying(),Student类或者说Student对象除了有函数原型还有对象原型应该是指向Function.prototype的对吧? //现在让它从指向Function.prototype指向Person.prototype; // Student.__proto__ = Person //这样就可以Student.eating()了; if (superClass) _setPrototypeOf(subClass, superClass); } // o: Student // p: Person // 静态方法的继承 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(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } 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 () { function Person(name, age) { _classCallCheck(this, Person); this.name = name; this.age = age; } _createClass(Person, [ { key: 'running', value: function running() { console.log(this.name + ' running~'); } } ]); return Person; })(); var Student = /*#__PURE__*/ (function (_Person) { // 实现之前的寄生式继承的方法 _inherits(Student, _Person); var _super = _createSuper(Student); function Student(name, age, sno) { var _this; //判断必须通过new的方式调用student,不然this不是student的实例; _classCallCheck(this, Student); // Person不能被当成一个函数去调用 // Person.call(this, name, age) debugger; // 创建出来的对象 {name: , age: } //这行代码像我们寄生式组合继承的Person.call(this,name,age) _this = _super.call(this, name, age); _this.sno = sno; // {name: , age:, sno: } return _this; } //给Student的函数原型添加方法,这样每个实例都能调用; _createClass(Student, [ { key: 'studying', value: function studying() { console.log(this.name + ' studying~'); } } ]); return Student; })(Person); var stu = new Student(); //解释Reflect.construct(Super, arguments, NewTarget);这行代码的的意思,和上文无关; // Super: Person // arguments: ["why", 18] // NewTarget: Student // 会通过Super创建出来一个实例, 但是这个实例的原型constructor指向的是NewTarget // 会通过Person创建出来一个实例, 但是这个实例的原型constructor指向的Student // Reflect.construct(Super, arguments, NewTarget);

 

 

 

 

四、类的继承之继承系统内置类

 

 

 

 

五、类的混入

了解即可;

 

 

 

 

 

 

 

 

 

六、多态

每个语言的多态都不一样,java的是父类引用指向子类对象,导致这个变量产生多态;

js其实也可以;

更广泛的定义的话:不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现。那js肯定是的;比如都对sum函数传递不同的参数,有不同的行为;

 

 

 

 

 

 

posted @ 2022-05-18 23:27  Eric-Shen  阅读(62)  评论(0编辑  收藏  举报