github 的地址 歡迎 starhtml
最近因爲在看一些 react 優化的方面,注意到了以前沒有注意到的東西,都知道在 react DOM 事件的綁定方式有幾種方式:node
在 constructor 中進行綁定。react
或者用箭頭函數(無 this)。git
對於須要動態傳參的形式:github
可使用閉包的方式web
或者能夠直接把處理函數傳入子組件,子組建時能夠拿到參數,再執行父組件的處理函數就能夠了算法
class App extends Component {
removeCharacter = index => () => {
const {list} = this.state;
list.splice(index, 1);
this.setState({
list
})
}
render() {
return (
<div>
{
this.state.list.map((value, index) =>(
<div
onClick={this.removeCharacter(index)}
key={value.id}
data={value}
>
點擊我
</div>
))
}
</div>
)
}
}
// 子組件處理的方式
class OtherApp extends Component {
removeCharacter = index => {
const {list} = this.state;
list.splice(index, 1);
this.setState({
list
})
}
render() {
return (
<div>
{
this.state.list.map((value, index) =>(
<Child
onClick={this.removeCharacter}
index={index}
key={value.id}
data={value}
/>
))
}
</div>
)
}
}
class Child extends Component {
handleClick = () => {
const { index, onClick} = this.props;
onClick(index)
}
render() {
return (
<div onClick={this.handleClick}>
{this.props.data}
</div>
)
}
}
複製代碼
重點介紹了須要傳參的方式,對於性能比較是要看具體的環境(和瀏覽器,平臺的優化都有關係),沒有什麼是絕對性能好的。chrome
看到了這篇博客的結論,就想具體實際比較一下:瀏覽器
若是每次都在 render 裏面的 jsx 去 bind 這個方法,會消耗性能,由於每次bind都會返回一個新函數,重複建立靜態函數確定是不合適的(閉包也是這樣,但 bind 內部有一系列的算法,比閉包複雜多了)
複製代碼
Chrome Version 72.0.3626.119 (Official Build) (64-bit)bash
react version "16.8.3"
node version v10.15.1
經過 chrome 的 JavaScript Profiler 進行性能分析,發現渲染1千次事件的渲染時間是差很少的(均採用首次刷新渲染)
能夠看到性能上雙方相差很少,而 Stack Overflow 上的 stackoverflow.com/questions/1… 以及在 chrome 中各個版本 bind 和 closure 以及 proxy 性能測試(點擊)發現 bind 是要比 closure 慢的多, 對於bind的實現以下:
Function.prototype.bind = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
複製代碼
發現內部的實現也是閉包!固然這個不是完整的處理,從實現上 bind 是對閉包的封裝,可讀性來講 bind 好,所以 bind 是要比 closure 慢的,可是 v8 作了優化,致使在 react 中差別也不是很大。
v8 裏面的實現(來自stackoverflow.com/questions/1…),尚未查看最新的實現。
function FunctionBind(this_arg) { // Length is 1.
if (!IS_SPEC_FUNCTION(this)) {
throw new $TypeError('Bind must be called on a function');
}
var boundFunction = function () {
// Poison .arguments and .caller, but is otherwise not detectable.
"use strict";
// This function must not use any object literals (Object, Array, RegExp),
// since the literals-array is being used to store the bound data.
if (%_IsConstructCall()) {
return %NewObjectFromBound(boundFunction);
}
var bindings = %BoundFunctionGetBindings(boundFunction);
var argc = %_ArgumentsLength();
if (argc == 0) {
return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2);
}
if (bindings.length === 2) {
return %Apply(bindings[0], bindings[1], arguments, 0, argc);
}
var bound_argc = bindings.length - 2;
var argv = new InternalArray(bound_argc + argc);
for (var i = 0; i < bound_argc; i++) {
argv[i] = bindings[i + 2];
}
for (var j = 0; j < argc; j++) {
argv[i++] = %_Arguments(j);
}
return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc);
};
%FunctionRemovePrototype(boundFunction);
var new_length = 0;
if (%_ClassOf(this) == "Function") {
// Function or FunctionProxy.
var old_length = this.length;
// FunctionProxies might provide a non-UInt32 value. If so, ignore it.
if ((typeof old_length === "number") &&
((old_length >>> 0) === old_length)) {
var argc = %_ArgumentsLength();
if (argc > 0) argc--; // Don't count the thisArg as parameter. new_length = old_length - argc; if (new_length < 0) new_length = 0; } } // This runtime function finds any remaining arguments on the stack, // so we don't pass the arguments object.
var result = %FunctionBindArguments(boundFunction, this,
this_arg, new_length);
// We already have caller and arguments properties on functions,
// which are non-configurable. It therefore makes no sence to
// try to redefine these as defined by the spec. The spec says
// that bind should make these throw a TypeError if get or set
// is called and make them non-enumerable and non-configurable.
// To be consistent with our normal functions we leave this as it is.
// TODO(lrn): Do set these to be thrower.
return result;
複製代碼
咱們在 bind 的實現方法裏面能夠看到一些額外的開銷,如'%_IsConstructCall()',這些額外的東西是爲了實現 bind 的規範,這也形成了在大多數狀況下 bind 比簡單的閉包慢的狀況。 另外一方面,bind 方法也有稍微的不一樣,使用 Function.prototype.bind 創造的函數沒有原型屬性或者是 [[Code]], [[FormalParameters]], 和 [[Scope]] 內部屬性。
總之,在 bind 和 closure 不成爲性能瓶頸的時候,優先考慮可讀性,儘可能保證代碼的簡潔
若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝!