javascript學習筆記
Table of Contents
隨筆亂記
陸續增加中
The function of javascript is just an Object so it can has properties or functions in it.
1 Javascript built-in types
string
number
boolean
null
andundefined
object
symbol
(new to ES6)
2 Hoisting
可以想像成所有宣告時機(var a)都被提升到function最開始,更精確的說是scope的最開始
實際上是javascript interpreter將var a = 2分成兩個步驟
declaration(var a),assignment(a = 2)
所有declaration在compile time完成,assignment在runtime執行
須注意的是ES6新增的block scope variable =let & const=,沒有hoisting機制
當然eval("var a = 2;");這種用法也會破壞hoisting,不過為了效能本來就該避免eval這東西了
var a = 2; |
3 Closure
Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.
foo()執行完後,local variable a沒被回收的原因是因為baz -> bar還擁有reference to foo,所以a不會被GC回收
function foo() { |
some useful snippet
function wait(message) { |
4 Immediately Invoked Function Expressions (IIFEs)
(function IIFE(){ |
5 Modules
var foo = (function CoolModule(id) { |
5.1 Modern Modules
module dependency loaders/managers
var MyModules = (function Manager() { |
use above
MyModules.define( "bar", [], function(){ |
5.2 Future Modules
ES6新語法,支援File based Modules
bar.js
function hello(who) { |
foo.js
// import only `hello()` from the "bar" module |
use above
// import the entire "foo" and "bar" modules |
6 This
javascript的this與一般OO的this很不一樣,他是runtime時才決定指向哪裡的
詳細決定規則請見以下
6.1 Default Binding
當不滿足以下三種狀況時,this會自動指向global,strict mode下則是undefined
6.2 Implicit Binding
this指向obj2
function foo() { |
在callback下,容易發生implicitly lost
function foo() { |
6.3 Explicit Binding
可利用call、apply來強制綁定this物件,解決上述的implicitly lost
function foo(something) { |
ES5內建bind方法可簡單達成上述
function foo(something) { |
6.4 New Binding
function foo(a) { |
6.5 So How To Determine This
- Is the function called with
new
(new binding)? If so,this
is the newly constructed object.
var bar = new foo()
- Is the function called with
call
orapply
(explicit binding), even hidden inside abind
hard binding? If so,this
is the explicitly specified object.
var bar = foo.call( obj2 )
- Is the function called with a context (implicit binding), otherwise known as an owning or containing object? If so,
this
is that context object.
var bar = obj1.foo()
- Otherwise, default the
this
(default binding). If instrict mode
, pickundefined
, otherwise pick theglobal
object.
var bar = foo()
6.6 Safer this
如果你發現你需要apply或bind的parameter spread、currying功能
但卻不care this到底是什麼,你可能會想用apply(null, [2, 3])
這可能導致this指向global(default binding)
底下是一種比較好的方式,create一個empty object來取代null
function foo(a,b) { |
6.7 arrow function
ES6新增的arrow function,有著跟一般function不一樣的this行為
其this永遠指向包圍它的function的this,使其在callback中很有用
function foo() { |
然而在不用arrow function時就已經有好解法了:
function foo() { |
當然你也能用bind來處理callback的this,這三種方法就是程式風格的不同而已,儘量統一就好
7 Object
7.1 Computed Property Names
ES6 adds computed property names, where you can specify an expression, surrounded by a [ ] pair, in the key-name position of an object-literal declaration:
var prefix = "foo"; |
常配合ES6新增的Symbol使用
var myObject = { |
7.2 Duplicating Objects
Copy Object分成shallow copy and deep copy,其中deep copy可能會出現某些問題
例如以下例子,deep copy將造成infinite circular duplication
function anotherFunction() { /*..*/ } |
若欲複製的object是JSON-safe (that is, can be serialized to a JSON string and then re-parsed to an object with the same structure and values),我們可以用以下方式做到deep copy
var newObj = JSON.parse( JSON.stringify( someObj ) ); |
shallow copy比較沒有爭議,所以ES6提供以下方式:
var newObj = Object.assign( {}, myObject ); |
7.3 Property Descriptors
ES5以後才有的功能
- writable(可否modify)
- enumerable(可否用for…in遍歷)
- configurable(可否用difineProperty重新定義 and 可否用delete刪除)
Get Property Descriptors
var myObject = { |
Modify Property Descriptors
var myObject = {}; |
若是在strict mode,上述會扔出TypeError
7.4 Immutability
以下方式均只能做到shallow immutability. That is, they affect only the object and its direct property characteristics. If an object has a reference to another object (array, object, function, etc), the contents of that object are not affected, and remain mutable.
7.4.1 Object Constant
Use writable:false and configurable:false to construct Constant Property
var myObject = {}; |
7.4.2 Prevent Extensions
var myObject = { |
7.4.3 Seal
Object.seal(..) creates a "sealed" object, which means it takes an existing object and essentially calls Object.preventExtensions(..) on it, but also marks all its existing properties as configurable:false.
7.4.4 Freeze
Object.freeze(..) creates a frozen object, which means it takes an existing object and essentially calls Object.seal(..) on it, but it also marks all "data accessor" properties as writable:false, so that their values cannot be changed.
若需要deep freeze,可自行recursive Object.freeze()
7.5 Getters & Setters
You will almost certainly want to always declare both getter and setter (having only one or the other often leads to unexpected/surprising behavior)
var myObject = { |
7.6 Existence
如何check object是否有某個property
var myObject = { |
in與hasOwnProperty的最大不同處在in會檢查prototype chain而hasOwnProperty不會,需小心的是若object沒有 delegation to Object.prototype,他將不會擁有hasOwnProperty,此時可以使用Object.prototype.hasOwnProperty.call(myObject,"a")來檢查
另外in看起來好像可以這樣用:4 in [2, 4, 6],但實際上它檢查的是object的property,所以此用法是錯誤的,in不可用來檢查containers是否存在某值
7.7 Enumeration
var myObject = { }; |
Note: for…in將會travel all values and properties(include prototype chain),所以建議遍歷object使用for…in loop,遍歷array的話使用一般for loop
var myObject = { }; |
目前沒有built-in方法可做到list all keys in object include prototype chain,需要自行recursive Object.keys()
7.8 Iteration
for…in loop拿出來的東西會是object的properties,但若我們想直接拿出value呢?
ES6新增的for…of loop可辦到
var myArray = [ 1, 2, 3 ]; |
for…of僅能用在有定義@@iterator的object上,array預設就有,但其他object預設是沒有的
若想用for…of的話還是可以自己定義@@iterator,滿足以下即可:
As long as your iterator returns the expected { value: .. } return values from next() calls, and a { done: true } after the iteration is complete, ES6's for..of can iterate over it.
var myObject = { |
以下是一個利用iterator的亂數產生器
var randoms = { |
8 Mixing (Up) "Class" Objects
JavaScript's object mechanism does not automatically perform copy behavior when you "inherit" or "instantiate". Plainly, there's no "classes" in JavaScript to instantiate, only objects. And objects don't get copied to other objects, they get linked together.
Since observed class behaviors in other languages imply copies, let's examine how JS developers fake the missing copy behavior of classes in JavaScript: mixins. We'll look at two types of "mixin": explicit and implicit.
8.1 Explicit Mixins
Such a utility is often called extend(..)
by many libraries/frameworks, but we will call it mixin(..)
here for illustrative purposes.
// vastly simplified `mixin(..)` example: |
But because of JavaScript's peculiarities, explicit pseudo-polymorphism (because of shadowing!) creates brittle manual/explicit linkage in every single function where you need such a (pseudo-)polymorphic reference. This can significantly increase the maintenance cost. Moreover, while explicit pseudo-polymorphism can emulate the behavior of "multiple inheritance", it only increases the complexity and brittleness.
The result of such approaches is usually more complex, harder-to-read, and harder-to-maintain code. Explicit pseudo-polymorphism should be avoided wherever possible, because the cost outweighs the benefit in most respects.
8.2 Implicit Mixins
var Something = { |
While this sort of technique seems to take useful advantage of this rebinding functionality, it is the brittle Something.cool.call( this ) call, which cannot be made into a relative (and thus more flexible) reference, that you should heed with caution. Generally, avoid such constructs where possible to keep cleaner and more maintainable code.
8.3 Conclusion
Classes are a design pattern. Many languages provide syntax which enables natural class-oriented software design. JS also has a similar syntax, but it behaves very differently from what you're used to with classes in those other languages.
Classes mean copies.
When traditional classes are instantiated, a copy of behavior from class to instance occurs. When classes are inherited, a copy of behavior from parent to child also occurs.
Polymorphism (having different functions at multiple levels of an inheritance chain with the same name) may seem like it implies a referential relative link from child back to parent, but it's still just a result of copy behavior.
JavaScript does not automatically create copies (as classes imply) between objects.
The mixin pattern (both explicit and implicit) is often used to sort of emulate class copy behavior, but this usually leads to ugly and brittle syntax like explicit pseudo-polymorphism (OtherObj.methodName.call(this, ...)
), which often results in harder to understand and maintain code.
Explicit mixins are also not exactly the same as class copy, since objects (and functions!) only have shared references duplicated, not the objects/functions duplicated themselves. Not paying attention to such nuance is the source of a variety of gotchas.
In general, faking classes in JS often sets more landmines for future coding than solving present real problems.
9 Prototype
JavaScript沒有Class(一般Class-Oriented Language的class),只有Object
Prototype的概念比較類似Delegate而不是Inherited(Copy)
Objects in JavaScript have an internal property, denoted in the specification as Prototype, which is simply a reference to another object. Almost all objects are given a non-null value for this property, at the time of their creation.
var anotherObject = { |
9.1 Setting & Shadowing Properties
- If a normal data accessor property named foo is found anywhere higher on the Prototype chain, and it's not marked as read-only (writable:false) then a new property called foo is added directly to myObject, resulting in a shadowed property.
- If a foo is found higher on the Prototype chain, but it's marked as read-only (writable:false), then both the setting of that existing property as well as the creation of the shadowed property on myObject are disallowed. If the code is running in strict mode, an error will be thrown. Otherwise, the setting of the property value will silently be ignored. Either way, no shadowing occurs.
- If a foo is found higher on the Prototype chain and it's a setter (see Chapter 3), then the setter will always be called. No foo will be added to (aka, shadowed on) myObject, nor will the foo setter be redefined.
var anotherObject = { |
Shadowing with methods leads to ugly explicit pseudo-polymorphism if you need to delegate between them. Usually, shadowing is more complicated and nuanced than it's worth, so you should try to avoid it if possible. Use Behavior Delegation for an alternative design pattern, which among other things discourages shadowing in favor of cleaner alternatives.
9.2 New(Constructor Call)
- a brand new object is created (aka, constructed) out of thin air
- the newly constructed object is Prototype-linked
- the newly constructed object is set as the this binding for that function call
- unless the function returns its own alternate object, the new-invoked function call will automatically return the newly constructed object.
JS沒有Class也沒有Constructor,new的概念跟一般Class-Oriented Language完全不同
New可以放在任何function前,此時被稱為Constructor Call
var a = new Foo()代表:
建立新object a,a的_proto_指向Foo.prototype,this綁定到該a上
function Foo() { |
利用prototype模擬Class instantiate Object
看起來很像Class-Oriented,但實際上是利用上述機制
function Foo(name) { |
9.3 constructor
function Foo() { |
看起來好像a擁有一個property指向Foo,實際上卻是a._proto__.constructor
此constructor指向Foo看似很有用實際上卻可被複寫,*因此別依賴這個特性*
function Foo() { /* .. */ } |
9.4 (Prototypal) Inheritance
function Foo(name) { |
Object.create(..) creates a "new" object out of thin air, and links that new object's internal Prototype to the object you specify (Foo.prototype in this case).
底下是兩個錯誤用法
// doesn't work like you want! |
ES6後可以用Object.setPrototypeOf()代替Object.create()
// pre-ES6 |
10 Behavior Delegation
以下都先參考原文吧,我有空在整理
主要是說在javascript世界中Behavior Delegation的做法比OO還要直覺簡單,有實際例子、code舉例
You-Dont-Know-JS/this & object prototypes/ch6.md
11 Asynchrony
大致介紹javascript的Asynchronous
JS Engine本身沒有multi-thread,javascript的Asynchronous是靠run environment的event loop queue達成
You-Dont-Know-JS/async & performance/ch1.md
12 Callbacks
主要是在說明傳統使用callback來達成Asynchronous的各種缺點
- 反人類思考模式,難以閱讀
- callback會將程式帶到非自己所寫的function,可以在callback中撰寫各種check來處理但不夠好
13 ES6新東東
let, const, arrow function, class, module, symbol
14 Reference
此筆記主要是來自截取並整理自Github上的You-Dont-Know-JS
附上原始位置供各位參考
https://github.com/getify/You-Dont-Know-JS
Render by hexo-renderer-org with Emacs 25.2.1 (Org mode 8.2.10)