Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@iwill
Last active September 21, 2023 08:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iwill/2303057 to your computer and use it in GitHub Desktop.
Save iwill/2303057 to your computer and use it in GitHub Desktop.
$class-2.0.0
/*!
* Javascript library - $class 2.0.0
* https://gist.github.com/iwill/2303057
*/
export default function $class(source, SuperClass) {
// default values
SuperClass = SuperClass || Object;
source = source || {};
// constructor & super constructor
source.constructor = source.hasOwnProperty("constructor") ? source.constructor : function() {
SuperClass.apply(this, arguments);
};
// class & prototype
const Class = source.constructor;
Class.isPlainObject = true;
Class.prototype = new SuperClass();
// override Class.prototype[each] by source[each]
for (let each in source) {
Class.prototype[each] = source[each]; // include `constructor`
}
return Class;
}
/**
* $class 1.0.0
this.$class = function(src) {
src.constructor.prototype = src;
return src.constructor;
}; */
@iwill
Copy link
Author

iwill commented Mar 11, 2016

// class
const Person = $class({
    constructor: function Person(name) {
        this.name = name;
        this.friends = [];
    },
    makeFriend: function(person) {
        this.friends.push(person.name);
    }
});
// subclass
const Student = $class({
    constructor: function Student(name, grade) {
        // super constructor
        Person.apply(this, arguments);
        this.grade = grade;
    },
    makeFriend: function() {
        // super method
        Person.prototype.makeFriend.apply(this, arguments);
    }
}, Person);

@iwill
Copy link
Author

iwill commented Apr 6, 2022

不能说一毛一样,但实在是 太像了 😎😎

// class
const Person = class {
    constructor(name) {
        this.name = name;
        this.friends = [];
    }
    makeFriend(person) {
        this.friends.push(person.name);
    }
};
// subclass
const Student = class extends Person {
    constructor(name, grade) {
        // super constructor
        super(...arguments);
        this.grade = grade;
    }
    makeFriend(person) {
        // super method
        super.makeFriend(...arguments);
    }
}

@iwill
Copy link
Author

iwill commented Feb 8, 2023

如果您想改进这个代码,以下是一些建议:

可以使用 ECMAScript 6 中的 class 语法来定义类,因为它是更直观和更简洁的。

默认的构造函数应该在调用 SuperClass 构造函数之前检查它是否存在,以避免 TypeError。

源对象可能包含额外的属性,这些属性不应该在类原型中复制。因此,可以使用 hasOwnProperty 方法来检查该属性是否是 source 对象的自身属性,仅复制自身属性。

该函数可能会影响到原型链,因此请小心使用它。考虑使用 Object.create 方法来设置类原型,以避免影响到原型链。

—— ChatGPT

@iwill
Copy link
Author

iwill commented Sep 21, 2023

刚刚给 Class 增加了一个属性:Class.isPlainObject = true;,用来说明它的实例是 plain object —— 所有的属性都可遍历、可修改。

原本考虑用 Object.defineProperty(Class, "isPlainObject", { value: true }); 避免 isPlainObject 属性被修改,但是这好像偏离了本意,是的 isPlainObject 本身也应该可遍历、可修改。

做这个改动,主要源于最近用到的对 Object 的判断,判断的方法千奇百怪,逻辑其实有两种:

  • A: 严格的 Object 的直接实例,即所谓的 plain object
  • B: Object 子类的实例

本来 Object 子类的实例也不该被区别对待的,可是有两种特殊情况:区分数组和对象,以及用遍历所有属性。前者还好,有 Array.isArray() 可用,可是 ...

期盼已久的 ES6 的 class 并不如想象中那样完美,虽然有了 classextendsconstructorgetsetstatic,也支持了私有属性,但是 get 定义的属性不能遍历、不能序列化成 JSON
要用 Object.defineProperty(this, k, { value: v, enumerable: true })
或者 Object.defineProperties(this, { k: { value: v, enumerable: true } })
定义才行,不愧是 JavaScript 啊,一如既往的蛋疼。

所以,对 Object 的判断我偏向于 plain object 了。为了避免传统的 function + prototype 方式创建的 plain object 被误伤,我在 $class 创建的 Class 中添加了 isPlainObject 属性,注意是 Class 的属性,通过示例要这样访问 object.constructor.isPlainObject 访问。

function isObject(o) {
    return o?.constructor == Object || o?.constructor?.isPlainObject;
}

参考:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment