KieSun / all-of-frontend

你想知道的前端内容都在这

Home Page:https://yuchengkai.cn/docs/frontend

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

第十一题:class 用 ES5 实现

KieSun opened this issue · comments

commented

class 使用 ES5 来实现。

去答题

新建了一个大厂真题每日打卡群,有意愿学习打卡的再进,请备注打卡

function Animal(name) {
  this.name = name;
}

Animal.prototype.bark = function (msg) {
  console.log(`${this.name}'s bark: ${msg}`);
};

function Cat(name, color) {
  this.color = color;
  Animal.call(this, name);
}

Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.getColor = function () {
  console.log(`${this.name}'s color: ${this.color}`);
};

const tom = new Cat("tom", "red");
tom.bark("miao, miao");
tom.getColor();

// tom's bark: miao, miao
// tom's color: red
// class只可以用new来创建,不可以直接使用
function chunk(obj, constructor) {
  if (!(obj instanceof constructor)) {
    throw new Error('class只可以用new来调用');
  }
}

// class内部定义的方法都是不可枚举的
function defindProperties(target, descriptors) {
  for (let desc of descriptors) {
    desc.enumerable = desc.enumerable || false;
    desc.configurable = true;
    if ('value' in desc) {
      desc.writable = true;
    }

    Object.defineProperty(target, desc.key, desc);
  }
}

// 构造class
function _createClass(constructor, protoDesc, staticDesc) {
  // class内部定义的方法
  protoDesc && defindProperties(constructor.prototype, protoDesc);
  // class内部定义的静态方法
  staticDesc && defindProperties(constructor, staticDesc);
  return constructor;
}

const Fn = function () {
  function Fn(prop) {
    chunk(this, Fn);

    this.prop = prop;
  }

  _createClass(
    Fn,
    [
      {
        key: 'show',
        value: function () {
          console.log(this.prop);
        },
      },
    ],
    [
      {
        key: 'show',
        value: function () {
          console.log('static show', this.prop);
        },
      },
    ]
  );

  return Fn;
}();
根据大佬的实现补习了很多边边角角的属性知识
var Person = (function () {
  function Person(name) {
    this.name = name;
  }
  Person.prototype.getName = function () {
    return this.name;
  };
  return Person;
})();

var Man = (function () {
  function Man(name, height) {
    Person.call(this, name);
    this.height = height;
  }
  Man.prototype = new Person();
  Man.prototype.construcor = Man;
  Man.prototype.getHeight = function () {
    return this.name + " " + this.height;
  };
  return Man;
})();
var man = new Man("张三", 13);
console.log(man.getName());
console.log(man.getHeight());

image

// class 用 ES5 实现
function A(name) {
    this.name = name
}

A.prototype.say = function(word) {
    console.log(`${this.name} say ${word}!`)
}
A.prototype.run = function() {
    console.log('run!')
}


function B(name) {
    A.call(this, name)
    this.type = 'B'
}

B.prototype = new A()

B.prototype.run = function() {
    console.log(`${this.name} run!`)
}
B.prototype.self = function() {
    console.log(this.type)
}
B.prototype.constructor = B

const b = new B('bbbb')

b.say('1231232')
b.run()
b.self()
function defineProperty(constructor, protoProperties) {
  if(Array.isArray(protoProperties)) {
    for (let i = 0; i < protoProperties.length; i++) {
      const {key,...config} = protoProperties[i];
      Object.defineProperty(constructor.prototype, key,{
        configurable:true,
        enumerator: false,
        ...config
      })
    }
  }
}

const Animal = (function() {
  function _CreateClass(name) {
    if(!(this instanceof Animal)) {
      throw TypeError('Class constructor a cannot be invoked without new')
    }
    this.name = name;
  }
  defineProperty(_CreateClass, [{
    key: 'sex',
    value: '男'
  },{
    key: 'getName',
    value: function() {
      return this.name;
    }
  }]);
  return _CreateClass;
})()

// 不可枚举
console.log(Object.keys(Animal.prototype));

let animal = new Animal('动物');
console.log(animal.getName());
console.log(animal.sex);

// 不可单独使用
console.log(Animal());

ES5 模拟 Class 与 extends 继承

  • 工厂函数 _extends_
  • 将父类 prototype 与 子类 prototype 相连接
function _extends_(proto) {
  function F() {}
  F.prototype = proto;
  return new F();
}

function Parent(name) {
  this.name = name;
}

Parent.prototype.construtor = Parent;

