18. class

时间:2022-10-27 12:51:12

Class

基本用法


class n {
  constructor(x,y) {
    this.x = x;
    this.y = y;
    console.log(x,y)
  }

  proint() {
    console.log(' this is proint ')
  }
}

var newObject = new n(1,2);
// c {x: 1, y: 2}

newObject.proint();
// this is proint 

现在不用像以前.


function o(x,y) {
    this.x = x;
    this.y = y;
    console.log(x,y)
}

o.prototype.proint = function() {
    console.log(' this is proint ');
};

var oldObject = new o(1,2); //1,2

oldObject.proint() //this is proint

书中说这是语法糖.

也就是说。本质上依旧使用的是

ES5 版本的创建对象.

Class 不过一种简化的写法.

不过需要验证一下。 接下来的例子都会同时有两个版本.


n === n.prototype.constructor //true
o === o.prototype.constructor

newObject.constructor === n.prototype.constructor //true
oldObject.constructor === o.prototype.constructor //true

也就是这样.

为了说明他是语法糖.


Object.assign(n.prototype,{
    mytest:function() { console.log('mytest') }
});

n.mytest() //mytest

n.prototype.proint //proint() { console.log(' this is proint ') }

constructor

你不写那么 javascript 就会为你自动创建一个空的

constructor() {}

new n() 就是调用 constructor 返回一个实例.

所以你可以改变返回的实例.


class foo {
    constructor() {
        return { y : 1 };
    }
}

new foo() // { y : 1 }

类的实例对象

基本和ES5原型链基本一样.

this , prototype

以及

类的所有实例共享一个原型对象

完全是ES5的老内容.

不存在变量提升

我一直认为变量提升这种东西.

是你写代码不规范导致的.

不存在也就不存在吧.

Class的表达式

几种写法.


let n = class c {  } //只有在class 内部调用才能调用C

let n1 = class {  }

let n2 = new class { } //自执行相当于马上实例化.

私有方法

书中提到了3种方式.

  1. 前面加 **_** 下划线
  2. 将私有方法移出模块
  3. 用Symbol

前两种我表示都太垃圾..

第三种还有些意思

const bar = Symbol('bar');

const snaf = Symbol('snaf');

export default class myClass{

// 公有方法

foo(baz) {

thisbar;

}

// 私有方法

bar {

return this[snaf] = baz;

}

// ...

};

这样确实可以私有。

百度了一波。 发现大部分都是使用 Symbol

例子中 bar 依然暴露在外.

没办法彻底的私有. 因为 Symbol 你如何防止随意的使用呢?

这样反而不如。 function 来的ok


function() {
    var a1 = function() {};
    return {
        a : function() {
            return a1();
        }
    }
}

继承

基本用法


class c1n extends n {}

基本的继承就是这样。 啥都不写。 完全照搬父类方法.

继承父类的一切。 this & prototype

你也可以自己写点儿啥.

具体的跟 ES5 并没有什么区别

需要注意的点在于 super,这个关键字.

它在 构造函数(constructor) 中,就是直接调用父类的构造函数.

它在普通的方法中,就是想到父类的this

class c1n extends n {
   constructor(x,y,z) { super(x,y); this.z = z }

   print() {
        super.proint();
        console.log('child print')
   }
}

var o = new c1n(1,2,3);

o.print();

// this is proint
// child print

不调用父类的构造函数是无法创建自己的this

所以 super 是必须在构造函数中调用的。

当然如果你忘记了。 浏览器会自动为你调用。

前提是你参数没有问题.

类的prototype属性和__proto__属性


class c1n extends n { };

c1n.__proto__ === n;

c1n.prototype.__proto__ === n.prototype;

这种继承你完全可以自己模拟


Object.setPrototypeOf(c1n.prototype, n.prototype);

c1n.__proto__ = n;

就可以继承了.

Object.getPrototypeOf(c1n) === n

这样也可以获取父类.

super 关键字

super.

上面说过。 首先需要再 构造函数调用 super,调用父类的构造函数。

然后 super 会转变模式.

super 会指向父类的原型对象. 可以调用父类的方法.

super 引用当前子类的引用(this)


class a1 {
   constructor(x,y) {
      this.x = x;
      this.y = y;
   }

   p() { console.log(this.x,this.y); }
}

class a2 extends a1 {
   constructor(x,y) {
      super(x,y);

      this.x =  33;
   }

   p1() { super.p(); }
}

var o = new a2()

o.p1(); //33 undefined

所以 this.yundefined.

Extends 的继承目标

上面代码的A,只要是一个有prototype属性的函数,就能被B继承。由于函数都有prototype属性(除了Function.prototype函数),因此A可以是任意函数。

也就是说

只要你有 prototype 属性. 且是一个函数,那就可以被继承.

书中讲了3个特殊情况.

  1. 继承 Object 相当于Object 的一个复制

  2. class a {}

这种情况下,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承Funciton.prototype。但是,A调用后返回一个空对象(即Object实例),所以A.prototype.__proto__指向构造函数(Object)的prototype属性。

  1. class a extends null
class C extends null {
  constructor() { return Object.create(null); }
}

3.原生构造函数的继承

ES6 以前你并不能继承一些原生的对象.

Boolean()

Number()

String()

Array()

Date()

Function()

RegExp()

Error()

Object()

你并不能继承,去模拟它。

因为你不能继承到对象的构造函数 constructor.

但是 extends 可以

所以你可以完全继承一个 Array 来改造他.


class nArray extends Array {

    constructor(...args) {
        super(...args); //调用构造函数.
    }

    push(obj) {
        //可以开始改造.
    }

}

就是这样.

4. Class的取值函数(getter)和存值函数(setter)

可以有 get set. 虽然ES5 也有


class testClass {
   get p() { console.log('b') }
   set p(value) { console.log('a') }
}

5. Class的Generator方法


class testClass {

    * [Symbol.iterator]() { }

}

6. Class的静态方法


class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

就是静态类。 不用实例化就可以调用。

不过不能访问内部的东西。 可以用于某些设计模式.

ES6 不支持静态属性。 书中说 ES7 支持

8. new.target属性

new.target 能返回实例化的对象.

如果不是 new 实例化,就是 undefined.


function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必须使用new生成实例');
  }
}

这就是一个例子。

必须是 Person 实例化.

利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。


class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本类不能实例化');
    }
  }
}

class Rectangle extends Shape {
  constructor(length, width) {
    super();
    // ...
  }
}

var x = new Shape();  // 报错
var y = new Rectangle(3, 4);  // 正确

就是这样.

大概了解了一下。 基本是以前就有的概念。当然也有一些没有的概念.

可以在实际中运用一下再来复盘一下.