• es6入门5--class类的基本用法


    在ES6之前,准确来说JavaScript语言并无类的概念,却有模拟类的做法。相比在类似java这类传统面向对象语言中通过类来生成实例,js则通过构造函数模拟类来生成实例。

    这是因为在JS设计初期,作者Brendan Eich选择使用原型来描述对象而非类,但被管理层要求模仿java,因此引入了new this等语言特性,也就是我们所使用的构造函数做法。

    那么自ES6起,JavaScript正式引入了class关键字,自此我们也可以通过class来定义类了。

    但需要清楚的是ES6中class只是构造函数的一种语法糖,并非新鲜玩意,class能实现的,我们通过ES5构造函数同样可以实现。

    本篇文章只是ES6入门学习笔记,想了解详细文档请阅读阮一峰大神的 ECMAScript 6 入门,那么本文开始。

     一、class写法与构造函数的部分区别

    1.写法变化

    在ES6之前,我们模拟一个类的做法是通构造函数

    let Parent = function (name, age) {
        this.name = name;
        this.age = age;
    };
    Parent.prototype.sayName = function () {
        console.log(this.name)
    };
    let child = new Parent('echo', 26);
    child.sayName();//echo

    ES6 class实现更像类的写法,我们改写上面的方法:

    class Parent {
        constructor(name, age) {
            this.name = name;
            this.age = age;
        };
        sayName() {
            console.log(this.name);
        };
    };
    let child = new Parent('echo', 26);
    child.sayName(); //echo

    简单对比下,写法上主要这几点变化:

    1.构造函数名Parent在class写法时变成了类名,但调用方式不变,依然通过new关键字创建实例。

    2.构造函数中this相关操作,在class写法时归纳到了constructor方法中,也就是说ES5的构造函数Parent对应ES6的Parent类中的constructor构造方法。

    3.ES5中原型上的方法sayName在ES6 class写法中直接写在了内部,同时省略了function关键字。

    所以对于方法添加,下面这两种写法是等效的:

    // ES6
    class Parent {
        constructor() {};
        sayName() {};
        sayAge() {};
    };
    // ES5
    let Parent = function () {};
    Parent.prototype = {
        constructor: function () {},
        toString: function () {},
        sayAge: function () {}
    };

    可以看到,ES5写法一样可以一次批量在原型上添加方法,但我发现,class类不能直接这么做,以下有三种给class类原型添加方法的做法,我分别输出了它们:

    // 写法一
    class Point {
        constructor() {}
        toString() {}
        toValue() {}
    };
    
    // 写法二
    class Point {};
    Point.prototype = {
        constructor() {},
        toString() {},
        toValue() {},
    };
    
    // 写法三
    class Point {};
    Point.prototype = {
        constructor: function () {},
        toString: function () {},
        toValue: function () {},
    };

    通过对比可以发现,第二、三种写法先定义class类,再使用prototype在原型上添加方法对于class类无效,方法并没有被添加进去。

    你肯定纳闷了,外部添加无效,那为啥那偏偏有个constructor呢,这是因为constructor方法是class类自带的

    class Parent {
    
    };
    //等同于 class Parent { constructor() {} };

    也就是说当要给class类原型添加方法时,如果使用ES5的添加做法并不会生效;当然也不是写在外部就会失效,通过assign方法还是可以做到这一点:

    class Point {
        constructor() {};
    };
    
    Object.assign(Point.prototype, {
        toString() {},
        toValue() {}
    });

    2.类创建实例必须使用new

    通过class类创建实例必须使用new关键字,不使用会报错,这点与构造函数不同,在ES5中其实我们不使用new也能调用构造函数创建实例,虽然这样做不符合规范。

    class Parent {
        constructor() {}
    };
    let son = Parent();//报错
    
    let Parent = function (name) {
        this.name = name;
    };
    let son  = Parent();//不符合规范

    3.类的内部方法无法枚举

    最后一点区别是,class类内部定义的方法无法枚举,也就是无法通过Object.keys方法获取,但在ES5中keys方法是有效的。

    class Point {
        constructor() {};
        toString() {};
        toValue() {};
    };
    Object.keys(Point);//[]
    Object.getOwnPropertyNames(Point.prototype);//['constructor','toString','toValue']

    二、constructor方法

    前面已经说了,当我们创建一个类,即便内部没写constructor方法,类也会自带。

    由于类的方法都在原型上,所以当我们调用实例上的方法等同于调用类原型上的方法:

    class Parent {
        constructor() {}
    };
    let son = new Parent();
    console.log(son.sayName === Parent.prototype.sayName);//true

    类实例的constructor指向类,这与构造函数实例的constructor指向构造函数本身保持一致:

    //类的实例与构造函数实例的constructor属性都指向创建自己的类或构造函数
    class Parent {
        constructor() {}
    };
    let son = new Parent();
    console.log(son.constructor);//class Parent
    
    let Parent = function (name) {
        this.name = name;
    };
    let son  = new Parent();
    console.log(son.constructor);//Parent

    类与构造函数prototype对象的constructor属性都会指向自己,这点也保持了一致。

    //类与构造函数prototype的constructor属性都指向自己
    class Parent {
        constructor() {}
    };
    console.log(Parent.prototype.constructor === Parent);//true
    
    let Parent = function (name) {
        this.name = name;
    };
    console.log(Parent.prototype.constructor === Parent);//true

    其实到这里,class类基本用法算说完了,下面主要是对于class概念其它补充。

    三、class类的其它补充

    1.class类中的存值取值函数

    在类的内部也可以使用get与set方法对某个属性的取值存值操作做拦截处理。

    class Parent {
        get name() {
            return 'echo'
        };
        set name(val) {
            console.log(val);
        };
    };
    let son = new Parent();
    son.name = '时间跳跃'; //时间跳跃
    son.name; //echo

    当我们存值和取值时,实际上是调用了class内部的get与set对应方法,这点与ES5保持一致。

    2.class内中的属性名可以使用变量

    let name = 'sayName';
    class Parent {
        [name]() {
            console.log('echo');
        }
    };
    let son = new Parent();
    son[name]();//echo

    3.class表达式写法

    let Parent = class {
        sayName() {
            console.log('echo');
        }
    };
    let son = new Parent();
    son.sayName(); //echo

    与函数表达式相同,class类在表达式写法下也能添加一个class名:

    let Parent = class Me{
        sayName() {
            console.log('echo');
        }
    };
    let son = new Parent();
    son.sayName(); //echo
    console.log(Parent.name);//Me

    那么此时,Parent类的name属性为Me,但创建实例你得new Parent,而非new Me();

     4.类中this指向

    类方法中自带严格模式,且类方法中的this默认指向类的实例

    class Parent {
        sayName() {
            console.dir(this); //Patent {}
            this.sayAge(26);
        };
        sayAge(age) {
            console.log(age, this);
        }
    };
    let son = new Parent();
    son.sayName(); //26 Patent {}
    console.log(Parent); //class Parent

    但如果你在内部方法单独抽出来在外部调用,this此时会指向undefined(严格模式);

    class Parent {
        sayName() {
            console.log(this);//undefined
            this.sayAge(26);
        };
        sayAge(age) {
            console.log(age);
        }
    };
    let son = new Parent();
    let {sayName} = son;
    sayName();//报错

    解决方式是,可以通过在class类中的constructor构造方法中直接为你需要外部调用的方法绑定this。

    class Parent {
        constructor(){
            this.sayName = this.sayName.bind(this);
        }
        sayName() {
            this.sayAge(26)
        };
        sayAge(age) {
            console.log(age);
        }
    };
    let son = new Parent();
    let {sayName} = son;
    sayName();//26

    或者在构造方法中利用箭头函数,因为箭头函数中的this指向自身定义时所在的对象,此时箭头函数this指向实例。

    class Parent {
        constructor() {
            this.sayName = () => {
                this.sayAge(26);
            }
        }
        sayAge(age) {
            console.log(age);
        }
    };
    let son = new Parent();
    let {sayName} = son;
    sayName(); //26

    5.类的静态方法

    我在JavaScript模式一书的读书笔记中也有提到ES5中的构造函数静态方法;如果某个方法只有类自身可以调用,实例并不会继承,那么我们一般称此方法为静态方法。

    //ES5
    function Parent (){};
    Parent.sayAge = function (){
        console.log(26);
    };
    Parent.sayAge()//26
    let son = new Parent();
    son.sayAge()//报错,找不到这个方法
    
    //ES6
    class Parent {
        static sayAge() {
            console.log(26);
        }
    };
    Parent.sayAge()//26
    let son = new Parent();
    son.sayAge()//报错,找不到这个方法

    在上述代码中,我分别用ES5与ES6两种写法分别为类(构造函数)添加了静态方法sayAge。

    很明显这个方法只能被类自身调用,实例无法继承,只是相比ES5直接添加在构造函数上,类使用了static字段,也就是说,如果你想让某个方法作为静态方法,请在前面添加static。

    另外有一点,如果静态方法中使用了this,此时this指向了类,而不是实例,请注意。

    //ES5
    function Parent (){};
    Parent.sayAge = function (){
        console.log(this);
    };
    Parent.sayAge()//构造函数自身
    
    //ES6
    class Parent {
        static sayAge() {
            console.log(this);
        }
    };
    Parent.sayAge()//class Patent 类自身

    6.类的静态属性

    同理,在ES5中,我在构造函数内部某个属性只想给构造函数自身使用,实例无法使用,此时就得使用静态属性

    //ES5
    function Patent(){};
    Patent.age = 26;
    Patent.sayAge = function (){
        console.log(this.age);
    }
    Patent.sayAge();//26
    
    //ES6
    class Parent {
        static sayAge() {
            console.log(this.age);
        };
    };
    Parent.age = 26;
    Parent.sayAge();//26

    上述代码中分别用ES5,ES6为类添加了静态属性,其实都是直接加在类上,做法相同。

    当然在后面的提案中,也推荐使用static来创建类的静态属性,做法与静态方法相同,也就是说有两种方法可以做到这点。

    class Parent {
        static age = 26;
        static sayAge() {
            console.log(this.age);
        };
    };
    Parent.sayAge();//26

    7.实例属性的简写方法

    我在前面说,当声明一个类,即使不写constructor方法,类也会自带,那我想省略掉constructor方法,同时还想给实例添加属性呢怎么办,其实也可以简写。

    class Parent {
        constructor() {
            this.name = 'echo';
            this.age = 26;
        };
        sayName() {};
    };
    let son = new Parent('echo', 26);
    son.name //echo
    son.age //26
    
    class Parent {
        name = 'echo';
        age = 'age';
        sayName() {};
    };
    let son = new Parent();
    son.name //echo
    son.age //26

    以上两种写法等效,第二种实例赋值时直接写在了类的顶部,同时去掉了this,当然如果实例赋值带参数,那就没法简写了。

    最后有一个类的私有属性和私有方法没说,因为ES6没提供,只能模拟,有兴趣可以自行阅读。

    那么ES6中class类基本用法就说到这里了。

  • 相关阅读:
    链串
    一个外行谈行业应用的营销问题
    SharePoint 2013的100个新功能之场管理
    Deep Learning and Shallow Learning
    [IOS]UIWebView 请求网络页面或者加载本地资源页面
    九度OJ 打印日期 (模拟)
    STM32学习之路-SysTick的应用(时间延迟)
    box-shadow
    让算法会说话之高速排序
    A5-1和DES两个加密算法的学习
  • 原文地址:https://www.cnblogs.com/echolun/p/10835901.html
Copyright © 2020-2023  润新知