Parent.prototype.getName = function () {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

Child.prototype = _extends_(Parent.prototype);

Child.prototype.construtor = Child;
Child.prototype.getAge = function () {
  console.log(this.age);
};

const parent = new Parent("Turdyden");
parent.getName();

const child = new Child("Tanwen", 18);
child.getName();
child.getAge();

输出:

Turdyden 
Tanwen 
18

image

ecmaScript 2015 新增class,要实现几个点

  • 需要 new 创建 因此要有构造函数
  • 类的方法属性需要设置成不可枚举
  • 子类可以继承父类的原型链和静态方法
//✅需要有构造函数
function ckTYpe(obj,constructor) { 
    if(!(obj instanceof constructor)) throw new Error('error')
 }
 // ✅设置参数不可枚举
function kk (tar, des) {
    for (let dr of des) {
        dr.enumerable = dr.enumerable || false //设置不可枚举
        dr.configurable = true
        if ('value' in dr) {
            dr.writable = true
        }
        Object.defineProperty(tar, dr.key, dr)
    }
}
function create (con, neBu, static) {
    neBu && kk(con.prototype, neBu)//设置内部函数不可枚举
    static && kk(con, static)//设置方法不可枚举
    return con
}
 const Foo = function () {
    function Foo(name) {
        ckTYpe(this, Foo) // 先检查是不是new调用的
        this.name1 = name
    }
    return  create (Foo,  [ // 表示在class内部定义的方法
        {
            key: 'say',
            value: function () {
                console.log(this.name1,'hhhhhp')
            }
        }
    ],[ // 表示在class内部定义的静态方法
        {
            key: 'say',
            value: function () {
                console.log('static say')
                console.log(this.name)
            }
        }
    ])
}()
// ==================子类开始======================================
//父类原型方法被子类继承
function _inherits(cld, par) {
    if (typeof par !== 'function' && par !== null) {
        throw new TypeError('Super expression must either be null or a function, not')
    }
    //✅子类继承父类的
    cld.prototype = Object.create(par && par.prototype, {
        constructor: {
            value: cld,
            enumerable: false,
            writable: true,
            configurable: true
        }
    })

    if (par) {
        Object.setPrototypeOf ? Object.setPrototypeOf(cld, par) : cld.__proto__ = par
    }
}
// 返回父类的this;若为null,则返回自身
function pos(self, call) {
    if (!self) {
        throw new ReferenceError("this hasn't been initialised")
    }
    return call && (typeof call === 'object' || typeof call === 'function') ? call : self
}
const Child = function (_Parent) {
    _inherits(Child, _Parent) // 继承父类原型上的属性及静态方法的继承
    function Child(name, age) {
        ckTYpe(this, Child)//先检查是否能被new
        // 先使用父类实例对象this,再返回
        const _this = pos(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name)) //获取
        _this.age = age
        return _this
    }
    return Child
}(Foo)
// ==========================================
const foo = new Foo('aaa')
console.log(foo);
console.log(foo.say());//父类实例待调用内部方法
console.log(Child.say());// 子类继承父类的原型name1
const chid1 = new Child('小明', 12)
console.log(chid1);
console.log(chid1.say());
// class 使用 ES5 来实现。

// ES6 class
// class SuperType {
//     static staticFn() {
//         console.log('this is SuperType static function');
//     }
//     constructor(name) {
//         this.name = name;
//     }

//     say() {
//         console.log(`this is SuperType prototype function: name is ${this.name}`);
//     }
// }

// // ES6 extends
// class SonType extends SuperType {
//     constructor(name, type) {
//         super(name);
//         this.type = type;
//     }

//     sonSay() {
//         console.log(`this is sonType prototype function: name is ${this.name}`);
//     }
// }

// ES5 class
function SuperType(name) {
    this.name = name;
}

SuperType.staticFn = function () {
    console.log('this is SuperType static function');
};

SuperType.prototype.say = function () {
    console.log(`this is SuperType prototype function: name is ${this.name}`);
};

// ES5 extends
function SonType(name, type) {
    SuperType.call(this, name);
    this.type = type;
}

// Object.create()  polyfill
function object(prototype) {
    function F() {}
    F.prototype = prototype;

    return new F();
}

SonType.prototype = object(SuperType.prototype);
SonType.prototype.constructor = SonType;

SonType.prototype.sonSay = function () {
    console.log(`this is sonType prototype function: name is ${this.name}`);
};

var sup = new SuperType('super');
console.log(sup);
SuperType.staticFn();
console.log(sup.name);
sup.say();

