title | tags | created | modified | ||
---|---|---|---|---|---|
dev-log-faq-lang-js |
|
2020-11-07 11:48:30 UTC |
2021-03-29 19:18:55 UTC |
- 对于index.js中的
export * from './A.js
;export * from './B.js
,如果A.js和B.js中都有export default
,那最后index.js中有导出default吗
- decorator提案已有三版草案,尤其第三版是对前两版的推倒重来
- 前两版是类似于python decorator的语义,第三版是静态语义,类似于弱化的宏
- 但是这三版都无法推进到 stage 3(主要的障碍来自于引擎厂商)
- 现在TypeScript所实现的 decorator,基于第一版的草案
- 现在TS团队拒绝投入精力到与 decorator 相关的任何改进
- Vue 3放弃了class component而转向 composition API,也有部分原因源于 decorator 前景不明
- TypeScript装饰器(Decorators)具体做了什么工作)
- Angular使用的根本不是装饰器(Decorator),而是注解(Annotation)
- 装饰器的定位是通过对应的装饰函数,修改内容本身的定义,从而实现不同的行为。
- 而注解并不产生任何行为,仅仅添加附加内容,需要相应的Scanner读取并识别其中的内容,从而使得Scanner自身产生不同的行为。
- Angular是通过装饰器来模拟了注解的功能
What is a simple explanation of Decorators in JavaScript and how useful is it in functional programming - Stack Overflow
- testing becomes super easy because you have to test individual functions and while coding in react, it saves a lot of your time and energy.
- I use recompose all the time and don't even use the class keyword.
- You have to take care that all the functions are pure and the state is immutable.
-
class-pros
- 通过原型链共享方法定义,更节省内存
-
class-cons
- private属性仍可被访问
- mixin缺少最佳实践,一般用来实现多重继承,可复用多个方法
-
factory-pros
- hoc方便复用,跨框架复用
- 纯函数容易test
- 闭包中的游离变量无法访问,更安全
-
factory-cons
- hoc函数导致更多的方法创建,更占用内存
-
实例属性f不在原型对象上
- 无法继承、无法使用super.f
- 内存消耗更大
-
You should avoid using arrow functions in class as they won't be the part of prototype and thus not shared by every instance.
- It is same as giving the same copy of function to every instance.
-
Arrow Functions in Class Properties Might Not Be As Great As We Think_201711
-
conclusion
- 👉🏻 The initialization of arrow functions in class properties are transpiled into the constructor. 构造函数中的逻辑每个实例对象初始化时都会执行一遍
- Arrow functions in class properties won’t be in the prototype and we can’t call them with
super
. - Arrow functions in class properties are much slower than bound functions, and both are much slower than usual function.
- You should only bind with
.bind()
or arrow function a method if you’re going to pass it around.
-
class A中值为箭头函数的实例属性
- 没有定义在A.prototype上
- 子类会继承该属性,child.handleClick()会正常执行,但super.handleClick会抛出异常
- If class
C
inherit of classA
, but implementhandleClick
as a function instead of an arrow function,handleClick
will only executessuper.handleClick()
and nothing else. Strange isn’t?
class A {
static color = "red";
counter = 0;
handleClick = () => {
this.counter++;
console.log(';; click-A ', this.counter);
}
handleLongClick() {
this.counter++;
}
}
//#region babel-class
class A {
constructor() {
_defineProperty(this, "counter", 0);
_defineProperty(this, "handleClick", () => {
this.counter++;
});
}
handleLongClick() {
this.counter++;
}
}
_defineProperty(A, "color", "red");
//#endregion babel
typeof A // function
A.prototype // 👉🏻 没有handleClick方法
class C extends A {
handleClick() {
super.handleClick();
console.log(";; click-C");
}
}
C.prototype.__proto__ === A.prototype // true
new C().handleClick() // ;; click-A 没有click-C
-
We know that usual functions are defined in the prototype and will be shared across all instances.
- As we’re calling the same method multiple times across the prototype, the JavaScript engine can optimize it.
-
for the arrow functions in class properties, if we’re creating N components, these N components will also create N functions.
- Remember what we’ve seen in the transpiled version, class properties are initialized in the constructor.
- Which means if we click on N components, N different functions will be called.
-
👉🏻 In short, to improve performance, you should declare your shared method in the prototype and only bound it to the context if you need to (if you pass it as prop or callback).
- It makes sense to bound our shared methods to the prototype and initialized our properties in the constructor of each instance, but methods not much.
- arrow functions in class properties are not as performant as we thought.
-
Our savior will be the autobind-decorator, unfortunately it’s only available with babel as it’s still a proposal at stage 2. /inactive
- The distinction between .js and .jsx files was useful before Babel, but it’s not that useful anymore.
- There are other syntax extensions (e.g. Flow). What would you call a JS file that uses Flow? .flow.js? What about JSX file that uses Flow? .flow.jsx? What about some other experimental syntax? .flow.stage-1.jsx?
- Most editors are configurable so you can tell them to use a JSX-capable syntax scheme for .js files. Since JSX (or Flow) are strict supersets of JS, I don’t see this as an issue.
- It's a bit concerning that many devs insist on putting JSX inside .js files "just because it's common practice"
- https://twitter.com/youyuxi/status/1362049928139321348
- The reason Vite requires .jsx extension for JSX processing is because in most cases plain .js files shouldn't need full AST transforms to work in the browser. Allowing JSX in .js files means every served file must be full-AST-processed just in case it contains JSX.
- Try .tsx
- ref
- tips
- Don't use this
- You actually don't want to access
this
in particular, but the object it refers to. - That's why an easy solution is to simply create a new variable that also refers to that object.
- The variable can have any name, but common ones are
self
andthat
.
- You actually don't want to access
- Explicitly set this of the callback
- use
bind
- use
- Don't use this
this
is a property of an execution context (global, function or eval) that,- in non–strict mode, is always a reference to an object
- and in strict mode can be any value.
- A function's
this
keyword behaves a little differently in JavaScript compared to other languages.- It also has some differences between strict mode and non-strict mode.
- In most cases, the value of
this
is determined by how a function is called (runtime binding). - It can't be set by assignment during execution, and it may be different each time the function is called.
- ES5 introduced the
bind()
method to set the value of a function's this regardless of how it's called,- and ES2015 introduced arrow functions which don't provide their own
this
binding - (it retains the
this
value of the enclosing lexical context).
- and ES2015 introduced arrow functions which don't provide their own
- In the global execution context (outside of any function),
this
refers to the global object(window
) whether in strict mode or not.- You can always easily get the global object using the global
globalThis
property, regardless of the current context in which your code is running.
- You can always easily get the global object using the global
- Inside a function, the value of
this
depends on how the function is called. - Since the following code is not in strict mode, and because the value of
this
is not set by the call,this
will default to the global object, which iswindow
in a browser
function f1() {
return this;
}
// In a browser:
f1() === window; // true
// In Node:
f1() === globalThis; // true
- In strict mode, however, if the value of
this
is not set when entering an execution context, it remains asundefined
- To set the value of
this
to a particular value when calling a function, usecall()
, orapply()
.- The first parameter is the object to use as 'this', subsequent parameters are passed as arguments in the function call
- Note that in non–strict mode, with call and apply, if the value passed as this is not an object, an attempt will be made to convert it to an object.
- Values null and undefined become the global object.
- Primitives like 7 or 'foo' will be converted to an Object using the related constructor, so the primitive number 7 is converted to an object as if by new Number(7) and the string 'foo' to an object as if by new String('foo')
- The behavior of
this
in classes and functions is similar, since classes are functions under the hood. - Within a class constructor,
this
is a regular object.- All non-static methods within the class are added to the prototype of
this
- Static methods are not properties of
this
.- They are properties of the class itself.
- All non-static methods within the class are added to the prototype of
- For derived classes, derived constructors have no initial
this
binding. - Calling
super()
creates athis
binding within the constructor, like evaluatingthis = new Base();
- Referring to this before calling super() will throw an error.
- Derived classes must not return before calling super(), unless they return an Object or have no constructor at all.
- Calling
f.bind(thisArg,arg1,arg2)
creates a new function with the same body and scope asf
,- but where
this
occurs in the original function, - in the new function it is permanently bound to the first argument of
bind
, regardless of how the function is being used. - The
bind()
function creates a new bound function, which is an exotic(奇异的) function object (a term from ECMAScript 2015) that wraps the original function object.- Calling the bound function generally results in the execution of its wrapped function.
- but where
function f() {
return this.a;
}
var g = f.bind({ a: 'azerty' });
console.log(g()); // azerty
var h = g.bind({ a: 'yoo' }); // bind only works once!
console.log(h()); // azerty
var o = { a: 37, f: f, g: g, h: h };
console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty
- In arrow functions,
this
retains the value of the enclosing lexical context'sthis
.- In global code, it will be set to the global object
- If
this
arg is passed tocall/apply
, orbind
on invocation of an arrow function, it will be ignored.- You can still prepend arguments to the call, but the first argument (thisArg) should be set to
null
- You can still prepend arguments to the call, but the first argument (thisArg) should be set to
- When a function is called as a method of an object, its
this
is set to the object the method is called on.- In the following example, when
o.f()
is invoked, inside the functionthis
is bound to theo
object. - Note that this behavior is not at all affected by how or where the function was defined.
- This demonstrates that it matters only that the function was invoked from the
f
method member of objecto
. - the
this
binding is only affected by the most immediate member reference
- In the following example, when
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // 37
function independent() {
return this.prop;
}
o.f1 = independent;
console.log(o.f1()); // 37
o.b = { g: independent, prop: 42 };
console.log(o.b.g()); // 42
- If the method is on an object's prototype chain,
this
refers to the object the method was called on, as if the method were on the object.
var o = { f: function() { return this.a + this.b; } };
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
- A function used as getter or setter has its
this
bound to the object from which the property is being set or gotten.
function sum() {
return this.a + this.b + this.c;
}
var o = {
a: 1,
b: 2,
c: 3,
get average() {
return (this.a + this.b + this.c) / 3;
}
};
Object.defineProperty(o, 'sum', {
get: sum,
enumerable: true,
configurable: true
});
console.log(o.average, o.sum); // 2, 6
- When a function is used as a constructor (with the
new
keyword), itsthis
is bound to the new object being constructed.- While the default for a constructor is to return the object referenced by
this
, it can instead return some other object - if the return value isn't an object, then the
this
object is returned - If the function has a return statement that returns some other object, that object will be the result of the
new
expression.
- While the default for a constructor is to return the object referenced by
function C2() {
this.a = 37;
return { a: 38 };
}
o = new C2();
console.log(o.a); // 38
- When a function is used as an event handler, its
this
is set to the element on which the listener is placed- (some browsers do not follow this convention for listeners added dynamically with methods other than addEventListener()).
- When the code is called from an inline
on-event
handler, its this is set to the DOM element on which the listener is placed- Note however that only the outer code has its
this
set this way - the inner function's
this
isn't set so it returns the global/window object (i.e. the default object in non–strict mode wherethis
isn't set by the call).
- Note however that only the outer code has its
<button onclick="alert((function() { return this; })());">
Show inner this
</button>
- this in classes
- Just like with regular functions, the value of
this
within methods depends on how they are called. - Sometimes it is useful to override this behavior so that
this
within classes always refers to the class instance. - To achieve this,
bind
the class methods in the constructor - Classes are always strict mode code. Calling methods with an undefined
this
will throw an error.
- Just like with regular functions, the value of
class Car {
constructor() {
this.sayBye = this.sayBye.bind(this);
}
sayHi() {
console.log(`Hello from ${this.name}`);
}
sayBye() {
console.log(`Bye from ${this.name}`);
}
get name() {
return 'Ferrari';
}
}
class Bird {
get name() {
return 'Tweety';
}
}
const car = new Car();
const bird = new Bird();
// The value of 'this' in methods depends on their caller
car.sayHi(); // Hello from Ferrari
bird.sayHi = car.sayHi;
bird.sayHi(); // Hello from Tweety
// For bound methods, 'this' doesn't depend on the caller
bird.sayBye = car.sayBye;
bird.sayBye(); // Bye from Ferrari
- ref
- 案例分析可参考笔记 job-js-prototype-class.md
Ctr.prototype
显式原型: explicit prototype property- 每个函数在创建之后都会拥有一个名为
prototype
的属性,这个属性指向函数的原型对象。 - 通过
Function.prototype.bind
方法构造出来的函数是个例外,它没有prototype属性 - 显式原型的作用:用来实现基于原型的继承与属性共享
- ECMAScript does not use classes such as those in C++, or Java.
- Instead objects may be created in various ways including via a literal notation or via constructors which create objects
- and then execute code that initialises all or part of them by assigning initial values to their properties.
- Each constructor is a function that has a property named
prototype
that is used to implement prototype-based inheritance and shared properties.- Objects are created by using constructors in
new
expressions; for example,new Date(2009,11)
creates a new Date object. ----ECMAScript Language Specification
- Objects are created by using constructors in
- 每个函数在创建之后都会拥有一个名为
obj.__proto__
隐式原型: implicit prototype link- js中任意对象都有一个内置属性
[[Prototype]]
,在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__
来访问。 - 对象的隐式原型指向创建这个对象的构造函数(constructor)的prototype
obj.__proto__ === obj.constructor.prototype
// true- ES5中有了对于这个内置属性标准的Get方法
Object.getPrototypeOf()
. - 隐式原型的作用:构成原型链查找属性,同样用于实现基于原型的继承
- 举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着__proto__依次查找。
- Every object created by a constructor has an implicit reference (called the object’s
prototype
) to the value of its constructor’sprototype
----ECMAScript Language Specification
- js中任意对象都有一个内置属性
- The
__proto__
property is an accessor property (a getter function and a setter function) that exposes the internal [[Prototype]] (either an object or null) of the object through which it is accessed. - The
__proto__
getter function exposes the value of the internal [[Prototype]] of an object.- For objects created using an object literal, this value is
Object.prototype
. - For objects created using array literals, this value is
Array.prototype
. - For functions, this value is
Function.prototype
. - For objects created using
new fun
, wherefun
is one of the built-in constructor functions provided by JavaScript (Array, Boolean, Date, Number, Object, String, and so on — including new constructors added as JavaScript evolves), this value is alwaysfun.prototype
. Object.prototype
这个对象是个例外,Object.prototype.__proto__ === null
// trueArray/Function.prototype.__proto__ === Object.prototype
// true
- For objects created using an object literal, this value is
- The
__proto__
setter allows the [[Prototype]] of an object to be mutated.- Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine
- The effects on the performance of altering inheritance are subtle(不易察觉的,不明显的) and far-flung(广泛的), and are not limited to simply the time spent in
obj.__proto__ = ...
statements, but may extend to any code that has access to any object whose [[Prototype]] has been altered. - If you care about performance you should avoid setting the [[Prototype]] of an object.
- Instead, create a new object with the desired [[Prototype]] using
Object.create()
.
__proto__
的指向- 根据ECMA定义 'to the value of its constructor’s "prototype" ' ----指向创建这个对象的函数(构造函数)的显式原型
- JS中对象被创建的方式,一眼看过去似乎有三种方式:(1)对象字面量
{}
的方式 (2)new
的方式 (3)ES5中的Object.create()
- 但是我认为本质上只有一种方式,也就是通过
new
来创建。 - 首先字面量的方式是一种为了开发人员更方便创建对象的一个语法糖,本质就是
var o = new Object(); o.xx = xx; o.yy=yy;
- 再来看
Object.create()
, 这是ES5中新增的方法,在这之前这被称为原型式继承,- 从实现代码 return new F() 中我们可以看到,这依然是通过new来创建的。
- 不同之处在于由 Object.create() 创建出来的对象没有构造函数,其实这里说它没有构造函数是指在 Object.create() 函数外部我们不能访问到它的构造函数,然而在函数内部实现中是有的
- 因此由Object.create(o)创建出来的对象它的隐式原型指向o
// 模拟object.create(o)
function object(o) {
function F() {}
F.prototype = o;
return new F()
}
//以下是用于验证的伪代码,f就是object.create(o)创建出的对象
var f = new F();
//于是有
f.__proto__ === F.prototype //true
//又因为
F.prototype === o; //true
//所以
f.__proto__ === o;
Array.prototype
也是一个对象,对象就是由Object()
这个构造函数创建的,因此Array.prototype.__proto__ === Object.prototype
为true,- 或者也可以这么理解,所有的内建对象都是由Object()创建而来。
Foo.prototype.__proto__ === Object.prototype
//true 理由同上
- 如果构造函数带返回值且为对象,则该对象直接赋给变量,那么当然
__proto__
属性指向该对象的prototype咯。- 但一般来说,当函数作为构造器使用,不应该带返回值。
instanceof
原理- instanceof的左值一般是一个对象,右值一般是一个构造函数,用来判断左值是否是右值的实例
- 它的内部实现原理是这样
- 也就是沿着L的__proto__一直寻找到原型链末端,直到等于R.prototype为止
//设 L instanceof R
//通过判断
L.__proto__.__proto__..... === R.prototype?
//最终返回true or false
Function instanceof Object // true
Object instanceof Function // true
Function instanceof Function //true
Object instanceof Object // true
Number instanceof Number //false
- 一张图搞定JS原型&原型链
- js中对象和函数的关系,函数其实是对象的一种。
- 函数、构造函数的区别,任何函数都可以作为构造函数,但是并不能将任意函数叫做构造函数,只有当一个函数通过new关键字调用的时候才可以成为构造函数。
- 1.
__proto__
、 constructor属性是对象所独有的; - 2.prototype属性是函数独有的;
-
- 上面说过js中函数也是对象的一种,那么函数同样也有属性
__proto__
、 constructor;
- 上面说过js中函数也是对象的一种,那么函数同样也有属性
- prototype设计之初就是为了实现继承,让由特定函数创建的所有实例共享属性和方法,也可以说是让某一个构造函数实例化的所有对象可以找到公共的方法和属性。
- Parent.prototype上添加的属性和方法叫做原型属性和原型方法,该构造函数的实例都可以访问调用。那这个构造函数的原型对象上的属性和方法,怎么能和构造函数的实例联系在一起呢,就是通过__proto__属性。
- __proto__通常称为隐式原型,prototype通常称为显式原型,那我们可以说一个对象的隐式原型指向了该对象的构造函数的显式原型。
- 通过输出看到Parent函数的构造函数是Function(),这点也不奇怪,因为我们每次定义函数其实都是调用了new Function(),下面两种效果是一样的。
var fn1 = new Function('msg', 'alert(msg)');
function fn1(msg) {
alert(msg);
}
-
constructor属性的作用是从一个对象指向一个函数,这个函数就是该对象的构造函数。
Function.constructor === Function
// true- Function是所有函数的根构造函数。
f.prototype.constructor === f
// true
-
misc
- __proto__是每个对象都有的一个属性,而prototype是函数才会有的属性。
-
ref
- Since the class and extends keywords are only syntactic sugar on top of prototypal inheritance,
- the answer simply is: Yes, you can replace
util.inherits
byextends
and keep the same behavior.
- the answer simply is: Yes, you can replace
- Of course, there are minor things to watch out for,
- e.g. you need to make sure to call the
super
constructor in your derived class's constructor, - whereas with
util.inherits
you had to call the constructor function andapply
it tothis
. - But effectively, these things are only other syntactic constructs, semantically, they are equivalent.
- e.g. you need to make sure to call the
- classes feel kind of loose for an library API.
- Functions give better defaults and stricter types for methods, feels more on rails.
- But after spending the day making a factory pattern work here… I think we might be heading back to classes.
- ref
- es6的class提供了一个class实现的标准,之前无标准而较乱
- Opinionated take:
- Class inheritance, getters and setters makes your modules overly complex and hard to debug.
- Should have complicated syntax to discourage use, ES6 made it too easy.
- ref
- Why use classes instead of functions?
- 5 reasons not to use ES6 classes in React
- Inconsistent context
- No mixin support
- Inconsistent state handling
- Inconsistent definition
- Dealing with super
- Not Awesome: ES6 Classes
- Instead of ES6 classes, you should consider factory functions, object composition, and/or prototypal inheritance via the use of prototypes, object literals, Object.create(), Object.assign(), etc. while avoiding constructors and the
new
keyword altogether.
- Instead of ES6 classes, you should consider factory functions, object composition, and/or prototypal inheritance via the use of prototypes, object literals, Object.create(), Object.assign(), etc. while avoiding constructors and the
- Because some built-in functions are just defined to act this way.
- For example see ES5 15.2.1.1 for
Object
:- When the
Object
function is called with no arguments or with one argument value, the following steps are taken:- If value is
null/undefined
or not supplied, create and return a new Object object exactly as if the standard built-in Object constructor had been called with the same arguments (15.2.2.1). - Return
ToObject(value)
.
- If value is
- They test whether they have been called with
new
or not and if not act like they'd have been called withnew
. - You can implement this yourself:
Foo()
andnew Foo()
will act the same way
- When the
function Foo() {
if (!(this instanceof Foo)) {
return new Foo();
}
// do other init stuff
}
- Since your example is an Object type of built-in function, as it is answered above it is the same for this type, it does not work the same way for most of the other built-in functions such as
Number()
.- You should be very careful when invoking them with the
new
keyword or not. - Because by default the
new
keyword with a function constructor returns an object, not a primitive type directly. - So you can not, for example, check strict equality on two variables that one of them is declared and assigned using
new Number()
, and the other is withNumber()
- You should be very careful when invoking them with the
var num1 = Number(26);
var num2 = new Number(26);
num1 == num2; // returns true
num1 === num2; // returns false
- The
Object()
constructor behaves identically with and without thenew
keyword. That's just how it works.- Note that
Array()
andnew Array()
work the same way too - Doing some more testing I see that behavior happens with all factory constructors (String, Date, RegExp, etc.)
- Every factory constructor has its own behavior when it's called as a function.
- For instance, the
Date
constructor returns the date as a string (and not aDate
object) when called as a function.
- For instance, the
- Precisely. The only way to know what happens is to read the spec (or a derivative work that tries to be more accessible :-).
- And again, most constructors in the wild (not part of the spec) probably don't implement specific non-construction behaviors (unless they say they do!).
- Note that
- ref
- Here's a pattern I've come across that really helps me.
- It doesn't use a
class
, but it doesn't require the use ofnew
either.
const Foo = x => ({
x,
hello: () => `hello ${x}`,
increment: () => Foo(x + 1),
add: ({ x: y }) => Foo(x + y)
})
console.log(Foo(1).x) // 1
console.log(Foo(1).hello()) // hello 1
console.log(Foo(1).increment().hello()) // hello 2
console.log(Foo(1).add(Foo(2)).hello()) // hello 3
- This deserves points.
- I really wonder whether adding
class
to JS was an improvement. This shows what JS code should look like. - For people wondering why there is no
this
anywhere, the created object is just using thex
that was passed in to the 'constructor' (arrow function). - Whenever it needs to be mutated, it returns a new object. The objects are immutable.
- I really wonder whether adding
- The problem with technique is that each time
Foo
is invoked, it has to create all methods again.- With classes, the
prototype
methods are efficiently shared between instances without having to re-create then per instance. - Because the methods are re-created, you use up more memory as well.
- For production purposes, it is better to use something similar to the answer by Tim and use a method to create a new class.
- With classes, the
- my answer is similar to the one here except it doesn't create new functions each time.
const assoc = (prop, value, obj) =>
Object.assign({}, obj, {
[prop]: value
})
const reducer = ($values, accumulate, [key, val]) => assoc(key, val.bind(undefined, ...$values), accumulate)
const bindValuesToMethods = ($methods, ...$values) =>
Object.entries($methods).reduce(reducer.bind(undefined, ...$values), {})
const prepareInstance = (instanceMethods, staticMethods = ({})) => Object.assign(
bindValuesToMethods.bind(undefined, instanceMethods),
staticMethods
)
// Let's make our class-like function
const Right = prepareInstance(RightInstanceMethods, RightStaticMethods)
const RightInstanceMethods = ({
chain: (x, f) => f(x),
map: (x, f) => Right(f(x)),
fold: (x, l, r) => r(x),
inspect: (x) => `Right(${x})`
})
const RightStaticMethods = ({
of: x => Right(x)
})
- Binding issues.
- As class constructor functions deal closely with
this
keyword, - it can introduce potential binding issues, especially if you try to pass your class method as a callback to an external routine (hello, React devs)
- As class constructor functions deal closely with
- Performance issues.
- Because of classes' implementation, they are notoriously difficult to optimize at runtime.
- Private variables.
- One of the great advantages and the main reasons for classes in the first place, private variables, is just non-existent in JS.
- Strict hierarchies.
- Classes introduce a straight top-to-bottom order and make changes harder to implement, which is unacceptable in most JS applications.
- Because the React team tells you not to.
- While they did not explicitly deprecate the class-based components yet, they are likely to in the near future.
- One of the design constraints and motivations for hooks was to represent a component being multiple states concurrently. That's something classes cannot express properly. 使用class组件对并发操作状态不友好
- 想象一下这种场景,一个组件的render函数结构基本一致,但是它的数据层states可能会根据环境条件有不同的变化逻辑(相似的state结构,但是计算state的逻辑不同)
- 这种情况下用class写就免不了大量的if else,
- 但是hooks就可以把每种条件下的state变化逻辑抽离出来,
- hooks比class组件更优雅的实现了states的多态
- Using a standard function+object+prototype chain "classes" is a good way to learn how JS works, but hiding all that behind the class sugar is too much abstraction to me.
- Main benefit I get out of ES6 classes is defining the shape of data I am dealing with, so I can talk and think about it in precise terms.
- When I say "Customer", I know exactly what fields and properties that entails (and IDE will help me remember it too).
- Member functions and inheritance are less useful for the reasons you stated in your article.
- Any kind of business code I prefer to keep in pure functions or service objects, that are given class based objects to manipulate.
- Code and data kept mostly separate.
- Before es6 class, I worked with projects that had different class implementations in them, all with their own ups and downs. Now everyone tends to use the ES2015 version and things are much clearer.
- proposed private field in js may not be better than typescript class.
- ref
- namedFunc pros
- 调试时的可读性更好
- funcExpression难以递归调用自身(arguments.callee无法在严格模式),具名可以
- auto hoist
- no implicit returns
- usecase
- I prefer function because python and java both use it and I don’t get confused.
- 用在class中时常需要bind(this)
export default function Button() {}
vsconst Button = () => {}; export default Button;
前者更方便export
- funcExpression pros
- 更好的scoped,namedFunc总是提升,而箭头函数是先声明再使用
- 方便动态修改调用的函数
- usecase
- Arrow functions are especially meant to be placed inline, anonymously. 函数能写为一行时常用
- 箭头函数常用作事件监听器函数
- 箭头函数内部没有自己的局部this
- Availability (scope) of the function,函数或变量会自动提升hoist
- one difference that concerns me is when the machine creates the function object. Which in the case of declarations is before any statement is executed but after a statement body is invoked (be that the global code body or a sub-function's), and in the case of expressions is when the statement it is in gets executed.
(function(){}).name === ""
,匿名函数表达式会返回true- Both have a name property
- In Google's V8 and Firefox's Spidermonkey there might be a few microsecond JIST compilation difference, but ultimately the result is the exact same.
- If we declare the variable as
function funcName(){}
, then the immutability of the variable is the same as declaring it with var。- function expression can be const
- function declaration name can be reassigned, too
- Function Declarations are only allowed to appear in Program or FunctionBody. Syntactically, they can not appear in Block (
{ ... }
) — such as that of if, while or for statements. This is because Blocks can only contain Statements, not SourceElements, which Function Declaration is.- The only way Expression is allowed directly within Block is when it is part of ExpressionStatement.
- However, ExpressionStatement is explicitly defined to not begin with "function" keyword, and this is exactly why Function Declaration cannot appear directly within a Statement or Block (note that Block is merely a list of Statements).
Is there a reason that we've started to use the function key word for function components in the React community? function Button() {}
vs. const Button = () => {}
- I went back to using
function
keywords again because of hooks.- It's easier to add hooks without the implicit return.
- Now I follow a pattern: if it's top level, I use
function
keyword. Otherwise, arrow functions. - And, of course, callbacks are always arrow functions.
- Now, I mostly
export function Name
for both named exports and better stack traces.- because the airbnb style guide says so
- If I'm gonna return straight away, I'll use the const syntax and make use of the implicit return, if not I'll use the function syntax. Most of the time I don't even think about it but that's as much logic as I can come up.
- IIRC there was a time when the
.name
prop mattered (for React dev tools maybe?) and function decl’s have that implicitly while arrow func don’t. I remember HoC code would set displayName (the fallback for .name) manually, so there’s some value to that I guess. - React docs mostly I think. And for me I just decided that the arrow functions weren’t really adding much there. The declaration syntax is more consistent, no implicit returns, always using parens for params. Refactoring is easier, don’t need to change much to add a console log
- ref
- Object is similar to Map
- both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key.
- For this reason (and because there were no built-in alternatives), Object has been used as Map historically.
- However, there are important differences that make Map preferable in certain cases:
- A Map does not contain any keys by default. It only contains what is explicitly put into it.
- An Object has a prototype, so it contains default keys that could collide with your own keys if you're not careful.
- As of ES5, this can be bypassed by using
Object.create(null)
, but this is seldom done.
- A Map's keys can be any value (including functions, objects, or any primitive).
- The keys of an Object must be either a String or a Symbol.
- The keys in Map are ordered. Thus, when iterating over it, a Map object returns keys in order of insertion.
- The keys of an Object are not ordered.
- Since ECMAScript 2015, objects do preserve creation order for string and Symbol keys.
- In JavaScript engines that comply with the ECMAScript 2015 spec, iterating over an object with only string keys will yield the keys in order of insertion.
- The number of items in a Map is easily retrieved from its
size
property.
- The number of items in an Object must be determined manually.
- A Map is an iterable, so it can be directly iterated.
- Iterating over an Object requires obtaining its keys in some fashion and iterating over them.
- Performs better in scenarios involving frequent additions and removals of key-value pairs.
- Object is NOT optimized for frequent additions and removals of key-value pairs.
- ref
- Both
for...in
andfor...of
statements iterate over something.- The main difference between them is in what they iterate over.
- The
for...in
statement iterates over the enumerable properties of an object, in an arbitrary order.- for-in遍历集合的属性,在遍历数组arr时会打印0, 1, 2, prop
- The
for...of
statement iterates over values that the iterable object defines to be iterated over.- for-of遍历集合的值,在遍历数组arr时会打印arr[0], arr[1], ...
- tips
- Don't use for..in for Array iteration
- ref