es6 filter方法_ES5和ES6函数你不知道的区别
前言
JS 中函數(shù)是高等公民,但是function 和 class 的區(qū)別你真的清楚嘛?本文從PolyFill 實(shí)現(xiàn),再到性能分析,再復(fù)習(xí)哈基礎(chǔ)篇的使用;另外深圳前端求坑,有坑的大佬麻煩內(nèi)推一下。
1. PolyFill
1.利用原生 js 擼一個簡單的 class; 2.根據(jù)上面的用法知道 class 必須通過 new 調(diào)用,不能直接調(diào)用;
//?阻止直接()調(diào)用,直接在ES6運(yùn)行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");????}}復(fù)制代碼3.里面可以定義實(shí)例的屬性
//?_createClass方法,它調(diào)用Object.defineProperty方法去給新創(chuàng)建的Parent添加各種屬性//?defineProperties(Constructor.prototype,?protoProps)是給原型添加屬性//?defineProperties(Constructor,?staticProps)是添加靜態(tài)屬性const?_createClass?=?function?()?{????function?defineProperties(target,?props)?{????????for?(var?i?=?0;?i?4.實(shí)現(xiàn)繼承
function?_inherits(subClass,?superClass)?{??//?判斷父類必須是函數(shù)????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),將一個指定的對象的原型設(shè)置為另一個對象或者null????//?等同于?subClass.prototype.__proto__?=?superClass.prototype????if?(superClass)?Object.setPrototypeOf???Object.setPrototypeOf(subClass,?superClass)?:?subClass.__proto__?=?superClass;}復(fù)制代碼5.完整演示代碼請戳:,歡迎star!
2.性能上
2.1 先測試下
1.先用 Google 的開源插件 bench測試下 function 和 class 的性能; 如果不知道 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()復(fù)制代碼3.測試結(jié)果
//?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?速度差不多復(fù)制代碼4.完整代碼請戳:,歡迎star!
2.2 原因
4.function 的 AST 元素Functions
2.class 的 AST元素: ClassBody MethodDefinition ClassDeclaration ClassExpression 元屬性
AST 雖然新增了新的 AST 元素,但是內(nèi)部屬性和方法相對于 function 來說增加了很多,所以兩個性能基本差不多; 但是 class 定義代碼更利于維護(hù);
3 hooks和 class 的性能
3.1 先測試下
1.在 2.1 測試中知道 class 比 function 要快好幾倍; 2.假設(shè)場景是有一個父組件,包裹一個function子組件和class子組件,class組件在render過后,定義好的function,可以通過this.func進(jìn)行調(diào)用,并且不會重新再創(chuàng)建,function組件會重新執(zhí)行一遍,并且重新進(jìn)行創(chuàng)建需要的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()復(fù)制代碼3.結(jié)果
//?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復(fù)制代碼可以看出 function 和 class 性能基本差不多
4.完整代碼請戳:,歡迎star!
https://github.com/lanzhsh/react-vue-koa/blob/master/js/class%2Cfunction/2.class-polyfill.js
3.2 原因
React官方回答:4.Hooks避免了類所需的大量開銷,例如在構(gòu)造器中創(chuàng)建類實(shí)例和綁定事件處理程序的開銷。2.使用Hooks的不需要在使用高階組件,渲染道具和上下文的代碼庫中普遍存在的深層組件樹嵌套。使用較小的組件樹,React要做的工作更少。3.傳統(tǒng)上,與React中的內(nèi)聯(lián)函數(shù)有關(guān)的性能問題與如何在每個渲染器上傳遞新的回調(diào)破壞shouldComponentUpdate子組件的優(yōu)化有關(guān)。Hooks從三個方面解決了這個問題。 該useCallback 的 hooks可以讓你保持相同的回調(diào)引用之間重新呈現(xiàn),這樣shouldComponentUpdate繼續(xù)工作:
//?Will?not?change?unless?`a`?or?`b`?changesconst?memoizedCallback?=?useCallback(()?=>?{??doSomething(a,?b);},?[a,?b]);復(fù)制代碼該useMemo鉤使得它更容易控制,當(dāng)個別兒童的更新,減少了對純組件的需求; useReducerHook減少了深入傳遞回調(diào)的需要
4.用法上
這個是基礎(chǔ)篇,只是帶大家回顧一下用法;class 是 function 的語法糖;
4.1 定義方法
表達(dá)式
const?MyClass?=?class?My?{??getClasOsName()?{????return?My.name;??}};復(fù)制代碼聲明式
const?MyClass?=?class?My?{??getClassName()?{????return?My.name;??}};復(fù)制代碼4.2 嚴(yán)格模式
內(nèi)部是默認(rèn)嚴(yán)格模式
//?引用一個未聲明的變量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();復(fù)制代碼4.3 constructor
是 class 的默認(rèn)方法,默認(rèn)為空,通過new命令生成對象實(shí)例時,自動調(diào)用該方法; constructor方法是一個特殊的方法,用來創(chuàng)建并初始化一個對象,并默認(rèn)返回; 在一個class中只能有一個命名為constructor的特殊方法; constructor中可以通過super關(guān)鍵字,調(diào)用父類的constructor方法;
class?Rectangle?{??//?構(gòu)造函數(shù)??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復(fù)制代碼4.4 static
static關(guān)鍵字為一個class創(chuàng)建靜態(tài)方法; static methods的調(diào)用無需對class實(shí)例化,也不能被實(shí)例對象所調(diào)用;
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復(fù)制代碼當(dāng)static或prototype method被調(diào)用的時候,如果沒有對this賦值,那么this將是undefine狀態(tài); 這和是否采用static模式無關(guān),因?yàn)閏lass類體中的代碼已經(jīng)默認(rèn)執(zhí)行static模式;
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復(fù)制代碼4.5 指向構(gòu)造函數(shù)
class?Point?{??//?...}typeof?Point?//?"function"Point?===?Point.prototype.constructor?//?true復(fù)制代碼4.6 必須用 new 調(diào)用
function?Bar()?{??this.bar?=?42;}const?bar?=?Bar();?//?正常執(zhí)行,也可以同?new?調(diào)用class?Foo?{??constructor()?{????this.foo?=?42;??}}const?foo?=?Foo();?//?報錯復(fù)制代碼4.7 內(nèi)部methods 不可枚舉
//?引用一個未聲明的變量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);?//?[]復(fù)制代碼4.8 屬性默認(rèn)定義在類上
//定義類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復(fù)制代碼因?yàn)閷傩?x,y 是顯式定義在 this(實(shí)例) 上,而 toString 屬性默認(rèn)定義在類 Point 上.
4.9 getter 和 setter
和function 一樣,在“類”的內(nèi)部可以使用get和set關(guān)鍵字,對某個屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為
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'復(fù)制代碼4.10 this 指向
默認(rèn)指向類的實(shí)例
class?My?{??printName(name?=?'there')?{????this.print(`Hello?${name}`);??}??print(text)?{????console.log(text);??}}const?my?=?new?My();const?{?printName?}?=?logger;printName();?//??報錯,print未定義復(fù)制代碼解決方法一:可以在constructor綁定 this
class?My?{??constructor()?{????this.printName?=?this.printName.bind(this);??}??//?...}復(fù)制代碼解決方法二:使用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());復(fù)制代碼4.11 super
4.super這個關(guān)鍵字,既可以當(dāng)作函數(shù)使用,也可以當(dāng)作對象使用; 2.super作為函數(shù)調(diào)用時,代表父類的構(gòu)造函數(shù);
class?Person?{}class?Child?extends?Person?{??constructor()?{????//?調(diào)用父類的構(gòu)造函數(shù)????//?返回子類?Child????//?等同于Person.prototype.constructor.call(this)????super();??}}復(fù)制代碼3.作為對象,普通方法指向父類的原型對象;在靜態(tài)方法中,指向父類
//?普通方法class?Person?{??p()?{????return?2;??}}class?Child?extends?Person{??constructor()?{????super();????console.log(super.p());?//?2??}}let?child?=?new?Child();//?子類Child當(dāng)中的super.p(),就是將super當(dāng)作一個對象使用。這時,super在普通方法之中,指向Person.prototype,所以super.p()就相當(dāng)于Person.prototype.p()復(fù)制代碼//?靜態(tài)方法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復(fù)制代碼4.12 extends
父類
class?Person{??constructor(name,birthday){????this.name?=?name;????this.birthday=?birthday;??}??intro(){????return?`${this.name},${this.birthday}`??}}復(fù)制代碼子類
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復(fù)制代碼4.13 不存在變量提升
new?Foo();?//?ReferenceErrorclass?Foo?{}復(fù)制代碼4.14 怎么實(shí)現(xiàn)多繼承
4.function 和 class 單次只能繼承一個;
//?如?A繼承?B和Cclass?A?extends?B{}class?A?extends?C{}復(fù)制代碼2.這樣寫還是比較 low,我們回顧下,Vue 和 React 的 mixin 方法,用來將多個Class的功能復(fù)制到一個新的Class上; 我們可以簡單來實(shí)現(xiàn)一個 mixins,核心是遍歷 B,C原型的屬性,通過Object.defineProperty設(shè)置到 A上;
function?mixin(constructor)?{??return?function?(...args)?{????for?(let?arg?of?args)?{??????for?(let?key?of?Object.getOwnPropertyNames(arg.prototype))?{????????if?(key?===?'constructor')?continue?//?跳過構(gòu)造函數(shù)????????Object.defineProperty(constructor.prototype,?key,?Object.getOwnPropertyDescriptor(arg.prototype,?key))??????}????}??}}mixin(A)(B,C)const?a?=?new?A()復(fù)制代碼5.總結(jié)
原創(chuàng)碼字不易,你的 star是我持續(xù)創(chuàng)作更新的動力,歡迎 star!
github地址: https://github.com/lanzhsh/react-vue-koa/blob/master/js/class%2Cfunction/2.class-polyfill.js
原鏈接:https://juejin.im/post/5e7aadd16fb9a07cc3216c17
總結(jié)
以上是生活随笔為你收集整理的es6 filter方法_ES5和ES6函数你不知道的区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吃鸡是什么游戏(是steam上的什么游戏
- 下一篇: 2999~8499 元,魅族 20 /