предисловие
Функции в JS — это высшие граждане, но знаете ли вы разницу между функцией и классом?
Эта статья начинается с реализации PolyFill, затем переходит к анализу производительности, а затем к обзору использования Harbin Foundation;
Кроме того, передняя часть Шэньчжэня ищет ямы, и большие парни, у которых есть ямы, с трудом проталкивают их внутрь.
1. PolyFill
1. Используйте нативный js для создания простого класса;
2. Согласно приведенному выше использованию, мы знаем, что класс должен вызываться через new и не может быть вызван напрямую;
// 阻止直接()调用,直接在ES6运行Parent(),这是不允许的,ES6中抛出Class constructor Parent cannot be invoked without 'new'错误
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
3. Он может определять свойства экземпляра
// _createClass方法,它调用Object.defineProperty方法去给新创建的Parent添加各种属性
// defineProperties(Constructor.prototype, protoProps)是给原型添加属性
// defineProperties(Constructor, staticProps)是添加静态属性
const _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;
};
}();
4. Реализовать наследование
function _inherits(subClass, superClass) {
// 判断父类必须是函数
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
//Object.setPrototypeOf(obj, prototype),将一个指定的对象的原型设置为另一个对象或者null
// 等同于 subClass.prototype.__proto__ = superClass.prototype
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
5. Полный демо-код
Пожалуйста, нажмите:, добро пожаловать звезда!
2. производительность
2.1 Сначала проверьте
1. Сначала используйте стенд плагинов Google с открытым исходным кодом, чтобы проверить производительность функции и класса;
Если вы не знаете, что такое BenchMark,Пожалуйста, нажмите:
2. Протестируйте код
const bench = require('benchmark')
const suite = new bench.Suite()
function myFun(i) {
let baz = 42;
}
class myClass{
constructor() {
this.fol = 42;
}
}
suite
.add('function', () => {
myFun()
})
.add('class', () => {
myClass()
})
.on('cycle', (evt) => {
console.log(String(evt.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run()
3. Результаты испытаний
// node 版本v10.16.0
function x 815,978,962 ops/sec ±4.53% (87 runs sampled)
class x 812,855,174 ops/sec ±4.49% (88 runs sampled)
Fastest is function,class
// 可以看出 class 和 function 速度差不多
4. Полный код
Пожалуйста, нажмите:, добро пожаловать звезда!
2.2 Причины
4. Элемент функции АСТ
Functions
2. Элементы АСТ класса:
ClassBody
MethodDefinition
ClassDeclaration
ClassExpression
мета-атрибут
Хотя AST добавила новые элементы AST, внутренние свойства и способы увеличились по сравнению с функцией, поэтому производительность двух в основном одинакова;
Но код определения класса более удобен в сопровождении;
3 Выполнение хуков и классов
3.1 Сначала проверьте
1. Зная, что класс в несколько раз быстрее, чем функция в тесте 2.1;
2. Предположим, что у сцены есть родительский компонент, который обертывает подкомпонент функции и подкомпонент класса.После рендеринга компонента класса определенная функция может быть вызвана через this.func и не будет воссоздана, т.к. функциональный компонент будет создан заново Выполните его один раз и повторно создайте необходимые функции Это перехватчики более требовательны к производительности, чем классы?
const React = require('react')
const ReactDOM = require('react-dom/server.node')
const bench = require('benchmark')
const suite = new bench.Suite()
function Func(){
return React.createElement('span', {onClick: () => {console.log('click') }}, 'children')
}
class Cls extends React.Component{
handleP() {
console.log('click')
}
render(){
return React.createElement('span', {onClick: this.handleP}, 'children')
}
}
suite
.add('function component', () => {
ReactDOM.renderToString(React.createElement(Func))
})
.add('class component', () => {
ReactDOM.renderToString(React.createElement(Cls))
})
.on('cycle', (evt) => {
console.log(String(evt.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run()
3. Результаты
// node 版本v10.16.0
function component x 110,115 ops/sec ±13.91% (44 runs sampled)
class component x 118,909 ops/sec ±12.71% (43 runs sampled)
Fastest is class component,function component
4. Полный код
Пожалуйста, нажмите:, добро пожаловать звезда!
3.2 Причины
Официальный ответ на реагирование:
4. Хуки позволяют избежать больших накладных расходов, необходимых для классов, таких как накладные расходы на создание экземпляров класса в конструкторе и привязку обработчиков событий.
2. Использование хуков не требует глубокой вложенности дерева компонентов, которая преобладает в кодовых базах, использующих компоненты более высокого порядка, реквизиты рендеринга и контексты. С меньшим деревом компонентов у React меньше работы.
3. Традиционно проблемы производительности со встроенными функциями в React были связаны с тем, как передача нового обратного вызова в каждый модуль рендеринга нарушает оптимизацию дочерних компонентов shouldComponentUpdate. Хуки решают эту проблему тремя способами.
Хуки useCallback позволяют вам сохранять ссылку на один и тот же обратный вызов между повторными рендерингами, так что shouldComponentUpdate продолжает работать:
// Will not change unless `a` or `b` changes
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
Хук useMemo упрощает управление обновлением отдельных дочерних элементов, уменьшая потребность в чистых компонентах;
useReducerHook уменьшает необходимость глубокой передачи обратных вызовов
4. Использование
Это основная статья, предназначенная только для того, чтобы вы ознакомились с использованием;
class — это синтаксический сахар для функции;
4.1 Определение метода
выражение
const MyClass = class My {
getClasOsName() {
return My.name;
}
};
декларативный
const MyClass = class My {
getClassName() {
return My.name;
}
};
4.2 Строгий режим
Внутренне это строгий режим по умолчанию
// 引用一个未声明的变量
function Bar() {
baz = 42; // it's ok
}
const bar = new Bar();
class Foo {
constructor() {
fol = 42; // ReferenceError: fol is not defined
}
}
const foo = new Foo();
4.3 constructor
Метод класса по умолчанию, по умолчанию пусто, при генерации новой команды экземпляром объекта метод вызывается автоматически;
Метод конструктора — это специальный метод, используемый для создания и инициализации объекта и возвращаемый по умолчанию;
В классе может быть только один специальный метод с именем конструктор;
В конструкторе ключевое слово super может использоваться для вызова метода конструктора родительского класса;
class Rectangle {
// 构造函数
constructor(height, width) {
this.height = height;
this.width = width;
}
get area() {
return this.calcArea();
}
calcArea() {
return this.height * this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
4.4 static
Ключевое слово static создает статические методы для класса;
Вызов статических методов не требует создания экземпляра класса и не может быть вызван экземпляром объекта;
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2)); // 7.0710678118654755
Когда вызывается статический метод или метод прототипа, если для this нет значения, то это будет неопределенное состояние;
Это и следует ли использовать статический независимо от режима, потому что тело класса кода класса имеет статический режим выполнения по умолчанию;
class Animal {
talk() {
return this;
}
static drink() {
return this;
}
}
let obj = new Animal();
obj.talk(); // Animal {}
let talk = obj.talk;
talk(); // undefined
Animal.drink() // class Animal
let drink = Animal.drink;
drink(); // undefined
4.5 Указание на конструкторы
class Point {
// ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true
4.6 Должен вызываться с новым
function Bar() {
this.bar = 42;
}
const bar = Bar(); // 正常执行,也可以同 new 调用
class Foo {
constructor() {
this.foo = 42;
}
}
const foo = Foo(); // 报错
// 引用一个未声明的变量
function Bar() {
this.bar = 42;
}
Bar.answer = function() {
return 42;
};
Bar.prototype.print = function() {
console.log(this.bar);
};
const barKeys = Object.keys(Bar); // ['answer']
const barProtoKeys = Object.keys(Bar.prototype); // ['print']
class Foo {
constructor() {
this.foo = 42;
}
static answer() {
return 42;
}
print() {
console.log(this.foo);
}
}
const fooKeys = Object.keys(Foo); // []
const fooProtoKeys = Object.keys(Foo.prototype); // []
4.8 Атрибуты определены для классов по умолчанию
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
Потому что свойства x, y явно определены в этом (экземпляре), а свойство toString определено в классе Point по умолчанию.
4.9 Геттеры и сеттеры
Подобно функции, ключевые слова get и set можно использовать внутри «класса», чтобы установить функцию сохранения значения и функцию получения значения для свойства, а также перехватить поведение доступа к свойству.
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'getter'
4.10 это указывает на
Указание по умолчанию на экземпляр
class My {
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const my = new My();
const { printName } = logger;
printName(); // 报错,print未定义
Решение 1. Вы можете связать это в конструкторе
class My {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
Решение 2. Используя прокси, при получении метода автоматически связывайте этот
function selfish (target) {
const cache = new WeakMap();
const handler = {
get (target, key) {
const value = Reflect.get(target, key);
if (typeof value !== 'function') {
return value;
}
if (!cache.has(value)) {
cache.set(value, value.bind(target));
}
return cache.get(value);
}
};
const proxy = new Proxy(target, handler);
return proxy;
}
const logger = selfish(new Logger());
4.11 super
4. Ключевое слово super может использоваться либо как функция, либо как объект;
2. Когда super вызывается как функция, она представляет конструктор родительского класса;
class Person {}
class Child extends Person {
constructor() {
// 调用父类的构造函数
// 返回子类 Child
// 等同于Person.prototype.constructor.call(this)
super();
}
}
3. В качестве объекта обычный метод указывает на объект-прототип родительского класса, в статическом методе он указывает на родительский класс
// 普通方法
class Person {
p() {
return 2;
}
}
class Child extends Person{
constructor() {
super();
console.log(super.p()); // 2
}
}
let child = new Child();
// 子类Child当中的super.p(),就是将super当作一个对象使用。这时,super在普通方法之中,指向Person.prototype,所以super.p()就相当于Person.prototype.p()
// 静态方法
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
4.12 extends
отец
class Person{
constructor(name,birthday){
this.name = name;
this.birthday= birthday;
}
intro(){
return `${this.name},${this.birthday}`
}
}
Подкласс
class Child extends Person{
constructor(name,birthday){
super(name,birthday);
}
}
let child = new Child('xiaoming','2020-1-25');
console.log(child.intro()); //zhangsan,1988-04-01
4.13 Переменное продвижение отсутствует
new Foo(); // ReferenceError
class Foo {}
4.14 Как реализовать множественное наследование
4. Функция и класс могут наследоваться только по одному за раз;
// 如 A继承 B和C
class A extends B{}
class A extends C{}
2. Это все еще относительно низко, чтобы писать таким образом. Давайте рассмотрим методы примесей Vue и React, которые используются для копирования функций нескольких классов в новый класс; мы можем просто реализовать примеси, ядром является обход B, Свойство прототипов C, установленное в A через Object.defineProperty;
function mixin(constructor) {
return function (...args) {
for (let arg of args) {
for (let key of Object.getOwnPropertyNames(arg.prototype)) {
if (key === 'constructor') continue // 跳过构造函数
Object.defineProperty(constructor.prototype, key, Object.getOwnPropertyDescriptor(arg.prototype, key))
}
}
}
}
mixin(A)(B,C)
const a = new A()
5. Резюме
Оригинальные кодовые слова непросты, ваша звезда - движущая сила для меня, чтобы продолжать создавать и обновлять, добро пожаловать, звезда!
Кроме того, передняя часть Шэньчжэня ищет ямы, и большие парни, у которых есть ямы, с трудом проталкивают их внутрь.