整理一些文章與小知識,以供複習或尋找solution
ant design 的範例都是使用這種寫法
- PureComponent 謹慎使用,讓性能優化
比對前後props、state是否有改變,沒有改變shouldComponentUpdate會return false,則物件不會再次執行render()
- 由於Immutable data structures的關係,使用PureComponent的shallowEqual可能會出錯(不如預期),所以在setState之前,不能直接引用props或state,必須創建一個新的物件 -- 1
習慣必須養成不要直接調用props、state,需另創一個物件,如:const arr = [...this.state.array]。 必須如此的原因,是因為無論在shouldComponentUpdate 或者 virtualDOM的前後樹判斷時,很多時候是去比較物件前後是否相同。
- 在react中如果function想要傳遞參數,有兩種方法 [1]onClick={()=>this.myFunction(params)}, [2]onClick={this.myFunction.bind(this,params)} --詳解arrow function in react
由於兩種方法都會創造新的函數,如果在render()中使用,每次重新render都會重新創造函數,會造成些許的效能問題
如果是用這兩種方法傳遞給child component 或 purecomponent,由於每次都創造新的函數,會使得purecomponent失效,解法如下-1
不需要排斥使用這樣的寫法,但在撰寫程式時可以盡量縮減,當網頁有效能問題時,可以再回來構思如何修改。
- 卸除component時(componentWillUnmount階段),不要有setState()的執行
雖然有此情況不會造成程式崩壞,但會有warning,本次經驗是在使用者請求資料後,突然放棄等候而跳頁產生。
需要才加載--3
- controlled component & uncontrolled component 的使用情境--1
受控組件即是指使用setState去監控擁有輸出輸入功能的element,如:select, textarea, input等等,功能較為強大,能夠做到即時驗證、強迫改變資料輸入型態、autocomplete等等, 非受控組件與傳統的HTML element有些相像,相對受控組件而言,程式碼實現較為精簡,但能延伸出來的變化也較少。--1 --2
- React Lifecycle -- 1
- 頁面傳值有三個方法: 1. props.params, 2. query, 3. state --1
文章說明query方法類似get,會併接url,而state類似post,不會以明文傳遞,但不知原因為何,實際使用兩者的url都沒有顯示,還是推薦使用state。
前後端的 router 實作差異
- 在IE中使用axios,安裝babel-polyfill,在import時必須擺在第一個。1
由於IE沒支援promise語法,需要使用額外套件編譯。
- 在cancel request的時候,使用 cancelToken: new CancelToken(function(cancel){}),必須注意post的cancel必須寫在config中。 --1
axios 文檔中寫道: axios.get(url[, config]) , axios.post(url[, data[, config]]),在post的config須注意是第二個參數。
- 在axios中想要取得更多得error資訊,在官方文檔中說明滿清楚的1
可以用於catch不同的error以做出不同的動作,如network error, time out 等等......。
- 提升(Hoisting),若是某行程式碼需要取得的變數宣告在其後執行,編譯器會自動幫程式碼在最上頭加上var x,此時值為undefined,直到程式執行給值那行--1
函數物件同樣會被hoisting, 如: var x = function(){...}, 單純函數不會。
- callback function 與 IIEF --1
把函數當成另一個函數的參數,主要作用在制定函數執行順序,徹底理解文章中舉的例子。
有趣的題目變化--1
- 同步(Synchronous) 與 非同步(Asynchronous)--1
Callback Hell : 大量使用非同步且又想要依照固定的順序來執行時, 就可能會出現
使用promise語法來避免callback hell --1 promise語法使用概念
- 非侵入式javascript --1
這樣的寫法可以分離HTML與javascript,達成視圖與邏輯分離的架構,程式碼便不會全部混雜在一起。但如此一來,單看HTML的element無法知道他被綁定了甚麼事件,必須要去查詢js document中的所有監聽事件。
React 中的 JSX寫法反其道而行,兩者互相結合,將component合適的切割分層,程式碼便不會看起來混亂。
- Pass by value、 Pass by reference 、Pass by sharing(scope的關係) --1
物件、陣列可變,基本型不可變
-
事件的註冊綁定-[1] on-event(HTML屬性), [2] on-event (非HTML屬性), [3] EventTarget.addEventListener()
必須深刻了解其傳遞方式
- 阻擋事件冒泡 e.stopPropagation() --1
e.stopPropagation()、this的導向、event.currentTarget、Event Delegation
文章中所提到的重點:e.target 其實是「觸發事件的元素」,而 this 指的是「觸發事件的目標」元素,也就是 event.currentTarget
- event.preventDefault() 可在addEventListener使用,讓element的原始動作失效。
return false也能有同樣效果
-
rest operate & spread orperate --1
-
lodash & lazy 提升js程式碼可維護性、效能。
注意不要過早優化程式效能,初期開發階段,可維護性、可讀性、可測試性更為重要。
利用 .reduce() 進行陣列扁平化
- Property descriptor --1
屬性描述器:value, writable, enumerable, configurable, get, set。
Object.defineProperty(obj, prop, descriptor), Object.getOwnPropertyDescriptor()
A instanceof B , 'xxx' in A -- 透過「原型鏈」檢查屬性, A.hasOwnProperty('xxx') -- 檢查的屬性,是否為「物件本身」所有;
Object.prototype.hasOwnProperty(), Object.prototype.toString(), Object.prototype.valueOf()
Object.setPrototypeOf(A, B), 被當作原型的物件只能唯一(proto)。
當物件實體與它的原型同時擁有同樣的屬性或方法時,會優先存取自己的屬性或方法,如果沒有才會再順著原型鏈向上尋找。
- javascript是靜態作用域 --1
var a = 100
function echo() {
console.log(a) // 100 or 200?
}
function test() {
var a = 200
echo()
}
test() //100
but... 2
var foo = 'foo';
var obj = {
foo: 'foo in Object'
};
var sayFoo = function() {
console.log( this.foo );
};
obj.sayFoo = sayFoo;
obj.sayFoo(); // 'foo in Object'
sayFoo(); // 'foo'
在物件中的this會改變指向方向。
- this的scope
JavaScript 中,用來切分變數的最小作用範圍 (scope),也就是我們說的有效範圍的單位,就是 function。
當沒有特定指明 this 的情況下,預設綁定 (Default Binding) this 為 「全域物件」,也就是 window。 (但是嚴格模式底下,會禁止this自動成為全域物件)
- class & function 1
// 只是個函式
function Person(name) {
this.name = name;
}
var fred = new Person('Fred'); // ✅ Person {name: 'Fred'}
var george = Person('George'); // 🔴 不行
藉由在呼叫前增加 new,我們告訴 JavaScript 說:「嘿 JavaScript,我知道 Person 只是一個函式,但讓我們假裝它就像一個類別的建構子,創建一個 {} 物件並且將 Person 函式內部的 this 指向這個物件,這樣我就能設置 this.name 之類的東西了。然後把這個物件回傳給我。」
類別語法讓我們能表示:「這不只是一個函式 —— 他是一個類別,而且有建構子。」
let fred = new Person('Fred');
// ✅ 如果 Person 是一個函式: 沒問題
// ✅ 如果 Person 是一個類別: 也沒問題
let george = Person('George'); // 我們忘記 `new` 了
// 😳 如果 Person 是一個像建構子的函式:令人困惑的行為 (關鍵字:像建構子的函數,別混淆一般有return的函數了)
// 🔴 如果 Person 是一個類別:直接失敗
對於函數,需要判斷使用new的時機,有些狀況不適合,如箭頭函數、一般函數
const Person = (name) => {
// 🔴 這樣不合理! 箭頭函數並沒有自己的this
this.name = name;
}
--------
function Greeting() {
return 'Hello';
}
Greeting(); // ✅ 'Hello'
new Greeting(); // 😳 Greeting {}
JavaScript 還允許被 new 呼叫的函式藉由回傳其他物件來覆蓋它的回傳值,如果一個函式的回傳值不是一個物件, new 會完全忽略它,就是說如果你回傳字串或是數字,它會像根本沒有回傳一樣。
function Answer() {
return 42;
}
Answer(); // ✅ 42
new Answer(); // 😳 Answer {}
- anonymous function --1
javascript 函數中自帶關鍵字 arguments,它只在函數內不起作用,並且永遠指向當前函數的調用者傳入的所有參數。arguments 類似 Array 但它不是一個 Array
var x = function(arguments1,arguments2){
return arguments1;
}
console.log(x(1,2)) //1
var y = function(){
return arguments; //自帶關鍵字
}
console.log(y(1,2)) //arguments=[1,2]
- JavaScript this ,了解call-site(調用點),對於理解this的指向相當重要。 YDKJS
- 默認綁定(Default Binding) => this 實施了
默認綁定
,所以使 this 指向了全局對象
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
- 隱含綁定(Implicit Binding) => 調用點是否有一個環境對象(context object),也稱為擁有者(owning)或容器(containing)對象(object)
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
隱含丟失(Implicitly Lost) => this 綁定最常讓人沮喪的事情之一,就是當一個
隱含綁定
丟失了它的綁定,這通常意味著它會退回到默認綁定
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数引用!
var a = "oops, global"; // `a` 也是一个全局对象的属性
bar(); // "oops, global"
盡管 bar 似乎是 obj.foo 的引用,但實際上它只是另一個 foo 本身的引用而已
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// `fn` 只不过 `foo` 的另一个引用
fn(); // <-- 调用点!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // `a` 也是一个全局对象的属性
doFoo( obj.foo ); // "oops, global"
使用回調函數有一樣的結果
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // `a` 也是一个全局对象的属性
setTimeout( obj.foo, 100 ); // "oops, global"
套用上setTimeout(),即便裡面是使用obj.foo,依舊是會導向全域(implicitly loss)
函數丟掉他們的
this
是非常常見的,必須明確的知道函數的調用點,才能夠知道this指向何處,若是想要固定函數this的指向,可以使用明確綁定(call,apply,bind),有時候箭頭函數的特性也可拿來使用
- 明確綁定(Explicit Binding)
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
foo.call( obj ); // 2
可以參考 iT邦幫忙
硬綁定(Hard Binding),明確綁定的變種技巧
//技巧一
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// `bar` 将 `foo` 的 `this` 硬绑定到 `obj`
// 所以它不可以被覆盖
bar.call( window ); // 2
我們創建了一個函數 bar(),在它的內部手動調用 foo.call(obj),由此強制 this 綁定到 obj 並調用 foo。無論你過後怎樣調用函數 bar,它總是手動使用 obj 調用 foo。這種綁定即明確又堅定,所以我們稱之為
硬綁定(hard binding)
//技巧二,參數代入
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
var obj = {
a: 2
};
var bar = function() {
return foo.apply( obj, arguments );
};
var b = bar( 3 ); // 2 3
console.log( b ); // 5
ES5新增的
bind()
實作
//bind()實作,base on call or apply
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
// 简单的 `bind` 帮助函数
function bind(fn, obj) {
return function() {
return fn.apply( obj, arguments );
};
}
var obj = {
a: 2
};
var bar = bind( foo, obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
new
綁定(new binding)
- 一個全新的對象會憑空創建(就是被構建)
- 這個新構建的對象會被接入原形鏈([[Prototype]]-linked)
- 這個新構建的對象被設置為函數調用的 this 綁定
- 除非函數返回一個它自己的其他對象,否則這個被 new 調用的函數將
自動
返回這個新構建的對象。(意即函數中return其他物件,若為非物件則回傳{})
function foo(a) {
this.a = a;
}
var bar = new foo( 2 );
console.log( bar.a ); // 2
- 判定
this
,new綁定 > 明確綁定 > 隱含綁定 > 默認綁定
- 函數是通過 new 被調用的嗎(new 綁定)?如果是,this 就是新構建的對象。
var bar = new foo()
- 函數是通過 call 或 apply 被調用(明確綁定),甚至是隱藏在 bind 硬綁定 之中嗎?如果是,this 就是那個被明確指定的對象。
var bar = foo.call( obj2 )
- 函數是通過環境對象(也稱為擁有者或容器對象)被調用的嗎(隱含綁定)?如果是,this 就是那個環境對象。
var bar = obj1.foo()
- 否則,使用默認的 this(默認綁定)。如果在 strict mode 下,就是 undefined,否則是 global 對象。
var bar = foo()
this
的使用原則
function foo() {
var self = this; // 词法上捕获 `this`
setTimeout( function(){
console.log( self.a );
}, 100 );
}
var obj = {
a: 2
};
foo.call( obj ); // 2
對於不想使用bind(...),self=this和箭頭函數都是個看起來不錯的"解決方案",但實質上逃避了this而非理解與接受它,為此有兩個選擇。。
- 僅使用詞法作用域並忘掉虛偽的this風格代碼。
- 完全接受this風格機制,包括在必要的時候使用bind(...),並嘗試避開 self = this 和箭頭函數的 "詞法this"技巧。
雖說我個人認為self = this的確不是個很好的程式寫法,但在react中使用axios老是會用到(意即在promise中要使用this的東西如setState),或許在這樣的情況下會是不錯的解法,就我個人而言,程式並沒有變得更加複雜難明。
- YDKJS-Object
一個常見的錯誤判斷是"Javascript中的一切都是對象" (六個主要類型,null算一個)
Javascript 會在必要時強制將基本類型如 "string" 轉換為 對象類型 "String",一般都建議不需要去new出一個基本類型,如下範例,string自帶有length或charAt()
var strPrimitive = "I am a string";
console.log( strPrimitive.length ); // 13
console.log( strPrimitive.charAt( 3 ) ); // "m"
- YDKJS-複製對象 :淺(Shallow)拷貝 OR 深(Deep)拷貝
須注意是否為淺拷貝,如:var newObj = Object.assign( {}, myObject );
function anotherFunction() { /*..*/ }
var anotherObject = {
c: true
};
var anotherArray = [];
var myObject = {
a: 2,
b: anotherObject, // 引用,不是拷贝!
c: anotherArray, // 又一个引用!
d: anotherFunction
};
anotherArray.push( anotherObject, myObject );
如何完成拷貝 方法一:使用JSON (深)
var newObj = JSON.parse( JSON.stringify( someObj ) );
方法二:使用Spread operation (淺)
var newObj = {...someObj}
需要注意的地方,那就是 spread operator 只會複製第一層的資料而已,它並不是 deep clone
方法三:使用Object.assign( {}, myObject ) (淺)
- Object literals --1
let website = "pjchender";
let obj = {website};
console.log(obj); //[Object]{website: "pjchender"}
constructor huli
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.log = function () {
console.log(this.name + ', age:' + this.age);
}
var nick = new Person('nick', 18);
// 這段是要讓大家知道,這邊其實是往原型鍊的上面去找
console.log(nick.constructor === Person); // true
console.log(nick.hasOwnProperty('constructor')); // false
// Person 的 constructor 就是 Person
console.log(Person.prototype.constructor === Person); // true
console.log(Person.prototype.hasOwnProperty('constructor')); // true
A.prototype.constructor === A,把 A 用 Function, Person, Object 之類的值帶進去都成立。
new huli
有了原型鍊的概念之後,就不難理解new這個關鍵字背後會做的事情是什麼。
假設現在有一行程式碼是:var nick = new Person('nick');,那它有以下幾件事情要做:
1. 創出一個新的 object,我們叫它 O
2. 把 O 的 __proto__ 指向 Person 的 prototype,才能繼承原型鍊
3. 拿 O 當作 context,呼叫 Person 這個建構函式
4. 回傳 O
我們可以寫一段程式碼來模擬這個情形:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.log = function () {
console.log(this.name + ', age:' + this.age);
}
function newObj(Constructor, arguments) {
var o = new Object();
// 讓 o 繼承原型鍊
o.__proto__ = Constructor.prototype;
// 執行建構函式
Constructor.apply(o, arguments);
// 回傳建立好的物件
return o;
}
var nick = newObj(Person, ['nick', 18]);
nick.log(); // nick, age:18
理解如下變化-1
function F() {}
function O() {}
O.prototype = new F();
var obj = new O();
console.log(obj instanceof O); // true
console.log(obj instanceof F); // true
console.log(obj.__proto__ === O.prototype); // true
console.log(obj.__proto__.__proto__ === F.prototype); // true
//更改了順序後結果大不相同
function F() {}
function O() {}
var obj = new O();
O.prototype = new F();
console.log(obj instanceof O); // false
console.log(obj instanceof F); // false
console.log(obj.__proto__ === O.prototype); // false
console.log(obj.__proto__.__proto__ === F.prototype); // false
locale i18n