JavaScript深入理解(12课)笔记-ES6类的使用和转ES5源码阅读

12.ES6类的使用和转ES5源码阅读

ES6也称为:ES2015,ECMAScript2015
ES12 对应:ES2021

//类的声明
class Person { }  //是语法糖,对应:function Person() { }

//类的表达式
//var Animal = class { }

// 研究一下类的特点
console.log(Person.prototype)
console.log(Person.prototype.__proto__)
console.log(Person.prototype.constructor)  //[class Person]
console.log(typeof Person)  //function

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


00:43:00
//类的声明
class Person {
    //类的构造方法
    //注意:一个类只能有一个构造函数【如果包含多个构造函数,那么会抛出异常】
/*
//1.在内存中创建一个对象moni = {}
//2.将类的原型prototype赋值给创建出来的对象 moni.__proto__ = Person.prototype
//3.将对象赋值给函数的this: new绑定 this = moni
//4.执行函数体中的代码
//5.自动返回创建出来的对象
*/
    constructor(name, age) {
        this.name = name
        this.age = age
        this._address = "广州市"
    }

    //普通的实例方法
    //创建出来的对象进行访问
    // var p = new Person()
    // p.eating ()|
    eating() {
        console.log(this.name + " eating~")
    }
    running() {
        console.log(this.name + " running~")
    }

    //类的访问器方法
    get address() {
        return this._address
    }
    set address(newAddress) {
        this._address = newAddress
    }

    //类的静态方法(类方法) 【通过类名访问的方法】
    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 p1 = new Person("why", 18)
var p2 = new Person("kobe", 30)

var p = new Person("why", 18)
p.eating()
p.running()
console.log(Object.getOwnPropertyDescriptors(Person.prototype))

var pl = Person.randomPerson()
console.log(pl)


01:12:00
//ES6 继承的实现
class Person { 
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    eating() {
        console.log(this.name + " eating~")
    }
    running() {
        console.log(this.name + " running~")
    }
    static staticMethod() {
        console.log("PersonStaticMethod")
    }
}
// Student称之为子类(派生类)
class Student extends Person { 
    constructor(name, age, sno){
        //没用super时报错  【ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor】
        super(name,age)  
        this.sno = sno
    }
    studying() {
        super.eating()
        console.log(this .name + " studying~")
    }
    // 类对父类的方法的重写
    running() {
        console.log("student " + this.name +"running")
    }
    //重写静态方法
    static staticMethod() {
        //复用父类中的处理逻辑
        super.personMethod()
        console.log("StudentStaticMethod")
    }
}

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

console.log(Object.getOwnPropertyDescriptors(stu.__proto__))
console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__))
stu.eating()
stu.running()

stu.studying()
Student.staticMethod()

我们会发现在上面的代码中我使用了一个super关键字,这个super关键字有不同的使用方式:
· 注意:在子(派生)类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数
· super的使用位置有三个:子类的构造函数、实例方法、静态方法;

静态方法,比如:
Promise.all/Promise.resolve/reject    这些都是静态方法

console.log(Object.getOwnPropertyDescriptors(Person))
上面打印出来,静态方法是在函数上面的,不在函数的原型上面。

01:48:00
JS中比较新的语法,到底能不能写!【可以的】
脚手架中通过(babel)工具就能转成相应适配。

vue cli
webpack环境 (babel)
babel是什么? 将新JS、TS代码 转成-> 较低版本浏览器可以识别的代码

https://babeljs.io/
里面“TARGETS”设置:defaults, not ie 11, not ie_mob 11
即:默认,不支持IE11,不支持IE11移动端

比如设置为:defaults, not ie 10, not ie_mob 11  则 babel为下面这个:
class Person { }

//转成下面的
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)){
        throw new TypeError("Cannot call a class as a function");
    }
}
var Person = function Person() {
    _classCallCheck(this, Person);
}
new Person()

_classCallCheck作用是限制你直接调用 Person()【报错】


