Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >ES5和ES6函数你不知道的区别【面试篇】 前言1. PolyFill2.性能上3 hooks和 class 的性能4.用法上5.总结

ES5和ES6函数你不知道的区别【面试篇】 前言1. PolyFill2.性能上3 hooks和 class 的性能4.用法上5.总结

作者头像
火狼1
发布于 2020-05-09 08:08:35
发布于 2020-05-09 08:08:35
2.1K00
代码可运行
举报
运行总次数:0
代码可运行

前言

JS 中函数是高等公民,但是function 和 class 的区别你真的清楚嘛? 本文从PolyFill 实现,再到性能分析,再复习哈基础篇的使用; 另外深圳前端求坑,有坑的大佬麻烦内推一下。

1. PolyFill

1.利用原生 js 撸一个简单的 class; 2.根据上面的用法知道 class 必须通过 new 调用,不能直接调用;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 阻止直接()调用,直接在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
代码运行次数:0
运行
AI代码解释
复制
 _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
代码运行次数:0
运行
AI代码解释
复制
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!

2.性能上

2.1 先测试下

4.先用 Google 的开源插件 bench测试下 function 和 class 的性能; 如果不知道 benchMark 是啥的,请戳:;

2.测试代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
// 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 速度差不多

2.2 原因

4.function 的 AST 元素 Functions

2.class 的 AST元素: ClassBody MethodDefinition ClassDeclaration ClassExpression 元属性

AST 虽然新增了新的 AST 元素,但是内部属性和方法相对于 function 来说增加了很多,所以两个性能基本差不多; 但是 class 定义代码更利于维护;

3 hooks和 class 的性能

3.1 先测试下

4.在 2.1 测试中知道 class 比 function 要快好几倍; 2.假设场景是有一个父组件,包裹一个function子组件和class子组件,class组件在render过后,定义好的function,可以通过this.func进行调用,并且不会重新再创建,function组件会重新执行一遍,并且重新进行创建需要的function,那是不是 hooks 比 class 更耗性能呢;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
// 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 性能基本差不多

3.2 原因

React官方回答: 1.Hooks避免了类所需的大量开销,例如在构造器中创建类实例和绑定事件处理程序的开销。 2.使用Hooks的不需要在使用高阶组件,渲染道具和上下文的代码库中普遍存在的深层组件树嵌套。使用较小的组件树,React要做的工作更少。 3.传统上,与React中的内联函数有关的性能问题与如何在每个渲染器上传递新的回调破坏shouldComponentUpdate子组件的优化有关。Hooks从三个方面解决了这个问题。 该useCallback 的 hooks可以让你保持相同的回调引用之间重新呈现,这样shouldComponentUpdate继续工作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Will not change unless `a` or `b` changes
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

该useMemo钩使得它更容易控制,当个别儿童的更新,减少了对纯组件的需求; useReducerHook减少了深入传递回调的需要

4.用法上

这个是基础篇,只是带大家回顾一下用法; class 是 function 的语法糖;

4.1 定义方法

表达式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const MyClass = class My {
  getClasOsName() {
    return My.name;
  }
};

声明式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const MyClass = class My {
  getClassName() {
    return My.name;
  }
};

4.2 严格模式

内部是默认严格模式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 引用一个未声明的变量
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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 指向构造函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true

4.6 必须用 new 调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function Bar() {
  this.bar = 42;
}
const bar = Bar(); // 正常执行,也可以同 new 调用

class Foo {
  constructor() {
    this.foo = 42;
  }
}
const foo = Foo(); // 报错

4.7 内部methods 不可枚举

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 引用一个未声明的变量
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
代码运行次数:0
运行
AI代码解释
复制
//定义类
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 上.

4.9 getter 和 setter

和function 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 this 指向

默认指向类的实例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
class My {
  constructor() {
    this.printName = this.printName.bind(this);
  }

  // ...
}

解决方法二:使用Proxy,获取方法的时候,自动绑定this

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
class Person {}

class Child extends Person {
  constructor() {
    // 调用父类的构造函数
    // 返回子类 Child
    // 等同于Person.prototype.constructor.call(this)
    super();
  }
}

3.作为对象,普通方法指向父类的原型对象;在静态方法中,指向父类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 普通方法
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
代码运行次数:0
运行
AI代码解释
复制
// 静态方法
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

父类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Person{
  constructor(name,birthday){
    this.name = name;
    this.birthday= birthday;
  }
  intro(){
    return `${this.name},${this.birthday}`
  }
}

子类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
new Foo(); // ReferenceError
class Foo {}

4.14 怎么实现多继承

4.function 和 class 单次只能继承一个;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 如 A继承 B和C
class A extends B{}
class A extends C{}

2.这样写还是比较 low,我们回顾下,Vue 和 React 的 mixin 方法,用来将多个Class的功能复制到一个新的Class上; 我们可以简单来实现一个 mixins,核心是遍历 B,C原型的属性,通过Object.defineProperty设置到 A上;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.总结

