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()
那么,一个实例方法,在实例上定义和在类原型上定义有什么区别?
在实例上定义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函数传递不同的参数,有不同的行为;