美文网首页
JavaScript之Class (类)

JavaScript之Class (类)

作者: 又菜又爱分享的小肖 | 来源:发表于2021-06-30 21:45 被阅读0次

面向对象的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是JavaScript中并没有类的概念,所以它的对象也与基于类的语言中的对象有所不同。
JavaScript中把对象定义为:无序属性的集合,其属性可以包含值,对象或者函数。
虽然Objeck构造函数或对象字面量都可以创建对象,但这些方法有个很明显的特点:使用一个接口创建很多对象,会产生大量的重复代码。

JavaScript语言的传统方法是通过构造函数定义并生成新对象。

        function Point(x, y){
            this.x = x;
            this.y = y;
        }
        Point.prototype.toString = function(){
            return `${this.x} + ${this.y}`
        }
        var p = new Point(1,2).toString();
        console.log(p);

prototype 这个属性是一个指针,指向一个对象的原型。

new在里面做了四个操作:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给这个新对象(所以this指向这个新对象)
  3. 执行构造函数中的代码(为了这个新对象添加属性)
  4. 返回一个新对象

ES6中提供了更接近传统语言的写法,引入了Class(类)这个概念作为对象的模板。通过class关键字可以定义类。

        class Point{
            constructor(x, y){
                this.x = x;
                this.y = y;
            }
            toString(){
                return `${this.x + this.y}`
            }
        }
       var s = new Point(1,2);
       console.log(s);

上面的代码定义了一个类,可以看到里面有个constructor方法,这就是构造函数,而this关键字则代表实例对象。
Point 类除了构造函数,还定义了一个toString方法。

定义类的方法时,前面不需要加上function这个保留字,直接把函数定义放进去就可以了。另外,方法之间不需要逗号分隔,加了会报错。

        class Point{};
        console.log(typeof Point); //"function"
        console.log(Point === Point.prototype.constructor); //true

类的数据类型就是函数,类本身就指向构造函数。

        class Point{
            constructor(){

            }
            toValue(){
                
            }
        }

        //等同于
        Point.prototype = {
            constructor(){},
            toValue(){}
        }

在类里面调用方法,其实就是调用原型上的方法。

        class B {}
        let b = new B();
        B.constructor === b.prototype.constructor // true

上面的代码中,b是B类的实例,它的constructor 方法就是B类原型的constructor 方法。
constructor始终指向创建当前对象的构造函数。
prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。
prototype 对象的constructor 属性直接指向类本身。

类的内部定义的所有方法都是不可枚举的

        class Point {
            constructor(){

            }
            toString(){

            }
        }
        Object.keys(Point.prototype); // []
        Object.getOwnPropertyNames(Point.prototype); //["constructor", ["toString"]

类的属性名可以使用表达式

        let methodName = 'getArea';
        class Point {
            constructor (){

            }
            [methodName] (){
                
            }
        }

constructor 方法

constructor 方法是类的默认方法,通过new生成对象实例时自动调用该方法。一个类必须有constructor 方法,如果没有显式定义,一个空的constructor 方法会被默认添加。

        function Point(x, y){
            this.x = x;
            this.y = y;
        }
        Point.prototype.toString = function(){
            return `${this.x} + ${this.y}`
        }
        var p = new Point(1,2);
        console.log(p.hasOwnProperty('x')); //true
        console.log(p.hasOwnProperty('y')); //true
        console.log(p.hasOwnProperty('toString')) //false
        console.log(p.__proto__.hasOwnProperty('toString')) //true

x和y都是实例对象 p 自身的属性(因为定义在this变量上),所以hasOwnProperty方法返回为true,而toString方法是原型对象的属性(因为定义在Point类上),所以hasOwnProperty方法返回为false。
与ES5一样,类的所以实例共享一个原型对象。

        var p1 = new Point(1,2);
        var p2 = new Point(2.3);
        p1.__proto__ === p2.__proto__; //true

proto并不是语言本身的特性,而是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的js引擎中都提供了这个私有属性,但依旧不建议再生产中使用该属性,避免对环境产生依赖。生产环境中,可以使用Objeck.getPrototypeOf 方法来获取实例对象的原型,然后在来为原型添加方法&属性。

私有方法

一种做法是在命名上加以区别

        class Widget {
            //公有方法
            foo(baz){
                this._bar(baz);
            }

            //私有方法
            _bar(baz){
                return this.snaf = baz;
            }
        }

_bar方法前面的下划线表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部依然可以调用这个方法。
另一种方法是索性将私有方法移出模块,因为模板内部的所有方法都是对外可见的。

        class Widget {
            //公有方法
            foo(baz){
                bar.call(this, baz);
            }
        }
        function bar(baz){
            return this.snaf = baz;
        }

foo是公有方法,内部调用了bar.call(this, baz)。这使得bar实际上成为了当前模块的私有方法。

还有一种方法,利用Symbol值的唯一性将私有方法的名字命名为一个symbol值。

        const bar = Symbol('bar');
        const snaf = Symbol('snaf');
        class Widget {
            //公有方法
            foo(baz){
                this[bar](baz);
            }
            [bar](baz){
                return this[snaf] = baz;
            }
        }

利用symbol值的唯一性,导致第三方无法获取到它,因此达到了私有方法和私有属性的效果。

私有属性

为class加了私有属性。方法是在属性名之前,使用#来表示。

        class Point{
            #x;
            constructor(x = 0){
                #x = +x;
            }
            get x() {
                return #x;
            }
            set x(value){
                #x = +value;
            }
        }

.#x表示私有属性x,在Point类之外是读取不到这个属性的,私有属性与实例的属性是可以同名的

this的指向

类的方法内部如果含有 this,它将默认指向类的实例。但是,一旦单独使用该方法,很可能或报错。

相关文章

网友评论

      本文标题:JavaScript之Class (类)

      本文链接:https://www.haomeiwen.com/subject/gibkultx.html