原创码字不易,你的 star是我持续创作更新的动力,欢迎 star! 另外深圳前端求坑,有坑的大佬麻烦内推一下。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一万字ES6的class类,再学不懂,请来找我(语法篇)
上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。
coder_koala
2021/11/12
3480
ES5的继承和ES6的继承有什么区别?让Babel来告诉你
如果以前问我ES5的继承和ES6的继承有什么区别,我一定会自信的说没有区别,不过是语法糖而已,充其量也就是写法有区别,但是现在我会假装思考一下,然后说虽然只是语法糖,但也是有点小区别的,那么具体有什么区别呢,不要走开,下文更精彩!
街角小林
2022/03/21
6990
ES5的继承和ES6的继承有什么区别?让Babel来告诉你
详解ES6中的class
class是一个语法糖,其底层还是通过 构造函数 去创建的。所以它的绝大部分功能,ES5 都可以做到。新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
木子星兮
2020/07/16
5290
ES6语法中类(class)的实现原理
JavaScript语言不同于其他的类C语言,没有提供类的概念,但是可以提供类似的语法糖来实现JS面向对象的编程范式,本质上不是严格意义上的类
伯爵
2019/10/12
2.1K0
ES6语法中类(class)的实现原理
ES6-标准入门·Class 类
直至 ES6,JavaScript 终于有了“类”的概念,它简化了之前直接操作原型的语法,也是我最喜欢的新特性之一,但此类非彼类,它不同于熟知的如 Java 中的类,它本质上只是一颗语法糖。
数媒派
2022/12/01
3070
玩转ES6(四)Set、Map、Class类和decorator 装饰器
在看Class之前建议看一下js的面向对象 https://juejin.im/post/5b8a8724f265da435450c591
前端迷
2019/12/03
8880
ES6的class详解
声明创建一个基于原型继承的具有给定名称的新类。 和类表达式一样,类声明体在严格模式下运行。构造函数是可选的。类声明不可以提升(这与函数声明不同)
用户10106350
2022/10/28
3840
【THE LAST TIME】一文吃透所有JS原型相关知识点
首先我想说,【THE LAST TIME】系列的的内容,向来都是包括但不限于标题的范围。
Nealyang
2019/11/04
1.1K0
JS原生方法原理探究(六)从 Babel 转译过程浅谈 ES6 实现继承的原理
都说 ES6 的 Class 是 ES5 的语法糖,那么 ES6 的 Class 是如何实现的呢?其实现继承的原理又是什么呢?不妨我们通过 Babel 转译代码的方式,看看其中有什么门道。
Chor
2021/06/08
1.2K0
es6 class
1.Es6引入了Class 类这个概念,作为对象的模板,通过class 关键字,可以定义类。 2.类和模块的内部,默认就是严格模式,所以不需要使用use strict 指定运行模式。 3.constructor 方法就是类的默认方法,通过new 命令生成对象实例时,自动调动该   方法,一个类必须有constructor 方法,如果没有显示定义,一个空的constructor   方法会默认添加。 4.类的实例对象   必须使用new ,像函数那样调用Class ,将会报错。     例如:       c
用户1197315
2018/01/19
6190
Babel下的ES6兼容性与规范
IMWeb前端团队
2017/12/28
2.1K0
深入理解class的继承,助你成为优秀的前端
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
前端老鸟
2019/07/29
3630
深入理解class的继承,助你成为优秀的前端
ES6——类(Class)
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。 ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。
羊羽shine
2019/07/31
1.1K0
理解装饰器是怎么使用的
js 和 ts 中经常看到@+函数名的字样这个就是装饰器。它可以修饰类,类的属性,类的原型上的方法,说的简单一点它就是一个函数,可以传递参数在修饰的时候把这个类的属性传递给修饰的函数。
小丑同学
2020/09/21
6760
面试官问:JS的this指向
面试官出很多考题,基本都会变着方式来考察this指向,看候选人对JS基础知识是否扎实。读者可以先拉到底部看总结,再谷歌(或各技术平台)搜索几篇类似文章,看笔者写的文章和别人有什么不同(欢迎在评论区评论不同之处),对比来看,验证与自己现有知识是否有盲点,多看几篇,自然就会完善自身知识。
若川
2020/03/19
7940
深入 Babel 6 loose 模式
Babel 的 loose 模式将 ES6 代码转译成 ES5 代码,loose 模式是不太忠实于 ES6 语义的。这篇文章解释了它是怎么工作的以及它的优点与缺点(剧透:通常是不推荐的)。
逆葵
2019/04/25
4K0
JS 继承
用过 React的读者知道,经常用 extends继承 React.Component:
grain先森
2019/03/28
3.1K0
JS 继承
es6转es5_es6转es5插件
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/09
7380
es6转es5_es6转es5插件
ECMAScript中类与继承详解(Java对比学习)
如果声明一个一个类的时候没有声明构造函数,那么会默认添加一个空的构造函数,构造函数在new实例化一个对象的时候会被调用
coder_koala
2019/07/30
4520
聊一聊面试中经常被问到的Tree Shaking
天下武功,唯快不破!最新版的 antd 以及 vue 都对 Tree Shaking 提供了支持。我们内部的组件在支持这部分功能时,也专门梳理了相关的特性。这是四月份写的文章了,长时间不用就会忘,复习一下!
前端迷
2020/08/28
2.1K0
聊一聊面试中经常被问到的Tree Shaking
推荐阅读
相关推荐
一万字ES6的class类,再学不懂,请来找我(语法篇)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验