A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 逆风TO 黑马粉丝团   /  2020-3-25 13:56  /  6851 人查看  /  174 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 逆风TO 于 2020-3-25 14:19 编辑

1. PolyFill
1.利用原生 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 static
static关键字为一个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 super
4.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






174 个回复

倒序浏览
认真学习,感谢分享
回复 使用道具 举报
小公举 来自手机 中级黑马 2020-5-18 14:45:51
藤椅
谢谢楼主的分享!
回复 使用道具 举报
回复 使用道具 举报
66666666666666
回复 使用道具 举报
666666666666
回复 使用道具 举报
666666666666666666666666666
回复 使用道具 举报
666666666666666666666666666666
回复 使用道具 举报
你只管去努力,剩下的交给天意
回复 使用道具 举报
我爱我1022 来自手机 中级黑马 2020-5-18 15:36:00
10#
回复 使用道具 举报
加油                              
回复 使用道具 举报
我爱我1022 来自手机 中级黑马 2020-5-18 15:50:43
12#
回复 使用道具 举报
666666666666666666666666666666
回复 使用道具 举报
66666666666666
回复 使用道具 举报
6666666666666666666
回复 使用道具 举报
你只管去努力,剩下的交给天意。
回复 使用道具 举报
感谢分享!
回复 使用道具 举报
加油加油加油!!!
回复 使用道具 举报
66666666666666666666666
回复 使用道具 举报
666666666666666666
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马