本帖最后由 逆风TO 于 2020-3-25 14:19 编辑
1. PolyFill1.利用原生 js 撸一个简单的 class;
2.根据上面的用法知道 class 必须通过 new 调用,不能直接调用; [JavaScript] 纯文本查看 复制代码 // 阻止直接()调用,直接在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.里面可以定义实例的属性[JavaScript] 纯文本查看 复制代码 // _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.实现继承
[JavaScript] 纯文本查看 复制代码 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;}
2.性能上2.1 先测试下1.先用 Google 的开源插件 bench测试下 function 和 class 的性能;
如果不知道 benchMark 是啥的,请戳: 2.测试代码 [JavaScript] 纯文本查看 复制代码 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.测试结果 [JavaScript] 纯文本查看 复制代码 // node 版本v10.16.0function 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 速度差不多 2.2 原因4.function 的 AST 元素
Functions 2.class 的 AST元素:
ClassBody
MethodDefinition
ClassDeclaration
ClassExpression
元属性 AST 虽然新增了新的 AST 元素,但是内部属性和方法相对于 function 来说增加了很多,所以两个性能基本差不多;
但是 class 定义代码更利于维护; 3 hooks和 class 的性能 3.1 先测试下1.在 2.1 测试中知道 class 比 function 要快好几倍;
2.假设场景是有一个父组件,包裹一个function子组件和class子组件,class组件在render过后,定义好的function,可以通过this.func进行调用,并且不会重新再创建,function组件会重新执行一遍,并且重新进行创建需要的function,那是不是 hooks 比 class 更耗性能呢;
[JavaScript] 纯文本查看 复制代码 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.结果
[JavaScript] 纯文本查看 复制代码 // node 版本v10.16.0function 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
可以看出 function 和 class 性能基本差不多
3.2 原因React官方回答:
4.Hooks避免了类所需的大量开销,例如在构造器中创建类实例和绑定事件处理程序的开销。
2.使用Hooks的不需要在使用高阶组件,渲染道具和上下文的代码库中普遍存在的深层组件树嵌套。使用较小的组件树,React要做的工作更少。
3.传统上,与React中的内联函数有关的性能问题与如何在每个渲染器上传递新的回调破坏shouldComponentUpdate子组件的优化有关。Hooks从三个方面解决了这个问题。
该useCallback 的 hooks可以让你保持相同的回调引用之间重新呈现,这样shouldComponentUpdate继续工作: [JavaScript] 纯文本查看 复制代码 // Will not change unless `a` or `b` changesconst memoizedCallback = useCallback(() => { doSomething(a, b);}, [a, b]); 该useMemo钩使得它更容易控制,当个别儿童的更新,减少了对纯组件的需求;
useReducerHook减少了深入传递回调的需要 4.用法上 这个是基础篇,只是带大家回顾一下用法;
class 是 function 的语法糖; 4.1 定义方法表达式
[JavaScript] 纯文本查看 复制代码 const MyClass = class My { getClasOsName() { return My.name; }};
声明式
[JavaScript] 纯文本查看 复制代码 const MyClass = class My { getClassName() { return My.name; }};
4.2 严格模式内部是默认严格模式 [JavaScript] 纯文本查看 复制代码 // 引用一个未声明的变量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是 class 的默认方法,默认为空,通过new命令生成对象实例时,自动调用该方法;
constructor方法是一个特殊的方法,用来创建并初始化一个对象,并默认返回;
在一个class中只能有一个命名为constructor的特殊方法;
constructor中可以通过super关键字,调用父类的constructor方法; [JavaScript] 纯文本查看 复制代码 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 staticstatic关键字为一个class创建静态方法;
static methods的调用无需对class实例化,也不能被实例对象所调用; [JavaScript] 纯文本查看 复制代码 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 当static或prototype method被调用的时候,如果没有对this赋值,那么this将是undefine状态;
这和是否采用static模式无关,因为class类体中的代码已经默认执行static模式;[JavaScript] 纯文本查看 复制代码 class Animal { talk() { return this; } static drink() { return this; }}let obj = new Animal();obj.talk(); // Animal {}let talk = obj.talk;talk(); // undefinedAnimal.drink() // class Animallet drink = Animal.drink;drink(); // undefined 4.5 指向构造函数
[JavaScript] 纯文本查看 复制代码 class Point { // ...}typeof Point // "function"Point === Point.prototype.constructor // true
4.6 必须用 new 调用[JavaScript] 纯文本查看 复制代码 function Bar() { this.bar = 42;}const bar = Bar(); // 正常执行,也可以同 new 调用class Foo { constructor() { this.foo = 42; }}const foo = Foo(); // 报错
4.7 内部methods 不可枚举[JavaScript] 纯文本查看 复制代码 // 引用一个未声明的变量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 属性默认定义在类上[JavaScript] 纯文本查看 复制代码 //定义类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') // truepoint.hasOwnProperty('y') // truepoint.hasOwnProperty('toString') // falsepoint.__proto__.hasOwnProperty('toString') // true
因为属性 x,y 是显式定义在 this(实例) 上,而 toString 属性默认定义在类 Point 上. 4.9 getter 和 setter和function 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为
[JavaScript] 纯文本查看 复制代码 class MyClass { constructor() { // ... } get prop() { return 'getter'; } set prop(value) { console.log('setter: '+value); }}let inst = new MyClass();inst.prop = 123;// setter: 123inst.prop// 'getter'
4.10 this 指向默认指向类的实例 [JavaScript] 纯文本查看 复制代码 class My { printName(name = 'there') { this.print(`Hello ${name}`); } print(text) { console.log(text); }}const my = new My();const { printName } = logger;printName(); // 报错,print未定义 解决方法一:可以在constructor绑定 this [JavaScript] 纯文本查看 复制代码 class My { constructor() { this.printName = this.printName.bind(this); } // ...} 解决方法二:使用Proxy,获取方法的时候,自动绑定this [JavaScript] 纯文本查看 复制代码 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 super4.super这个关键字,既可以当作函数使用,也可以当作对象使用;
2.super作为函数调用时,代表父类的构造函数; [JavaScript] 纯文本查看 复制代码 class Person {}class Child extends Person { constructor() { // 调用父类的构造函数 // 返回子类 Child // 等同于Person.prototype.constructor.call(this) super(); }} 3.作为对象,普通方法指向父类的原型对象;在静态方法中,指向父类 [JavaScript] 纯文本查看 复制代码 // 普通方法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() [JavaScript] 纯文本查看 复制代码 // 静态方法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 1var child = new Child();child.myMethod(2); // instance 2 4.12 extends父类 [JavaScript] 纯文本查看 复制代码 class Person{ constructor(name,birthday){ this.name = name; this.birthday= birthday; } intro(){ return `${this.name},${this.birthday}` }} 子类 [JavaScript] 纯文本查看 复制代码 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 不存在变量提升[JavaScript] 纯文本查看 复制代码 new Foo(); // ReferenceErrorclass Foo {}
4.14 怎么实现多继承4.function 和 class 单次只能继承一个; [JavaScript] 纯文本查看 复制代码 // 如 A继承 B和Cclass A extends B{}class A extends C{} 2.这样写还是比较 low,我们回顾下,Vue 和 React 的 mixin 方法,用来将多个Class的功能复制到一个新的Class上; 我们可以简单来实现一个 mixins,核心是遍历 B,C原型的属性,通过Object.defineProperty设置到 A上;
[JavaScript] 纯文本查看 复制代码 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()
转载自链接:https://juejin.im/post/5e7aadd16fb9a07cc3216c17
|