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 不成为性能瓶颈的时候,优先考虑可读性,尽可能保证代码的简洁
若是有错误或者不严谨的地方,请务必给予指正,十分感谢!