var sonType = new SonType('son', 'sonType');
console.log(sonType);
// 寄生式组合继承
function Person(color) {
   this.color = color
}
Person.prototype.getColor = function() {
  console.log(this.color)
}
function Student(name, age, color) {
  this.age = age
  this.name = name
  // 即使是引用类型也不会共享
  Person.call(this, color)
}
// function object(o) {
//   function F() {}
//   F.prototype = o
//   return new F()
// }
function inhertPrototype(student, person) {
  // 让Student.prototype的)__proto__指向Person.prototype
  // const prototype = object(person.prototype)
  const prototype = Object.create(person.prototype)
  Object.defineProperty(prototype, 'constructor', {
    configurable: false,
    value: Student
  })
  Student.prototype = prototype
}
inhertPrototype(Student, Person)
Student.prototype.say = function() {
  console.log(this.age)
}
const stu = new Student('ceshi', 28, ['red', 'block'])
console.log(stu)
// class 使用 ES5 来实现。

function Parent (name) {
  this.name = name;
  this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.getName = function () {
  console.log(this.name)
}

function Child (name, age) {
  Parent.call(this, name);
  this.age = age;
}

// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();


var child1 = new Child('kevin', '18');

console.log(child1);
commented

https://zhuanlan.zhihu.com/p/274856925
个人感觉写的不错,为了不误导大家 放个连接大家自行学习

const define = (target, properties) => {
  properties.forEach((property) => {
    const { key, ...restConfig } = property;
    Object.defineProperty(target, key, {
      enumerable: false,
      configurable: true,
      ...restConfig,
    });
  });
};
const defineProperty = (constructor, protoProperties, staticProperties) => {
  // 定义原型上的属性和方法
  if (Array.isArray(protoProperties)) {
    define(constructor.prototype, protoProperties);
  }
  // 定义静态属性和方法
  if (Array.isArray(staticProperties)) {
    define(constructor, staticProperties);
  }
};
const MyClass = function (protoProperties = [], staticProperties = []) {
  function ClassModal() {
    if (!(this instanceof ClassModal)) {
      throw TypeError(`Class constructor An cannot be invoked without 'new'`);
    }
  }
  // 定义原型上的属性和静态属性
  defineProperty(ClassModal, protoProperties, staticProperties);
  return ClassModal;
};
const Animal = MyClass(
  [
    {
      key: "age",
      value: 10,
    },
    {
      key: "eat",
      value: function () {
        console.log("eat");
      },
    },
  ],
  [
    {
      key: "chineseName",
      value: "佳佳",
    },
    {
      key: "like",
      value: function () {
        console.log("吃竹子");
      },
    },
  ]
);
let animal = new Animal();
console.log(animal.age, "animal.age------");   //  10 "animal.age------"
console.log(animal.eat, "animal.eat------");  
  /* ƒ () {
      console.log("eat");
      } "animal.eat------" */
console.log(Animal.chineseName, "Animal.chineseName------");  //  佳佳 Animal.chineseName------
const Person = MyClass(
  [
    {
      key: "name",
      value: "张三",
    },
    {
      key: "say",
      value: function () {
        console.log("say");
      },
    },
  ],
  [
    {
      key: "id",
      value: "555",
    },
  ]
);
let person = new Person();  
console.log(person.name, "person.name------");  // 张三 person.name------
console.log(Person.id, "Person.id------");  // 555 Person.id------

触及知识的盲区,直接写了个用例babel转义扒代码来看(附备注)

/**
 * class类使用提前必须是new调用,否则应该报错。
 * 这里可以用此区分于「构造函数」,构造函数可以当成普通函数使用
 * 
 * @param {*} instance 
 * @param {*} Constructor 
 */
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

/**
 * Object.defineProperty() 直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
 * 此处作用:用于处理类的内部所有定义的方法不可枚举的特性。
 */
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;
  };
}();

/**
 * 创建class
 */
var testFn = function () {
  function testFn() {
    _classCallCheck(this, testFn);

    // 这里就是constructor
    this.name = 'John';
    this.gender = 1;
  }

  // 数组定义的key是函数方法名,value为处理过程/结果,可直接修改,可return返回
  _createClass(testFn, [{
    key: 'changeName',
    value: function turnOffPreferFuture() {
      this.name = 'Mary';
    }
  }, {
    key: 'getGender',
    value: function getTimeBase() {
      return this.gender;
    }
  }]);

  return testFn;
}();
  1. 原型继承
  2. 静态方法继承
  3. 构造函数继承
function inherit(Sub, Sup) {
  // 1、原型继承
  const prototype = new Sup()
  Sub.prototype = prototype
  Sub.prototype.constructor = Sub

  // 2、静态属性继承
  Sub.__proto__ = Sup

}


function SupType(name) {
  this.name = name
  this.colors = ['red']
}

SupType.getDefaultValue = function() {
  return 'defaultValue'
}

SupType.prototype.getName = function(){
  return this.name
};

inherit(SubType, SupType)
function SubType(name,age) {
  this.age = age
  // 3、构造函数继承
  SupType.call(this, name)
}

SubType.prototype.getAge = function() {
  return this.age
}

const sub = new SubType('streetex', 25)

没时间了,改天优化,早睡早起

var createClassHelper = {
    checkIsNew: function(ins, cl){
        if(!(ins instanceof cl)){
            throw new TypeError("Class constructor " + cl.name + " cannot be invoked without 'new'")
        }
    },
    setEnumerable(target, props){
        var keys = Object.getOwnPropertyNames(props)
        for(var i = 0, len = keys.length;i < len; i++){
            var key = keys[i]
            var value = props[key]
            Object.defineProperty(target, key, {
                value: value,
                enumerable: false
            })
        }
    }
}
var Hp = (function(superClass){
    function Hp(){
        createClassHelper.checkIsNew(this, Hp)
    }
    // if('function' === typeof superClass){}
    Hp.prototype = {}
    // 定义原型
    createClassHelper.setEnumerable(Hp.prototype, {
        hahaha: function hahaha(){},
        isMe: true
    })
    // 静态变量
    createClassHelper.setEnumerable(Hp, {
        TYPE: 'HP'
    })
    return Hp
}(Anam2))

function Parent(age) {
this.age = age
}

function Chlid(name,age) {
Parent.call(this,age)
this.name = name
}

Chlid.prototype = Object.create(Parent.prototype)
Chlid.prototype.constructor = Child

const MyClass = (function () {
  function MyClass(args) {
    if (!(this instanceof MyClass)) {
      throw new TypeError(
        "Class constructor MyClass cannot be invoked without 'new'"
      );
    }
    this.args = args;
  }

  let staticMethods = [
    {
      key: "staticFunc",
      value: function () {
        console.log(this.args);
      },
    },
  ];
  let methods = [
    {
      key: "func",
      value: function () {
        console.log(this.args);
      },
    },
  ];

  defineProperties(MyClass, staticMethods);
  defineProperties(MyClass.prototype, methods);

  function defineProperties(target, descriptors) {
    for (let descriptor of descriptors) {
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) {
        descriptor.writable = true;
      }
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }

  return MyClass;
})();
function Parent (name) {
    this.name = name;
}

Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child (name) {
    Parent.call(this, name);
}

// 关键的三步
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
function Father(name){
  this.name = name
}

Father.staticFatherValue = 'staticFatherValue'

Father.prototype.sayName = function() {
  console.log(this.name)
}

function Child(name, age) {
  Father.call(this, name)
  this.age = age
}

Child.prototype = Object.create(Father.prototype, {
  constructor: {
    value: Child,
    configurable: false,
    enumerable: false,
    writeable: false
  }
})

Child.prototype.sayAge = function(){
  console.log(this.age)
}

// 继承静态属性
Child.__proto__ = Father

let child = new Child('Amy', 7)
child.sayName() //Amy
child.sayAge() // 7
console.log(Child.staticFatherValue) // staticFatherValue

function Parent (name, actions) {
this.name = name
this.actions = actions
}

Parent.prototype.getName = function () {
console.log(this.name + '调用了getName');
}

function Child() {
Parent.apply(this, arguments);
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

const c1 = new Child('xsh', ['eat']);

方法一 借助Babel转义实现

能力实在有限,感觉自己完全写不出来这种问题,手写Class 完全是自己的知识盲区
后面有能力会再写方法和具体实现方式讲解,写在后面更新

"use strict";

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a 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);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }
  return obj;
}

var Parent = /*#__PURE__*/ (function () {
  function Parent() {
    _classCallCheck(this, Parent);

    _defineProperty(this, "name", 123);
  }

  _createClass(Parent, [
    {
      key: "getName",
      value: function getName() {
        console.log(this.name);
      }
    }
  ]);

  return Parent;
})();

var child = new Parent();
console.log(child.getName());

function Father() {}
funciton Son() {
Father.call(this);
}
Son.prototype = Object.create(Father.prototype);
Son.prototype.constructor = Son;