导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

Class

构造函数 与 Class 对比

function Person(name, age){
	this.name = name;
  this.age = age;
}
Person.prototype.say = function(){
	console.log(`My name is ${this.name}, age is ${this.age}`);
}

var person = new Person('lisi', 20);
// person 的原型
console.log(Object.getPrototypeOf(person));
console.log(Object.getPrototypeOf(person).constructor === Person);
console.log(Object.getPrototypeOf(person) === Person.prototype);

Untitled

class Person{}
console.log(new Person())

Untitled

特性

私有属性、共有方法。Class 没有共有属性

class Person {
	constructor(name = 'zhangsna', age = 18) {
    // 实例化的属性配置: 私有属性
  	this.name = name;
    this.age = age;
  }
  // 公有方法: 出现在原型上
  say() {
  	console.log(`My name is ${this.name}, age is ${this.age}`);
  }
  eat() {
    console.log('I can eat')
  }
  drink() {
    console.log('I can drink')
  }
}

console.log(new Person());

Untitled

⭐️ Class 的内部方法不可枚举,而构造函数的可以

class Person {
  constructor(name = 'Lance', age = 27) {
    this.name = name
    this.age = age
  }
  run() {}
  say() {}
}

console.log(Object.keys(Person.prototype));

Untitled

function Person(name, age){
	this.name = name;
  this.age = age;
}
Person.prototype.say = function(){
	console.log(`My name is ${this.name}, age is ${this.age}`);
}

Object.assign(Person.prototype, {
	eat: function() {
  	console.log('I can eat')
  },
  drink: function() {
  	console.log('I can drink')
  }
})
// 这种方式添加的方法是可枚举的
console.log(Object.keys(Person.prototype));

Untitled

没写 constructor 会默认加上

class Person {}
console.log(new Person());

Untitled

⭐️ Class 调用必须在声明后,否则会报错,有暂时性死区

console.log(new Person());
class Person {}

Untitled

⭐️ 私有方法的实现

const eat = Symbol();
class Person {
	constructor(name = 'zhangsna', age = 18) {
    // 实例化的属性配置: 私有属性
  	this.name = name;
    this.age = age;
  }
  // 公有属性
  say() {
  	console.log(`My name is ${this.name}, age is ${this.age}`);
  }
  [eat]() {
    console.log('I can eat')
  }
}

console.log(new Person().say());
console.log(new Person().eat());
console.log(new Person()[eat]()); // 可以这样访问

Untitled

class Person {
	constructor(name = 'zhangsan', age = 18) {
    // 实例化的属性配置: 私有属性
  	this.name = name;
    this.age = age;
  }
  // 公有属性
  say(baz) {
  	children.call(this, baz);
  }
}
function children(baz) {
	return this.bar = baz;
}

⭐️ static

class Person {
  static a = 10;
	static say(){
  	console.log('say....')
  }
}
var per = new Person();
Person.say();
console.log(Person.a);
per.say();
console.log(per.a);

Untitled

getter、setter 存值和取值函数

var obj = {
	get a(){
  	console.log(1)
  },
  set b (val){
  	console.log(2)
  }
}
obj.a;
obj.b = 1;

Untitled

class Person {
	get a() {
  	console.log(1)
  }
  set b(val) {
  	console.log(2)
  }
}

var person = new Person()
person.a;
person.b = 1;

Untitled

可以用表达式方式书写

var Person = class {
	say() {
  	console.log('say')
  }
}
new Person().say();

Untitled

自执行的 class , 必须通过 new 来执行 class

var Person = class {
	say(){
  	console.log('say')
  }
}();

Person.say();

Untitled

var Person = new class {
	say(){
  	console.log('say')
  }
}();

Person.say();

Untitled

总结

extends 继承

class Parent {
	constructor(name = 'zhangsna') {
  	this.name = name;
  }
  say() {
  	console.log(1);
  }
  // 静态方法无法被继承
  static a() {
  	console.log(2)
  }
}
// 派生类
class Child extends Parent {
	
}

console.log(new Child());

Untitled

super

class Parent {
	constructor(name = 'zhangsan') {
  	this.name = name;
  }
  say() {
  	console.log(1);
  }
  // 静态方法无法被继承
  static a() {
  	console.log(2)
  }
}
// 派生类
class Child extends Parent {
	constructor(name = 'lisi', age = 20){
    super(name);
    this.type = 'child';
    this.age = age;
  }
}

console.log(new Child());

Untitled

⭐️ super 可以指向原型对象

var proto = {
	y: 20,
  z: 40
}
var obj = {
	x: 10,
  foo() {
  	console.log(super.y);
  }
}
Object.setPrototypeOf(obj, proto);
obj.foo();

Untitled

Class 源码

"use strict"
function _classCallBack(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError('Cannot call a class as a function')
    }
}
var _createClass = 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)
        }
      
        return function(Constructor,protoProps, staticProps) {
            if (protoProps) defineProperties(Constructor.prototype, protoProps)
            if (staticProps) defineProperties(Constructor, staticProps)
            return Constructor;
        }
    }
}();
var Person = function() {
    function Person() {
        var name = arguments[0] !== undefined && arguments.length > 1 ? arguments[0]:'xx';
        var test = arguments[0] !== undefined && arguments.length > 1 ? arguments[1]:'dd';
        _classCallBack(this, Person)
        this.name = name
        this.age = age
    }
    _createClass(Person,[{
        key:"test",
        value:function test(){
            console.log('hahahahahha');
        }
    }],[{
        key:'fuck',
        value:function fuck(){
            console.log(2);
        }
    }])
    return Person;
}();

⭐️ Class 中的 constructor 方法和 super 的作用

首先,ES6 的 class 属于一种“语法糖”,所以只是写法更加优雅,更加像面对对象的编程,其思想和 ES5 是一致的。

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() {
  return '(' + this.x + ',' + this.y + ')';
}

等同于

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

  toString() {
    return '(' + this.x + ',' + this.y + ')';
  }
}

其中 constructor 方法是类的构造函数,是一个默认方法,通过 new 命令创建对象实例时,自动调用该方法。一个类必须有 constructor 方法,如果没有显式定义,一个默认的 consructor 方法会被默认添加。所以即使你没有添加构造函数,也是会有一个默认的构造函数的。一般 constructor 方法返回实例对象 this ,但是也可以指定 constructor 方法返回一个全新的对象,让返回的实例对象不是该类的实例。

super 这个关键字,既可以当做函数使用,也可以当做对象使用。这两种情况下,它的用法完全不用。

1. 当做函数使用

class A {}
class B extends A {
  constructor() {
    super();  // ES6 要求,子类的构造函数必须执行一次 super 函数,否则会报错。
  }
}

注:在 constructor 中必须调用 super 方法,因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工, 而 super 就代表了父类的构造函数。super 虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指的是 B,因此 super() 在这里相当于:

class A {
  constructor() {
    console.log(new.target.name); // new.target 指向当前正在执行的函数
  }
}

class B extends A {
  constructor() {
    super();
  }
}

new A(); // A
new B(); // B

可以看到,在 super() 执行时,它指向的是 子类 B 的构造函数,而不是父类 A 的构造函数。也就是说,super() 内部的 this 指向的是 B。

2. 当做对象使用

在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

class A {
  c() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.c()); // 2
  }
}

let b = new B();

上面代码中,子类 B 当中的 super.c(),就是将 super 当作一个对象使用。这时,super 在普通方法之中,指向 A.prototype,所以 super.c() 就相当于 A.prototype.c()

通过 super 调用父类的方法时,super 会绑定子类的 this。

class A {
  constructor {
    this.x = 1;
  }
  s() {
    console.log(this.x);
  }
}

class B extends A {
  constructor {
    super();
    this.x = 2;
  }
  m() {
    super.s();
  }
}

let b = new B();
b.m(); // 2

上面代码中,super.s() 虽然调用的是 A.prototytpe.s(),但是 A.prototytpe.s()会绑定子类 B 的 this,导致输出的是 2,而不是 1。也就是说,实际上执行的是 super.s.call(this)

由于绑定子类的 this,所以如果通过 super 对某个属性赋值,这时 super 就是 this,赋值的属性会变成子类实例的属性。

class A {
  constructor {
    this.x = 1;
  }
}

class B extends A {
  constructor {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}

let b = new B();

上面代码中,super.x 赋值为 3,这时等同于对 this.x 赋值为 3。而当读取 super.x 的时候,调用的是 A.prototype.x,但并没有 x 方法,所以返回 undefined。

注意,使用 super 的时候,必须显式指定是作为函数,还是作为对象使用,否则会报错。

class A {}
class B extends A {
  constructor() {
    super();
    console.log(super); // 报错
  }
}

上面代码中,console.log(super); 的当中的 super,无法看出是作为函数使用,还是作为对象使用,所以 JavaScript 引擎解析代码的时候就会报错。这时,如果能清晰的表明 super 的数据类型,就不会报错。

最后,由于对象总是继承其他对象的,所以可以在任意一个对象中,使用 super 关键字。

⭐️ 装饰器

为对象添加新的功能,而不改变原有的结构和功能

解决痛点:

准备工作

package.json

{
  "name": "decorators-in-nodejs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "index": "babel-node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.17.6",
    "@babel/core": "^7.17.5",
    "@babel/node": "^7.16.8",
    "@babel/plugin-proposal-class-properties": "^7.16.7",
    "@babel/plugin-proposal-decorators": "^7.17.2",
    "@babel/preset-env": "^7.16.11",
    "babel-node": "0.0.1-security"
  }
}

.babelrc

{
  "presets": [
      "@babel/preset-env"
  ],
  "plugins": [
      ["@babel/plugin-proposal-decorators", { "legacy": true }],
      ["@babel/plugin-proposal-class-properties", { "loose" : true }]
  ]
}

运行方式:npm run index

符号@

修饰类

接收的参数:

@testable
class Person {
	constructor(name='lisi', age='19') {
  	this.name = name;
    this.age = age;
  }
  say() {
  	console.log('hello world')
  }
  eat() {
  	console.log('eat')
  }
}
var person = new Person();

function testable(target) {
	console.log('target', target)
}

// target [Function: Person]

修饰属性

接收的参数:

class Person {
	constructor(name='lisi', age='19') {
  	this.name = name;
    this.age = age;
  }
  @readOnly
  say() {
  	console.log('hello world')
  }
  eat() {
  	console.log('eat')
  }
}
function readOnly(target, name, descriptor) {
	console.log(target, name, descriptor)
  descriptor.writable = false
}

var person = new Person()
person.say()

// hello world

⭐️ 埋点分析

var log = (type) => {
	return function(target, name, descriptor) {
  	let src_method = descriptor.value
    descriptor.value = (...arg) => {
    	src_method.apply(target, arg);
      console.log(type);
    }
  }
}

class AD {
  @log('show')
	show() {
  	console.log('ad show')
  }
  @log('click')
  click() {
  	console.log('ad click')
  }
}

const ad = new AD();
ad.show();

// ad show
// show

资源