把下面【类无继承代码】也用 babel转一下:
class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    eating() {
        console.log(this.name + " eating~")
    }
}
再来看看babel转成的代码:

var Person = /*#__PURE__*/ (function (){
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;
})();

这边用到“立即执行函数”,其实会在整个作用域中直接执行的。简化一下去掉(头尾),只保留中间代码(……):
var Person = /*#__PURE__*/ (function (){
…………
return Person;
})();

那为什么生成的代码要用到“立即执行函数”呢?原因有两个:
1、担心在立即执行函数里面生成的东西(变量|函数等),放到了全局里面了,有可能会跟全局里面的某一段代码是有冲突的。
    放到“立即执行函数”里面,可以避免出现这种情况。
2、“立即执行函数”前面有弄了一个标记的 /*#__PURE__*/ ,它相当于一种魔法注释,它后面在代码压缩的时候:

// /*#__PURE__*/ 纯函数
// jsx -> babel -> React.createElement 【也是标记纯函数的】
// webpack 压缩 tree-shaking(因为纯函数在进行压缩时可用“tree-shaking”技术的)

即最终生成的代码从来都没有用到的,“摇树”一下,就可以把没用到的代码删掉。


继续看生成的代码:
function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}
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);
    }
}

上上面的两段代码相当于是对下面进行【封装】:
Person.prototype.eating = function() {
    console.log(this.name + " eating~")
}

02:16:10
把下面【类有继承代码】也用 babel转一下:
class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    running() {
        console.log(this.name + " running~")
    }
}

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


// babel转换后的代码
"use strict";

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);

    // 因为有限制 Person不能独立函数调用
    var _super =_createSuper(Student);

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

        // debugger;  //可以试着加入调试
        // 创建出来的对象 {name: …, age: …}
        _this = _super.call(this, name, age);
        _this.sno = sno;
        return _this;
    }
    _createClass(Student, [
        {
            key: "studying",
            value: function studying() {
                console.log(this.name + " studying~");
            }
        }
    ]);

    return Student;
})(Person);

var Student = /*#__PURE__*/ (function (_Person){
})(Person);

function _inherits(subClass, superClass) {
    if (typeof superclass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function");
    }
    // 这边用到逻辑与  superClass &&  即:superClass 有值的情况下,才用 superClass.prototype
    // Object.create会创建一个【新的对象__proto__】指向 父类的原型对象【superClass.prototype】
    // 然后,把新的对象赋值给【subClass.prototype】
    // 即是:Student.prototype = 新的对象.__proto__ = Person.prototype
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: { value: subclass, writable: true, configurable: true }
    });

    // 即是:Student.__proto__ = Person  【目的:静态方法的继承】
    // 静态方法:Student.__proto__ = Function.prototype  而 Function.prototype 中没有静态方法
    // 所以上面:Student.__proto__ = Person  因为 Person 中是有静态方法的,因为 Student中找不到就会去 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();
    return function _createSuperInternal() {
        var Super = _getPrototype0f(Derived),
            result;
        if (hasNativeReflectConstruct) {
            var NewTarget = _getPrototype0f(this).constructor;
            result = Reflect.construct(Super, arguments, NewTarget);
        } else {
            result = Super.apply(this, arguments);
        }
        return _possibleConstructorReturn(this, result);
    };
}


读源码时,这边我们先如下调试:
var stu = new Student()


具体说明:
// Super: Person
// arguments: ["why",·18]
// NewTarget: Student
Reflect.construct(Super, arguments, NewTarget);

// 会通过Super 创建出来一个实例,但是这个实例的原型constructor指向的是NewTarget        对应翻译是
// 会通过Person创建出来一个实例,但是这个实例的原型constructor指向的Person


02:53:00
阅读源码大家遇到的最大的问题:
1.一定不要浮躁
2.看到后面忘记前面的东西
  · VSCode插件中 Bookmarks的打标签的工具: command(ctrl) + alt + k
  · 读一个函数的时候忘记传进来是什么?
3.读完一个函数还是不知道它要干嘛【多积累】
4.debugger 打断点的方式


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值