JS 中函数是高等公民,可是function 和 class 的区别你真的清楚嘛?
本文从PolyFill 实现,再到性能分析,再复习哈基础篇的使用;
另外深圳前端求坑,有坑的大佬麻烦内推一下。
1.利用原生 js 撸一个简单的 class;
2.根据上面的用法知道 class 必须经过 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.里面能够定义实例的属性vue
_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.实现继承node
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.完整演示代码
请戳:,欢迎star!react
4.先用 Google 的开源插件 bench测试下 function 和 class 的性能;
若是不知道 benchMark 是啥的,请戳:;git
2.测试代码github
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.测试结果express
// 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.function 的 AST 元素
Functionsless
2.class 的 AST元素:
ClassBody
MethodDefinition
ClassDeclaration
ClassExpression
元属性 dom
AST 虽然新增了新的 AST 元素,可是内部属性和方法相对于 function 来讲增长了不少,因此两个性能基本差很少;
可是 class 定义代码更利于维护;koa
4.在 2.1 测试中知道 class 比 function 要快好几倍;
2.假设场景是有一个父组件,包裹一个function子组件和class子组件,class组件在render事后,定义好的function,能够经过this.func进行调用,而且不会从新再建立,function组件会从新执行一遍,而且从新进行建立须要的function,那是否是 hooks 比 class 更耗性能呢;
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
能够看出 function 和 class 性能基本差很少
React官方回答:
1.Hooks避免了类所需的大量开销,例如在构造器中建立类实例和绑定事件处理程序的开销。
2.使用Hooks的不须要在使用高阶组件,渲染道具和上下文的代码库中广泛存在的深层组件树嵌套。使用较小的组件树,React要作的工做更少。
3.传统上,与React中的内联函数有关的性能问题与如何在每一个渲染器上传递新的回调破坏shouldComponentUpdate子组件的优化有关。Hooks从三个方面解决了这个问题。
该useCallback 的 hooks可让你保持相同的回调引用之间从新呈现,这样shouldComponentUpdate继续工做:
// Will not change unless `a` or `b` changes const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);
该useMemo钩使得它更容易控制,当个别儿童的更新,减小了对纯组件的需求;
useReducerHook减小了深刻传递回调的须要
这个是基础篇,只是带你们回顾一下用法;
class 是 function 的语法糖;
表达式
const MyClass = class My { getClasOsName() { return My.name; } };
声明式
const MyClass = class My { getClassName() { return My.name; } };
内部是默认严格模式
// 引用一个未声明的变量 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();
是 class 的默认方法,默认为空,经过new命令生成对象实例时,自动调用该方法;
constructor方法是一个特殊的方法,用来建立并初始化一个对象,并默认返回;
在一个class中只能有一个命名为constructor的特殊方法;
constructor中能够经过super关键字,调用父类的constructor方法;
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
static关键字为一个class建立静态方法;
static methods的调用无需对class实例化,也不能被实例对象所调用;
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模式;
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
class Point { // ... } typeof Point // "function" Point === Point.prototype.constructor // true
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); // []
//定义类 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 是显式定义在 this(实例) 上,而 toString 属性默认定义在类 Point 上.
和function 同样,在“类”的内部可使用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'
默认指向类的实例
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
class My { constructor() { this.printName = this.printName.bind(this); } // ... }
解决方法二:使用Proxy,获取方法的时候,自动绑定this
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.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
父类
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
new Foo(); // ReferenceError class Foo {}
4.function 和 class 单次只能继承一个;
// 如 A继承 B和C class A extends B{} class A extends C{}
2.这样写仍是比较 low,咱们回顾下,Vue 和 React 的 mixin 方法,用来将多个Class的功能复制到一个新的Class上; 咱们能够简单来实现一个 mixins,核心是遍历 B,C原型的属性,经过Object.defineProperty设置到 A上;
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()
原创码字不易,你的 star是我持续创做更新的动力,欢迎 star!另外深圳前端求坑,有坑的大佬麻烦内推一下。