Generator是一種異步編程解決方案。javascript
next方法的參數php
yield
表達式自己沒有返回值,或者說老是返回undefined
。next
方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值。css
因爲Generator函數返回的遍歷器對象,只有調用next方法纔會遍歷下一個內部狀態,因此其實提供了一個能夠暫時執行的函數。yield表達式就是暫停標誌。html
function* foo(x) {
var y = 2 * (yield (x + 1))
var z = yield (y / 3)
return (x + y + z)
}
var a = foo(5)
a.next() // { value: 6, done: false }
a.next() // { value: NaN, done: false }
a.next() // { value: NaN, done: true}
var b = foo(5)
b.next() // { value: 6, done: false }
b.next(12) // {value: 8, done: false }
b.next(13) // {value: 42, done: false }
複製代碼
yield 後面的值做爲返回值前端
上面代碼中,a在第二次運行next方法的時候不帶參數,致使y的值等於 2 * undefined (即 NaN),除以3之後仍是NaN,所以返回對象的value屬性也等於NaN。第三次運行Next方法的時候不帶參數,因此z等於undefined,返回對象的value屬性等於5 + NaN + undefined,即NaN.java
若是向next方法提供參數,返回結果就徹底不同了。b在第一次調用next方法時,返回x+1的值6;第二次調用next方法,將上一次yield表達式的值設爲12,所以y等於24,返回y/3的值8;第三次調用next方法,將上一次yield表達式的值設爲13,所以z等於13,這時x等於5,y等於24,因此return語句的值等於42.node
注意:因爲next方法的參數表示上一個yield表達式的返回值,因此**在第一次使用next方法時,傳遞參數是無效的。**V8引擎直接忽略第一次使用next方法時的參數,只有從第二次使用next方法開始,參數纔是有效的。從語義上講,第一個next方法用來啓動遍歷器對象,因此不用帶有參數。react
回調函數自己並無問題,它的問題出如今多個回調函數嵌套。linux
Promise
的寫法只是回調函數的改進,使用then
方法之後,異步任務的兩段執行看得更清楚了,除此之外,並沒有新意。c++
Promise
的最大問題是代碼冗餘,原來的任務被 Promise
包裝了一下,無論什麼操做,一眼看去都是一堆then
,原來的語義變得很不清楚。
co模塊
var gen = function* () {
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
複製代碼
co 模塊可讓你不用編寫 Generator 函數的執行器。
var co = require('co');
co(gen);
複製代碼
上面代碼中,Generator 函數只要傳入co函數,就會自動執行。
co函數返回一個Promise對象,所以能夠用then方法添加回調函數。
co(gen).then(function (){
console.log('Generator 函數執行完成');
});
複製代碼
co 模塊其實就是將兩種自動執行器(Thunk 函數和 Promise 對象),包裝成一個模塊。使用 co 的前提條件是,Generator 函數的yield命令後面,只能是 Thunk 函數或 Promise 對象。若是數組或對象的成員,所有都是 Promise 對象,也可使用 co
co 模塊的源碼
co 就是上面那個自動執行器的擴展,它的源碼只有幾十行,很是簡單。
首先,co 函數接受 Generator 函數做爲參數,返回一個 Promise 對象。
function co(gen) {
var ctx = this;
return new Promise(function(resolve, reject) {
});
}
複製代碼
第一行,檢查當前是否爲 Generator 函數的最後一步,若是是就返回。
第二行,確保每一步的返回值,是 Promise 對象。
第三行,使用then方法,爲返回值加上回調函數,而後經過onFulfilled函數再次調用next函數。
第四行,在參數不符合要求的狀況下(參數非 Thunk 函數和 Promise 對象),將 Promise 對象的狀態改成rejected,從而終止執行。
處理併發的異步操做
let obj1 = {a:1,b:{c:2},d:3}
let obj2 = Object.assign({},obj1)
複製代碼
說明:Object.assign()方法只能複製第一層的對象值。可是obj2裏b對象的c的值得時候,obj1裏b的對象裏的c值也發生了變化,說明Object.assign()這個方法不是深層的複製對象,只是讓對象裏第一層的數據沒有了關聯,可是對象內的對象則跟被對象複製的對象有着關聯性。
目前最經常使用的進行深拷貝的方法
let obj1 = {a:1,b:{c:2},d:3}
let obj2 = JSON.parse(JSON.stringify(obj1))
複製代碼
咱們先把obj1轉成字符串類型,這樣他就失去了對象的屬性和特性,而後咱們再把他轉成一個對象類型,這樣咱們新生成的對象是經過字符串轉換過來的,這樣居生成了一個新對象。 注:此方法對對象的value爲function的無效。
按值傳遞和按引用傳遞
function test(obj) {
obj.age= '28';
console.log('inner',obj) // inner {name: "shuiguoge", age: "28"}
}
let obj={
name: 'shuiguoge'
}
test(obj)
console.log('outer',obj) // outer {name: "shuiguoge", age: "28"}
複製代碼
咱們發現兩個obj指向同一個內存的副本
js中常見的按引用傳遞: object array
按值傳遞:string number boolean
setTimeout(function () {
setTimeout(function () {
console.log(1)
}, 100);
console.log(2)
setTimeout(function () {
console.log(3)
}, 0);
}, 0);
setTimeout(function () {
console.log(4)
}, 100);
結果: 2 3 4 1
複製代碼
瀏覽器是多線程的可是js是單線程的! 解析:事件循環event loop。主線程(js線程)只會作一件事,就是從消息隊列裏面取消息、執行消息,再取消息、再執行。消息隊列爲空時,就會等待直到消息隊列變成非空。只有當前的消息執行結束,纔會去取下一個消息。這種機制就叫作事件循環機制Event Loop,取一個消息並執行的過程叫作一次循環。
鏈式setTimeout:在前一個定時器執行完以前,不會向隊列插入新的定時器。
此方法只能用於String
和Array
在數組中的用法 indexOf()方法返回在數組中能夠找到一個給定元素的第一個索引,若是不存在,則返回-1. 參數 searchElement 要查找的元素 fromIndex 可選開始查找的位置。若是該索引值大於或等於數組長度,意味着不會在數組裏查找,返回-1.若是參數中提供的索引值是一個負值,則將其做爲數組末尾的一個抵消,即-1表示從最後一個元素開始查找,-2表示從倒數第二個元素開始查找,以此類推。注意:若是參數中提供的索引值是一個負值,並不改變其查找順序,查找順序仍然是從前向後查詢數組。若是抵消後的索引值仍小於0,則整個數組都將會被查詢。其默認值爲0.
var array = [2, 5, 9]
array.indexOf(2) // 0
array.indexOf(7) // -1
array.indexOf(9) // 2
複製代碼
找出指定元素出現的全部位置
var indices = []
var array = ['a','b','a','c','a','d']
var element = 'a'
var idx = array.indexOf(element)
while (idx != -1) {
indices.push(idx)
idx = array.indexOf(element, idx + 1)
}
console.log(indices)
// [0,2,4]
複製代碼
在字符串中的用法
indexOf()方法返回調用String
對象中第一次出現的指定值的索引,開始在fromIndex進行搜索。若是未找到該值,則返回-1. indexOf
方法區分大小寫。 參數 searchValue
一個字符串表示被查找的值。 fromIndex
可選 表示調用該方法的字符串中開始查找的位置。能夠是任意整數。默認值爲 0。若是 fromIndex < 0 則查找整個字符串(如同傳進了 0)。若是 fromIndex >= str.length,則該方法返回 -1。 返回值 指定值得第一次出現的索引;若是沒有找到返回-1
'Blue Whale'.indexOf('Blue') !== -1 // true
複製代碼
使用 indexOf 統計一個字符串中某個字母出現的次數
var str = 'To be, or not to be. that is the question'
var count = 0
var pos = str.indexOf('e')
while (pos !== -1) {
count ++
pos = str.indexOf('e',pos+1)
}
console.log(count) // 4
複製代碼
lastIndexOf()
方法返回指定值在調用該方法的字符串中最後出現的位置,若是沒找到則返回-1.從該字符串的後面向前查找,從fromIndex
處開始。 demo:
let url = 'https://www.baidu.com/search'
let index = url.lastIndexOf('/')
let subStr = url.substring(0,index)
console.log(subStr) // https://www.baidu.com
複製代碼
substr()
方法返回一個字符串中從指定位置開始到指定字符數的字符。
str.substr(start[, length])
複製代碼
參數:
start:開始提取字符串的位置。首字符的索引爲0,最後一個字符的索引爲 字符串的長度減去1。若是爲負值
,則被看做 strLength + start,其中 strLength 爲字符串的長度(例如,若是 start 爲 -3,則被看做 strLength + (-3))。
length:可選。提取的字符數。
var str = 'idesefesefsggfsehnjk'
var strSub = str.substr(3,3)
console.log(strSub) // sef
複製代碼
substring()
方法返回一個字符串在開始索引到結束索引之間的一個子集,或從開始索引直到字符串的末尾的一個子集。
str.substring(indexStart[, indexEnd])
複製代碼
參數:
indexStart: 須要截取的第一個字符的索引,該字符做爲返回的字符串的首字母。
indexEnd: 可選。一個0到字符串長度之間的整數,以該數字爲索引的字符不包含在截取的字符串內。
返回值:包含給定字符串的指定部分的新字符串。
var str = 'idesefesefsggfsehnjk'
var strSub = str.substring(0,3)
console.log(strSub) // ide
複製代碼
區別:substr
方法返回的是從一個指定位置開始的指定長度的字符串。而substring
方法返回才從指定開始位置到指定結束位置的子字符串。
總結: toString()
的基礎用法就是返回相應的字符串
toString()
方法返回指定Number
對象的字符串表示形式。
numObj.toString([radix])
複製代碼
參數
redix指定要用於數字到字符串的轉換的基數(從2到36)。若是未指定radix
參數,則默認爲10.
異常信息
若是toString()
的radix
參數再也不2到36之間,將會拋出一個RangeError
進行數字到字符串的轉換時,建議用小括號將要轉換的目標括起來,防止出錯。
var a = 256
a.toString() // '256'
(178).toString() // '178' 要用小括號,防止出錯
複製代碼
toString()返回一個字符串,表示指定的數組及其元素。
var array1 = [1,2,'a','l']
console.log(array1.toString()) // 1,2,a,l
複製代碼
Array
對象覆蓋了Object的toString方法。對於數組對象,toString方法鏈接數組並返回一個字符串,其中包含用逗號分隔的每一個數組元素。
當一個數組被做爲文本值或者進行字符串鏈接操做時,將會自動調用其toString
方法。
toString() 方法返回一個表示該對象的字符串。
描述: 每一個對象都有一個toString()
方法,當該對象被表示爲一個文本值時,或者一個對象以預期的字符串方式引用時自動調用。默認狀況下,toString()
方法被每一個Object
對象繼承。若是此方法在自定義對象中未被覆蓋,toString()
返回「[object type]」,其中type
是對象的類型。如下代碼說明了這一點:
var o = new Object()
o.toString() // [object object]
複製代碼
使用toString()檢測對象類型
能夠經過toString()
來獲取每一個對象的類型。爲了每一個對象都能經過Object.prototype.toString()來檢測,須要以function.prototype.call()
或者function.prototype.call()
的形式來調用,傳遞要檢查的對象做爲第一個參數,稱爲thisArg
。
let toString = Object.prototype.toString
toString.call(new Date) // [object Date]
toString.call(new String) // [object String]
toString.call(Math) // [object Math]
toString.call(undefined) // [object Undefined]
toString.call(null) // [object Null]
複製代碼
4.Function.prototype.toString()
toString()
方法返回一個表示當前函數源代碼的字符串
function sum(a,b) {
return a + b
}
console.log(sum.toString())
// "function sum(a,b) { // return a + b //}"
複製代碼
Function
對象覆蓋了從Object
繼承來的toString
方法。對於用戶定義的Function
對象,toString
方法返回一個字符串,其中包含用於定義函數的源文本段。
在Function
須要轉換爲字符串時,一般會自動調用函數的toString
方法。
toString()
方法返回一個字符串,表示該Date對象。
Date
對象覆蓋了Object
對象的toString
方法;它不是繼承自Object.prototype.toString()
。對於Date
對象,toString()
方法返回一個表示該對象的字符串。
該toString
方法老是返回一個美式英語日期格式的字符串。
當一個日期對象被用來做爲文本值或用來進行字符串鏈接時,toString方法會自動調用。
var x = new Date()
myVar = x.toString()
// "Sun Mar 31 2018 17:15:41 GMT+0800 (中國標準時間)"
複製代碼
toString()
方法返回指定對象的字符串形式
String
對象覆蓋了Object
對象的toString
方法;沒有繼承Object.toString()
。對於String
對象,toString
方法返回該對象的字符串形式,和String.prototype.valueOf()
var x = new String('Hello World')
console.log(x) // String {"hello world"}
x.toString() // hello world
複製代碼
隱式類型轉換
const a = {
num: 0,
valueOf: function() {
return this.num += 1
}
};
const equality = (a==1 && a==2 && a==3);
console.log(equality)
複製代碼
參考: JavaScript:(a==1 && a==2 && a==3)能輸出ture麼?
總結: valueOf()
方法返回指定對象的原始值。
valueOf()
方法返回一個String
對象的原始值。
var x = new String('Hello World')
x.valueOf() // hello world
複製代碼
valueOf()
方法返回指定對象的原始值
JavaScript調用valueOf
方法將對象轉換爲原始值。你不多須要本身調用valueOf()
方法;當遇到要預期的原始值的對象時,JavaScript會自動調用它。
核心點在運算符的優先級
var a = {n: 1}
var b = a
a.x = a = {n: 2}
console.log(a.x) // undefined
console.log(b.x) // {n: 2}
複製代碼
運算符優先級 .
高於=
a = b = c = d = 7 的運算過程 d被賦值爲7,c被賦值爲7,b被賦值爲7,a被賦值爲7,整個表達式的返回值爲7
對於 a.x = a = {n: 2}
第一步執行a.x
,爲a對象建立x屬性,此時x屬性的值是undefined
,此時a對象和b對象的指針地址是相同的a.x === b.x // true
,因此b.x的值是{n:2}
,a.x的返回值爲undefined,第二部執行 a={n:2},a對象被指向了新地址,且返回值爲{n: 2}
最後的a.x是在被指向了新地址後的a的屬性,不存在x屬性,因未被賦值,爲undefined
聲明a對象的x屬性,用於賦值,此時b指向a,同時擁有未賦值的屬性x
對象a對象賦值,此時變量名a改變指向到對象 {n:2}
對步驟1中x屬性賦值
拆分
a.x = a = {n: 2}
a.x = {n: 2}
b.x = {n: 2}
a = {n: 2} // a被再次從新賦值,a中再也不含有a.x
// 將 a.x = a = {n: 2}換成 a = a.x = {n: 2} 效果相同
複製代碼
解析器在接受到a = a.x = {n: 2}
後的操做以下:
連續賦值考慮的點: 1. 指針的變化。2.運算符的優先級.
高於=
。3.按值傳遞與按引用傳遞
基本思路:
b.com
在發現未登陸時跳轉至a.com
進行登陸a.com
在登陸完成後將登陸authcookie
與用戶信息記錄到服務器(session_id或者redis)a.com
建立一個令牌token
關聯上一步的authcookie
,並帶回b.com
的backUrl中b.com
拿到a.com
發回來的令牌去調用a.com
的接口查詢是否令牌有效而且能夠查到用於信息,有的話則拿回數據並作後續操做注: 單點登陸與先後端分離沒有關係
基本類型
typeof undefined // undefined
typeof 'abc' // string
typeof '1' // string
typeof 123 // number
typeof true // boolean
typeof Symbol() // 'symbol'
typeof null // object
複製代碼
若是一個變量未聲明,或聲明但爲賦值,將顯示undefined
var a
typeof a // a聲明,可是未賦值,所以爲 undefined
typeof b // b 沒有聲明,可是還會顯示 undefined
可是 !b 卻會報 VM1613:1 Uncaught ReferenceError: b is not defined 的錯誤,!a 則爲 true 以下圖
複製代碼
typeof {} // object
typeof [] // object
typeof console.log // function
複製代碼
typeof總結
es6語法
var s = Symbol()
typeof s // "symbol"
var n = Symbol()
s === n // false
var m = Symbol('intel')
symbol做用
1.生成的值所有是不相等的
2.防止對象key相同時值被覆蓋的狀況
用法
var q = Symbol('intel')
m === q // false
var obj = {name: '張三'}
var sName = Symbol('name')
obj.sName = "李四"
console.log(obj) // {name: "張三", sName: "李四"} 理論上張三的李四的key值都是name,但由於symbol,避免了相同時值被覆蓋的狀況
複製代碼
字符串拼接
var a = 100 + 10 // 110
var b = 100 + '10' // 10010
var c = 100 - '10' // 90
複製代碼
變量提高
console.log(b) // b(){}
var b = 1
function b() {
}
console.log(b) // 1
複製代碼
100 == '100' // true 嘗試讓左右相等 '100' == '100'
0 == '' // true false == false
null == undefiend // true false == false
// 什麼時候使用 == 什麼時候使用 ===
只有下面這種狀況適合用 ==,別的狀況全用 === 判斷一個對象的屬性是否是存在(null或undefined)
if (obj.a == null) {
// 這裏至關於 obj.a === null || obj.a === undefined 的簡寫形式,是jq源碼推薦的寫法
}
=== 不會進行強制類型轉換
複製代碼
var a = true
if (a) {
// ...
}
var b = 100
if (b) { // b被轉化爲true
// ...
}
var c = ''
if (c) { // c被轉化爲 false
// ...
}
if中false的狀況
1. 0
2. NaN
3. ''
4. null
5. false
6. undefined
複製代碼
console.log(10 && 0) // 0
console.log('' || 'abc') // 'abc' 至關於 fasle || 'abc'
console.log(!window.abc) // true 至關於 !undefined
// 判斷一個變量會被轉化爲布爾類型的true仍是false
var a = 200
console.log(!!a) // true
複製代碼
Object // ƒ Object() { [native code] }
Array // ƒ Array() { [native code] }
Boolean
Number
String
Function
Date
RegExp
Error
複製代碼
// 值類型
var a = 10
var b = a
a = 16
console.log(b) // 10
// 引用類型
var obj1 = {x: 100}
var obj2 = obj1
obj1.x = 200
console.log(obj2.x) // 200
var arr = [1,2,3]
arr.age = 21 // 數組也能夠經過這種方式複製
console.log(arr) // [1,2,3,arg:21]
值類型是值是被複制後放在各自的內存空間中,值的改變相互之間不會影響。
引用類型,則是指向了同一個內存對象,指針相互傳遞。目的:經過這種方式能夠減小內存的開銷.js 、c++ 等高級緣由都是這麼設計的。
複製代碼
1.JSON只不過是一個JS對象而已,內置在js的語法中。有兩個經常使用API:stringfy、parse。和Math同樣。
2.JSON也是一種數據格式
JSON.stringfy({a: 10,b: 20})
JSON.parse('{"a":10,"b":20}')
JSON、Math是內置對象
JSON {parse: ƒ, stringify: ƒ, Symbol(Symbol.toStringTag): "JSON"}
複製代碼
構造函數
// 構造函數定義,大寫開頭
function Foo(name, age) {
this.name = name
this.age = age
this.class = '一年級1班'
// return this // 默認有這一行
}
var f = new Foo('張三', 20)
var n = new Foo('李四', 19)
複製代碼
構造函數--擴展
1. var a = {} 實際上是 var a = new Object()的語法糖
2. var a = [] 實際上是 var a = new Array()的語法糖
3. function Foo(){ } 實際上是 var Foo = new Function()
4. 使用instanceof判斷一個函數是不是一個變量的構造函數(可用於判斷一個變量是否爲「數組」,使用:arr instanceof Array)
複製代碼
原型規則和示例
5條原型規則
原型規則是原型鏈的基礎
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100
function fn () { }
fn.a = 100
複製代碼
console.log(obj,__proto__)
console.log(arr.__proto__)
console.log(fn.__proto__)
複製代碼
console.log(fn.__prototype)
複製代碼
prototype
屬性值console.log(obj.__proto__ === Object.prototype) // true
理解: var obj = {}其實是 var obj = new Object()
所以 obj.__proto__ === Object.prototype
複製代碼
// 構造函數
function Foo(name) {
this.name = name
}
Foo.prototype.alertName = function () {
alert(this.name)
}
// 建立示例
var f = new Foo('張三')
f.printName = function () {
console.log(this.name)
}
f.printName()
f.alertName()
// 遍歷
var item
for (item in f) {
// hasOwnPrototype 只遍歷自己的屬性,而不編譯原型鏈上的屬性
// 高級瀏覽器已經在for in中屏蔽了來自原型的屬性
// 可是仍是建議你們加上這個判斷,保證程序的健壯性
if (f.hasOwnPrototype(item) ) {
console.log(item)
}
}
複製代碼
原型鏈
f.toString() // 要去f.__proto__.__protoo__中查找
複製代碼
instanceof 用於判斷引用類型
屬於哪一個構造函數
的方法
f instanceof Foo 的判斷邏輯是:f 的__proto__一層一層往上,可否對應到 Foo.prototype
判斷 f instanceof Object
複製代碼
如何準確判斷一個變量是數組類型
框架源碼中如何使用原型鏈
複製代碼
1. 一個新對象被建立。它繼承自foo.prototype原型對象(很是重要)
2. 構造函數 foo 被執行。執行的時候,相應的傳參會被傳入,同時上下文(this)會被指定爲這個新實例。new foo等同於 new foo(),只能用在不傳遞任何參數的狀況下
3. 若是構造函數返回了一個「對象」,那麼這個對象會取代整個new出來的結果。若是構造函數沒有返回對象,那麼new出來的結果爲步驟1建立的對象。
var new = function(func) {
var o = Object.create(func.prototype)
var k = func.call(o)
if (typeof k === 'object') {
return k
} else {
return o
}
}
複製代碼
面向對象
一個原型鏈繼承的例子(本質就是原型鏈)
function Car (color) {
this.color = color
}
Car.prototype.sail = functin() {
console.log(this.color)
}
var s = new Car('RED')
function BMW(color) {
Car.call(this.color)
}
var __proto = Object.create(Car.prototype)
__proto.constructor = BMW
BMW.prototype = __proto
BMW.prototype.price = function () {
}
複製代碼
// 藉助構造函數實現繼承
function Parent1 () {
this.name = 'parent1'
}
Parent1.prototype.say = function () {} // 此方法未被Child1繼承
// 下面的方法只是繼承了父類構造函數中的屬性
function Child1 () {
// call或apply 在此處執行,改變了this指向,那麼父類在執行的時候,屬性都會掛載到child的類的實例上去
Parent1.call(this)
this.type = 'child1'
}
console.log(new Child1)
// 藉助原型鏈實現繼承
function Parent2 () {
this.name = 'Parent2'
}
function Child2 () {
this.type = 'child2'
}
// 任何函數都有prototype屬性,訪問到它的原型對象
Child2.prototype = new Parent2()
console.log(new Child2())
公用了同一個了父類的實例
// 組合方式
function Parent3 () {
this.name = 'parents'
this.play = [1,2,3]
}
function Child3 () {
Parent3.call(this) // 第一次執行
this.type = 'child3'
}
Child3.prototype = new Parent3() // 第二次執行
var s3 = new Child3()
var s4 = new Child3()
// 組合繼承的優化
function Parent3 () {
this.name = 'parents'
this.play = [1,2,3]
}
function Child3 () {
Parent3.call(this)
this.type = 'child3'
}
Child3.prototype = Parent3.prototype
var s5 = new Child3()
var s6 = new Child3()
cosole.log(s5,s6)
console.log(s5 instanceof Child3, s5 instanceof Parent3)
用constructor來判斷對象是否是原型鏈的實例
// 組合繼承優化2(完美寫法)
function Parent5 () {
this.name = 'parents'
this.play = [1,2,3]
}
function Child5 () {
Parent5.call(this)
this.type = 'child3'
}
Child5.prototype = Object.create(Parent5.prototype) // __proto__
Child5.prototype.constructor = Child5
var s7 = new Child5()
var s6 = new Child3()
console.log(s7 instanceof Child5,s7 instanceof Parent5)
console.log(s7.constructor)
複製代碼
1.對變量提高的理解
2.說明this幾種不一樣的使用場景
3.建立10個<a>
標籤,點擊的時候彈出來對應的序號
4.如何理解做用域
5.實際開發中閉包的應用
執行上下文 this 做用域 做用域鏈 閉包
範圍:一段<script>
或者一個函數
全局: 變量定義、函數聲明
函數: 變量定義、函數聲明、this、arguments
注意: "函數聲明"和"函數表達式"的區別
"函數聲明"是經過function
定義的函數 "函數表達式"是 var fn = function () {}
<. undefined
是整個程序的輸出,與代碼無關
console.log(a) // undefined
var a = 100
fn('zhangsan') // 'zhangsan' 20
function fn(name) { // fn會拿到全局的最上邊
age = 20
console.log(name.age)
var age
}
fu1() // 這種寫法會報錯,由於在執行到下面的時候function纔會被賦值
var fn1 = function () {
age = 20
console.log(name.age)
var age
}
複製代碼
this要在執行時才能確認值,定義時沒法確認
var a = {
name: 'A',
fn: function () {
console.log(this.name)
}
}
a.fn() // this === a
a.fn.call({name: 'B'}) // this === {name: 'B'}
var fn1 = a.fn
fn1() // this === window
// 構造函數
function Foo(name) {
this.name = name
}
var f = new Foo('張三')
// 做爲一個對象的屬性
var obj = {
name: 'A',
printName: function () {
console.log(this.name)
}
}
obj.printName()
// call apply bing
function fn1(name, age) {
console.log('name', name)
console.log(this)
}
fn1.call({x: 100}, '張三', 20)
fn1.apply({x: 200}, ['張三', 20])
var fn2 = function (name, age) {
console.log('name', name)
console.log(this)
}.bind({y: 300})
fn2('張三', 20) // this====> {y: 300}
// this
var a = 'a'
var obj = {
a: 'b',
prop: {
a: 'c',
getA: function () {
return this.a
}
}
}
console.log(obj.prop.getA()) // 'c'
var getA = obj.prop.getA
console.log(getA()) // 'a'
複製代碼
es5
// 無塊級做用域
if (true) {
var name = '張三'
}
console.log(name)
// 函數和全局做用域
var a = 100
function fn() {
var a = 200
console.log('fn', a)
}
console.log('global', a)
fn()
// 做用域鏈
var a = 100
function F1() {
var b = 200
function F2() {
var c = 300
console.log(a) // a 是自由變量
console.log(b) // b 是自由變量
console.log(c)
}
F2()
}
F1()
複製代碼
es6塊級做用域
for(var i = 0; i < 10; i++) {
}
console.log(i) // 10
for (let j = 0; j < 10; j++) {
}
console.log(j) // 報錯 沒法取到j, j is not defined
if (true) {
let name = '李四'
}
console.log(name) // 報錯 name is not defined 塊級做用域
複製代碼
閉包
function F1() {
var a = 100
return function () {
console.log(a)
}
}
// new 出來的是返回值
var f1 = new F1() // function () { console.log(a) }
var a = 200
f1() // 100
一個函數的做用域是它定義時的做用域,而不是執行時的做用域
複製代碼
閉包的使用場景
一、函數做爲返回值
二、函數做爲參數傳遞
function F1() {
var a = 100
return function () {
console.log(a) // a是自由變量,向父做用域去尋找--- 函數**定義**時的父做用域
}
}
var f1 = F1()
function F2(fn) {
var a = 300
fn()
}
F2(f1) // 100
複製代碼
在運行js代碼時,它的運行環境是很是重要的,運行環境多是以下幾種中的一種:全局代碼---首次執行代碼的默認環境;函數代碼---每當執行流程進入函數體時。咱們將執行上下文定義當前代碼的執行環境或做用域。
詞法做用域
function createCounter () {
let counter = 0
const myFunction = function () {
counter = counter + 1
return counter
}
return myFunction
}
const increment = createCounter()
const c1 = increment()
const c2 = increment()
const c3 = increment()
console.log('example increment', c1, c2, c3 ) // 1 2 3
複製代碼
當聲明一個函數式,它包含了一個函數定義和一個閉包。閉包是函數建立時聲明的變量的集合。
任何函數是否都有閉包,包括在全局範圍內建立的函數?答案是肯等的。在全局範圍中建立的函數也會建立一個閉包。但因爲這些函數式在全局範圍內建立的,所以他能夠訪問全局範圍內的全部變量,就無所謂閉包不閉包了。
當一個函數返回另外一個函數時,纔會真正涉及閉包。返回的函數能夠訪問僅存在於其閉包中的變量。
不經意的閉包
let c = 4
const addX = x => n => n + x
const addTree = addX(3)
let d = addTree(c)
console.log(d) // 7
等效代碼以下:
let c = 4
const addX = function (x) {
return function (n) {
return n + x
}
}
const addTree = addX(3)
let d = addTree(c)
console.log(d) // 7
複製代碼
能夠用揹包類比的方式記住閉包。當建立和傳遞一個函數或將其從另外一個函數返回時,這個函數就帶有一個揹包,揹包中包了全部在建立時聲明的變量。在高階函數、react中都很是經常使用
1.同步和異步的區別是什麼?分別舉一個同步和異步的例子
2.一個關於setTimeout
的筆試題
3.前端使用異步的場景有哪些
知識點
一、什麼是異步(對比同步)
二、前端使用異步的場景
三、異步和單線程
// 異步
console.log(100)
setTimeout(function() {
console.log(200)
},1000)
console.log(300)
// 同步
console.log(100)
alert(200)
console.log(300)
複製代碼
同步會阻塞程序的執行,異步會無條件、無阻塞的繼續往下執行
什麼時候須要異步
1.在可能發生等待的狀況
2.等待過程當中不能像alert
同樣阻塞程序運行
3.所以,全部的"等待的狀況"都須要異步
前端使用異步的場景 1.定時任務:setTimeout , setInterval
2.網絡請求:ajax請求,動態加載
3.事件綁定
console.log('start')
var img = document.createElement('img')
img.onload = function () {
console.log('loaded')
}
img.src = '/xxx.png'
console.log('end')
複製代碼
什麼是單線程?
同一時間只能執行一件事情
同步和異步的區別是什麼?
1.同步會阻塞代碼執行,而異步不會
2.alert是同步,setTimeout是異步
console.log(1)
setTimeout(function () {
console.log(2)
},0)
console.log(3)
setTimeout(function () {
console.log(4)
},1000)
console.log(5)
複製代碼
在異步隊列中,先進的會先出
console.log(1)
setTimeout(function () {
console.log(2)
},0)
console.log(3)
setTimeout(function () {
console.log(4)
},0)
console.log(5)
// 1 3 5 2 4
複製代碼
console.log(1)
setTimeout(function () {
console.log(2)
// 2執行完以後,再把這個放到異步隊列中
setTimeout(function() {
console.log(6)
},0)
},0)
console.log(3)
setTimeout(function () {
console.log(4)
},0)
console.log(5)
複製代碼
閱讀underscore.js源碼。閱讀zepto.js源碼。
獲取 2017-06-10 格式的日期
獲取隨機數,要求是長度一致的字符串格式
寫一個能遍歷對象和數組的通用 forEach 函數
知識點: 日期、Math、數組API、對象API
// Date是個構造函數
Date.now() // 獲取當前時間的毫秒數
var dt = new Date()
dt.getTime() // 獲取毫秒數
dt.getFullYear() // 年
dt.getMonth() // 月(0-11)
dt.getDate() // 日(0-31)
dt.getHours() // 小時(0-23)
dt.getMinutes() // 分鐘(0-59)
dt.getSeconds() // 秒(0-59)
複製代碼
Math
Math.random // 獲取隨機數 (0-1)可用於清除緩存
複製代碼
數組API
forEach 遍歷全部元素
every 判斷全部元素是否都符合條件
some 判斷是否至少一個元素符合條件
sort 排序
map 對元素從新組裝,生成新數組
filter 過濾符合條件的元素
var arr = [1,2,3]
arr.forEach(function (item, index) {
// 遍歷數組中的全部元素
console.log( item, index )
})
var result = arr.every(function (item, index) {
// 用來判斷全部的數組元素,都知足一個條件
if (item < 4) {
return true
}
})
console.log(result)
var result = arr.some(function(item, index) {
if (item < 2) {
return true
}
})
console.log(result)
猜想some的內部實現機制,只要有一個是true,就是true
var arr = [1,4,2,3,5]
var arr2 = arr.sort(function(a, b) {
// 從小到大排序
console.log('a',a,'b',b) // 從打印可看出內部真實的執行邏輯
return a - b
// 從大到小排序
// return b - a
})
console.log(arr2)
var arr = [1,2,3,4]
var arr2 = arr.map(function(item, index) {
// 將元素從新組裝,並返回
return `<b>${item}</b>`
})
console.log(arr2) // 返回新的數組
var arr = [1,2,3]
var arr2 = arr.filter(function(item, index) {
// 經過某一個條件過濾數組
if (item >= 2) {
return true
}
})
console.log(arr2) // [2,3]
複製代碼
對象API
var obj = {
x: 100,
y: 200,
z: 300
}
var key
for (key in obj) {
// hasOwnProperty
if (obj.hasOwnProperty(key)) {
console.log(key, obj[key])
}
}
複製代碼
變量類型和計算
原型和原型鏈
閉包和做用域
異步和單線程
其餘(如日期、Math、各類經常使用API)
內置函數: Object Array Boolean String
內置對象: Math JSON
ECMA 262標準:使用來規定一些基礎語法的規則
JS-Web-API: W3C 標準
W3C標準中關於JS的規定有:
DOM操做:將網頁中圖片刪除、增長DOM、添加動態效果、修改網頁結構
BOM操做:獲取瀏覽器特性、獲取當前屏幕的尺寸寬高、獲取當前地址欄地址
事件綁定:click、keyon、keyup、mouseout、moustenter
ajax請求(包括http協議)
存儲
操做DOM的各類方法
document.getElementById(id) // 獲取元素
複製代碼
W3C標準沒有規定任何JS基礎相關的東西:只管定義用於瀏覽器中JS操做頁面的API和全局變量
ECMA管 變量類型、原型、做用域和異步
JS內置的全局函數和對象有哪些? Object、Array、Boolean、String、Math、JSON、window、document、navigator.userAgent
常說的JS(瀏覽器執行的JS)包含兩部分:
JS基礎知識(ECMA262標準):語法、變量類型、計算等
JS-Web-API(W3C標準):DOM BOM ajax 存儲,nodejs沒有W3C標準,由於不是跑在瀏覽器端的
DOM操做:Document、Object、Model 文檔對象模型
DOM是哪一種基本的數據結構? DOM操做的經常使用API有哪些? DOM節點的attr和property的區別?
DOM本質
html是xml特殊類型
樹(類比真實的樹,有不少節點)
瀏覽器拿到html以後須要把html代碼結構化成瀏覽器和js均可以識別的DOM
DOM能夠理解爲:瀏覽器把拿到的html代碼,結構化一個瀏覽器能識別而且js可操做的一個模型而已。
獲取DOM節點
var div1 = document.getElementById('div1') // 元素
var divList = document.getElementsByTagName('div') // 集合
console.log(divList.length)
console.log(divList[0])
var containerList = document.getElementsByClassName('container') // 集合
var pList = document.querySelectorAll('p') // 集合
// property
var pList = document.querySelectorAll('p')
var p = pList[0]
console.log(p.style.width) // 獲取樣式
p.style.width = '100px' // 修改樣式
console.log(p.className) // 獲取 class
p.className = 'p1 main' // 修改 class
// 獲取 nodeName 和 nodeType
console.log(p.nodeName) // nodeName 是p的property
console.log(p.ndoeType)
// Attribute 是獲取html上的屬性,能夠擴展無限的屬性
var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name', 'fruit') // 能夠擴展無限的屬性
p.getAttribute('style')
p.setAttribute('style', 'font-size: 30px;')
複製代碼
DOM的根節點是document,DOM(document object model)
prop是純JS屬性,符合JS語法。而attr是JS經過getAttribute獲取的html上的屬性,即它歸根結底是html屬性,DOM對象上沒有這個屬性。
parentElement和parentNode有什麼區別?這兩個基本是一個意思。parentNode是W3C標準的,儘可能使用這個。前端還在標準化推廣的過程當中,遇到這種模棱兩可的問題很常見。
DOM結構操做
1.新增節點 2.獲取父元素 3.獲取子元素 4.刪除節點
新增節點
var div1 = document.getElementById('div1')
// 添加新節點
var p1 = document.createElement('p')
p1.innerHTML = 'this is p1'
// 添加新建立的元素
div1.appendChild(p1)
// 移動已有節點
var p2 = document.getElenemtById('p2')
div1.appendChild(p2) // 至關於把以前位置的p2節點移動到了div1下面
獲取父元素和子元素
var div1 = document.getElementById('div1')
var parent = div1.parentElement
顯示子節點
var child = div1.childNodes
div1.removeChild(child[0].nodeType) // text 3
div1.removeChild(child[1].nodeType) // p 1
div1.removeChild(child[0].nodeName) // text #text
div1.removeChild(child[1].nodeName) // p1 p
刪除子節點
div1removeChild(child[1])
複製代碼
nodeType有哪幾種類型
www.w3school.com.cn/jsref/prop_…
BOM操做
Browser Object Model
複製代碼
如何檢測瀏覽器的類型
拆解url的各部分
知識點
navigator & screen
// navigator 如何檢測瀏覽器的類型
var ua = navigator.userAgent
// Chrome
// "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
// iphone
// "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1"
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)
// screen
console.log(screen.width)
console.log(screen.height)
// location 拆解url
console.log(location.href)
console.log(location.protocol) // 'http:' 'https:'
console.log(locatin.host) // 域名
console.log(location.pathname) // 'editor/posts'
console.log(location.search)
console.log(location.hash)
// history
history.back()
history.forward()
複製代碼
ssh work@10.340.23.450 登陸
mkdir test 建立test文件夾
ls 看文件夾
ll 看文件夾詳細信息 包括權限
pwd 看當前目錄
cd ../ 返回上級目錄
rm -rf test 刪除test文件夾
vi a.js 編輯文件
cp a.js a1.js 將a.js拷貝爲a1.js
mkdir src
mv a1.js src/a1.js 將a1.js移動到src下
rm a.js 刪除文件(不會進入回收站)
vim a.js 進入vim編輯器
cat a.js 可直接查看文件內容
head a.js 看文件頭部
tail a.js 看文件尾部
head -n 1 a.js 看文件的第一行
tail -n 2 a,js 看尾部兩行
grep '2' a.js 從文件中搜索帶2的內容
複製代碼
從輸入url到獲得html的詳細過程
加載資源的形式
加載一個資源的過程
瀏覽器渲染頁面的過程
1. 輸入url(或跳轉頁面)加載html
2. 瀏覽器根據DNS服務器獲得域名的IP地址(可本身買個域名試一下,機器不識別域名,識別IP)
3. 向這個IP的機器發送http請求
4. 服務器手動、處理並返回http請求
5. 瀏覽器獲得返回內容
6. 根據HTML結構生成DOM Tree
7. 根據 CSS 生成 CSSOM(css放到head中)
8. 將 DOM 和 CSSOM 整合造成 RenderTree
9. 根據 RenderTree 開始渲染和展現
10. 遇到 <script> 時,會執行並阻塞渲染(js有權利改變DOM結構)
複製代碼
window.onload 和 DOMContentLoaded 的區別
window.addEventListener('load', function () {
// 頁面的所有資源加載完纔會執行,包括圖片、視頻等
})
window.addEventListener('DOMContentLoaded', function () {
// DOM 渲染完便可執行,此時圖片、視頻還可能沒有加載完
})
複製代碼
這是一個綜合性的問題
原則
多使用內存、緩存或者其餘方法
減小CPU計算、減小網絡清楚
加載頁面和靜態資源
頁面渲染
靜態資源和壓縮合並
靜態資源緩存
使用CDN讓資源加載更快
使用SSR(server side render)後端渲染,數據直接輸出到HTML中。如今Vue React中都提出了這樣的概念,其實jsp php asp 都屬於後端渲染
渲染優化
css放前面,js放後面
懶加載(圖片懶加載、下拉加載更多)
減小DOM查詢,對DOM查詢作緩存 (DOM操做很是昂貴)
減小DOM操做,多個操做盡可能合併在一塊兒執行
事件節流
儘早執行操做(如 DOMContentLoaded)
複製代碼
<img id="img1" src="preview.png" data-realsrc="abc.png" />
<script type="text/javascript">
var img1 = document.getElementById('img1')
img1.src = img1.getAttribute('data-realsrc')
</script>
複製代碼
緩存DOM查詢
// 未緩存DOM查詢
var i
for (i = 0; i < document.getElementsByTagName('p').length; i++ ) {
// todo
}
// 緩存了 DOM 查詢
var pList = document.getElementByTagName('p')
var i
for (i = 0; i < pList.length; i++) {
// todo
}
複製代碼
合併DOM插入
var listNode = document.getElementById('list')
// 插入 10 個 li 標籤
var frag = document.createDocumentFragment() // 臨時片斷,不會觸發DOM操做
var x, li
for(x = 0; x < 10; x++) {
li = document.createElement("li")
li.innerHTML = "List item" + x
frag.appendChild(li)
}
listNode.appendChild(frag)
複製代碼
事件節流
var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', fucntion() {
if (timeoutId) { // timeoutId 存在的時候直接清除
clearTimeout(timeoutId)
}
timeoutId = setTimeout(function () {
// 停留100ms以上觸發 change 事件
}, 100)
}
複製代碼
XSS
XSS 跨站請求攻擊
在博客寫一篇文章,同時偷偷插入一段<script>(script標籤支持跨域)
攻擊代碼中,獲取cookie,發送到本身的服務器
發佈博客,有人查看博客內容
會把查看者的 cookie 發送到攻擊者的服務器
預防
前端替換關鍵字,例如替換 < 爲 < > 爲 >
後端替換
複製代碼
CSRF 跨站請求僞造
1.你已登陸一個購物網站,正在瀏覽商品
2.該網站付費接口是 xxx.com/pay?id=100 可是沒有任何驗證
3.而後你收到一封郵件,隱藏着 <img src=xxx.com/pay?id=100>
4.你查看郵件的時候,就已經悄悄的付費購買了
複製代碼
解決方案
增長驗證流程,如輸入指紋、密碼、短信驗證碼
複製代碼
let statusText = {'loading': 'COMING SOON'}
const defaultStatusText = {
'loading': 'LOADING',
'noFollow': 'FAILED',
'hasFollow': 'SUCCESS',
...(statusText || {})
}
複製代碼
增量替換後的結果
const defaultStatusText = {
'loading': 'COMING SOON',
'noFollow': 'FAILED',
'hasFollow': 'SUCCESS'
}
複製代碼
二十8、String對象知多少
substr、substring、split、chartAt、concat、indexOf、lastIndexOf、tolowerCase、toUpperCase、search、replace、match
複製代碼
var a ="He quoted the bible to her"
a.substr(3,4) // quot
a.substring(3,4) // q
複製代碼
var b = "reference"
b.split('') // ["r", "e", "f", "e", "r", "e", "n", "c", "e"]
var c = hello,world
c.split(',') // ["hello", "world"]
複製代碼
附: slice(數組、字符串)、splice(用於數組)、split(字符串)傻傻分不清 www.jianshu.com/p/c5e67f4b9…
var b = "reference"
b.charAt(4) // "r"
複製代碼
無鏈接:鏈接一次就會斷掉
無狀態:沒法區分兩次http請求是否爲同一個身份
簡單快速
靈活
複製代碼
請求報文
請求行( GET / HTTP/1.1)、請求頭 (KEY VALUE)、空行、請求體 (數據部分)
響應報文
狀態行、響應頭、空行、響應體
複製代碼
GET ---> 獲取資源
POST ---> 傳輸資源
PUT ---> 更新資源
DELETE ---> 刪除資源(業務類型通常不刪除資源)
HEAD ---> 得到報文首部
複製代碼
*GET在瀏覽器回退時是無害的,而POST會再次提交請求
GET產生的URL地址能夠被收藏,而POST不能夠
*GET請求會被瀏覽器主動緩存,而POST不會,除非手動設置
GET請求只能進行url編碼,而POST支持多種編碼方式
*GET請求參數會被完整保留在瀏覽器歷史記錄裏,而POST中的參數不會被保留
*GET請求在URL中傳送的參數是有長度限制的,而POST沒有限制
對參數的數據類型,GET只接受ASCII字符,而POST沒有限制
GET比POST更不安全,由於參數直接暴露在URL上,因此不能用來傳遞敏感信息
*GET參數經過URL傳遞,POST放在Request body中
複製代碼
1xx : 指示信息 -- 表示請求已接收,繼續處理
2xx : 成功-- 表示請求已被成功接收
3xx : 重定向 -- 要完成請求必須進行進一步的操做
4xx :客戶端錯誤 -- 請求有語法錯誤或請求沒法實現
5xx : 服務器錯誤 -- 服務器未能實現合法的請求
200 ok: 客戶端請求成功
206 Partial Content: 客戶發送了一個帶有Range頭的GET請求,服務器完成了他它(通常用於音頻等)
301 Moved Permanently: 永久重定向,請求的頁面已經轉移至新的url
302: 臨時重定向
304: 瀏覽器已經有緩存
400:客戶端請求有語法錯誤,不能被服務器理解
401:請求未經受權
403: 禁止訪問請求頁面
404:請求資源不存在
500: 服務器發生不可預期的錯誤原來緩衝的文檔還能夠繼續使用
503: 請求未完成,服務器零食過載或宕機,一段時間後可能恢復正常
複製代碼
HTTP協議採用「請求-應答」模式,當使用普通模式,即非keep-Alive模式時,每一個請求/應答客戶和服務器都要新建一個鏈接,完成以後當即斷開鏈接(HTTP協議爲無鏈接的協議,無鏈接的協議上面說過)
當使用Keep-Alive模式(又稱爲持久鏈接、鏈接重用)時,Keep-Alive功能使客戶端到服務器端的鏈接持續有效,當出現對服務器的後續請求時,Keep-Alive功能避免了創建或從新創建鏈接(HTTP1.1版本才支持,1.0版本不支持)
複製代碼
在持久鏈接的狀況下,某個鏈接上消息的傳遞相似於
請求1 -> 響應1 -> 請求2 -> 響應2 -> 請求3 -> 響應3
某個鏈接上的消息變成了相似這樣(多個請求一次打包發送,一次打包返回,實在持久鏈接下完成的)
請求1 -> 請求2 -> 請求3 -> 響應1 -> 響應2 -> 響應3
管線化
管線化機制經過持久鏈接完成,僅HTTP/1.1 支持此技術
只有GET和HEAD請求能夠進行管線化,而POST則有所限制
初次建立鏈接時不該啓動管線機制,由於對方(服務器)不必定支持HTTP/1.1版本的協議
未完待續...
複製代碼
什麼是同源策略及限制
同源策略限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。包含協議、域名、端口。這是一個用於隔離潛在惡意文件的關鍵的安全機制。
先後端如何通訊
1. 聲明對象 兼容
2. get post 支持
3. open()肯定發送方式
4. 發送
5. xhr.onload 響應
複製代碼
原理:script標籤的異步加載,執行回調函數
<script src="http://www.xxx.com?callback=jsonp"></script>
<script>
jsonp({
data: {}
})
</script>
在出現postMessage和CORS以前一直用JSONP來作跨域通訊
複製代碼
url中的#
Hash改變不會刷新頁面
search改變會刷新頁面
利用hash場景:當前頁面A 經過iframe或frame嵌入了跨域的頁面B
var data = window.location.hash
複製代碼
H5 新增
窗口A(http:A.com)向跨域的窗口B(http://B.com)發送信息
window.postMessage('data','http://B.com') // 向跨域窗口B發出請求,'data'表明你要發送的數據
在窗口B中監聽
window.addEventListener('message', function (event) {
console.log(event.origin) // http://A.com
console.log(event.source) // Awindow
console.log(event.data) // data
},false)
// message事件和keyup keydown相似
複製代碼
參考資料:http://www.ruanyifeng.com/blog/2017/05/websocket.html
不受同源策略的限制
var ws = new webSocket('wss://echo.websocket.org')
ws.onopen = function (evt) {
console.log('Connection open ...')
ws.send('Hello WebSockets')
}
ws.onmessage = function (evt) {
console.log('Received Message' + evt.data)
ws.close()
}
ws.onclose = function (evt) {
console.log('Connection closed')
}
複製代碼
參考資料:http:// www.ruanyifeng.com/blog/2016/04/cors.html
是ajax的一個變種
fetch('/some/url', {
method: 'get'
}).then(function (response) {
}).catch(function (err) {
// 出錯了,等價於then的第二個參數,但這樣更好用更直觀
})
複製代碼
ES2017 標準引入了 async 函數,使得異步操做變得更加方便。
async 函數是什麼?一句話,它就是 Generator 函數的語法糖。
async函數對 Generator 函數的改進,體如今如下四點。
(1)內置執行器。
Generator 函數的執行必須靠執行器,因此纔有了co模塊,而async函數自帶執行器。也就是說,async函數的執行,與普通函數如出一轍,只要一行。
asyncReadFile();
複製代碼
上面的代碼調用了asyncReadFile
函數,而後它就會自動執行,輸出最後結果。這徹底不像 Generator
函數,須要調用next
方法,或者用co
模塊,才能真正執行,獲得最後結果。
(2)更好的語義。
async
和await
,比起星號和yield
,語義更清楚了。async
表示函數裏有異步操做,await
表示緊跟在後面的表達式須要等待結果。
(3)更廣的適用性。
co
模塊約定,yield
命令後面只能是 Thunk
函數或 Promise
對象,而async
函數的await
命令後面,能夠是 Promise
對象和原始類型的值(數值、字符串和布爾值,但這時會自動轉成當即 resolved
的 Promise
對象)。
(4)返回值是 Promise。
async
函數的返回值是 Promise
對象,這比Generator
函數的返回值是 Iterator
對象方便多了。你能夠用then
方法指定下一步的操做。
進一步說,async
函數徹底能夠看做多個異步操做,包裝成的一個 Promise
對象,而await
命令就是內部then
命令的語法糖。
async
函數的語法規則整體上比較簡單,難點是錯誤處理機制。
async
函數內部return
語句返回的值,會成爲then
方法回調函數的參數。
若是有多個await命令,能夠統一放在try...catch
結構中。
async function main() {
try {
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3);
}
catch (err) {
console.error(err);
}
}
複製代碼
async 函數的實現原理
async 函數的實現原理,就是將 Generator 函數和自動執行器,包裝在一個函數裏。
async function fn(args) {
// ...
}
// 等同於
function fn(args) {
return spawn(function* () {
// ...
});
}
複製代碼
全部的async
函數均可以寫成上面的第二種形式,其中的spawn
函數就是自動執行器。
二十三:this知多少
單例的代碼寫法
var a = {
fa: () => {
console.log(this)
},
fb: function () {
console.log(this)
}
}
a.fa() // window
a.fb() // a
箭頭函數裏面根本沒有本身的this,而是引用外層的this。
複製代碼
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
複製代碼
上面代碼中,cat.jumps()方法是一個箭頭函數,這是錯誤的。調用cat.jumps()時,若是是普通函數,該方法內部的this指向cat;若是寫成上面那樣的箭頭函數,使得this指向全局對象,所以不會獲得預期結果。這是由於對象不構成單獨的做用域,致使jumps箭頭函數定義時的做用域就是全局做用域。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
複製代碼
上面代碼運行時,點擊按鈕會報錯,由於button的監聽函數是一個箭頭函數,致使裏面的this就是全局對象。若是改爲普通函數,this就會動態指向被點擊的按鈕對象。
var a = 1
function fn(f) {
var a = 2
return f
}
function bar() {
console.log(a)
}
var f1 = fn(bar)
f1() // 1
複製代碼
var a = 1
function fn(f) {
var a = 2
return function () {
console.log(a)
}
}
var f1 = fn()
f1() // 2
複製代碼
Event Loop
即事件循環,是指瀏覽器或Node
的一種解決JavaScript
單線程運行時不會阻塞的一種機制,也就是咱們常用異步的原理。
弄懂Event Loop的必要性
JavaScript
的運行機制。堆 棧 隊列
堆是一種數據結構,是利用徹底二叉樹維護的一組數據,堆分爲兩種,一種爲最大堆,一種爲最小堆,將根節點最大的堆叫作最大堆或大根堆,根節點最小的堆叫作最小堆或小根堆。 堆是線性數據結構,至關於一維數組,有惟一後繼。
棧(Stack)
後進先出
棧在計算機科學中是限定僅在表尾進行插入或刪除操做的線性表。 棧是一種數據結構,它按照後進先出的原則存儲數據,先進入的數據被壓入棧底,最後的數據在棧頂,須要讀數據的時候從棧頂開始彈出數據。
棧是隻能在某一端插入和刪除的特殊線性表。
隊列(Queue)
先進先出
特殊之處在於它只容許在表的前端(front)進行刪除操做,而在表的後端(rear)進行插入操做,和棧同樣,隊列是一種操做受限制的線性表。
進行插入操做的端稱爲隊尾,進行刪除操做的端稱爲隊頭。 隊列中沒有元素時,稱爲空隊列。
隊列的數據元素又稱爲隊列元素。在隊列中插入一個隊列元素稱爲入隊,從隊列中刪除一個隊列元素稱爲出隊。由於隊列只容許在一端插入,在另外一端刪除,因此只有最先進入隊列的元素才能最早從列中刪除,故隊列又稱爲先進先出(FIFO—first in first out
)
Event Loop
在JavaScript
,任務被分爲兩種,一種宏任務(MacroTask
)也叫Task
,一種叫微笑任務(MicroTask
)。
MacroTask(宏任務)
script
所有代碼、setTimeout
、setInterval
、setImmediate
(瀏覽器暫時不支持,值有IE10支持)MicroTask(微任務)
Process.nextTick(Node獨有)
、Promise
、MutationObserver
瀏覽器中的Event Loop
JavaScript
有一個main thread
主線程和 call-stack
調用棧(執行棧),全部的任務都會被放到調用棧等待主線程執行。
JS調用棧
JS調用棧採用的是後進先出的規則,當函數執行的時候,會被添加到棧的頂部,當執行完成後,就會從棧頂移出,知道棧內被清空。
同步任務和異步任務
JavaScript
setTimeout(() => {
console.log('timer 1')
Promise.resolve().then(() => {
conosle.log('promise 1')
})
})
setTimeout(() => {
console.log('timer 2')
Promise.resolve().then(() => {
conosle.log('promise 2')
})
})
// timer 1
// promise 1
// timer 2
// promise 2
複製代碼
console.log('script start')
async function async1 () {
await async2()
console.log('async1 end')
}
async function async2 () {
console.log('async2 end')
}
async1()
setTimeout(function () {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
}).then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
console.log('script end')
// script start
// async2 end
// Promise
// script end
// async1 end
// promise1
// promise2
// setTimeout
複製代碼
async/await
在底層轉換成了Promise
和then
回調函數,是Promise
的語法糖。 每次咱們使用await
,解釋器都建立了一個promise
對象,而後把剩下的async
函數中的操做放到then
回調函數中。async/await
的實現離不開Promise
。從字面意思來理解,async
是'異步'的簡寫,而await
是async wait
的簡寫能夠認爲是等待異步方法執行完成。
let a = {p1: 1,p2: 2}
let b = 'm' + a
在命令行中打印,你會發現b
變成了 "m[object Object]"
,這並非咱們想要的結果。
這就涉及到了**隱式轉換**:
1. 除了`null`和`undefined`以外,其餘的類型(數值、布爾、字符串、對象)都有`toString()`方法,它返回相應值的字符串表示形式。
2. 每一個對象都有一個`toString()`方法。
3. 當該對象被表示爲一個文本值時,或者一個對象以預期的字符串方式引用時自動調用。
4. 默認狀況下,`toString()`方法被每一個`Object`對象繼承。若是此方法在自定義對象中未被覆蓋,toString()返回 "[object type]",其中type是對象的類型。
複製代碼
所以,a
是咱們自定義的對象,當咱們的代碼中有+
運算符時就會調用toString()
,將其它類型的值轉化爲字符串,再進行字符串的拼接。因此,a.toString()
的結果爲[object Object]
,因此'm' + a
的結果爲"m[object Object]"
。
解決方法:
let b = 'm' + JSON.stringify(a)
(1).toString() // "1"
[1,2].toString() // "1, 2"
({}).toString() // [object Object] 不能直接寫成{}.toString()是由於:{}會被當成代碼塊而不是空對象
true.toString() // "true"
null.toString() // Uncaught TypeError: Cannot read property 'toString' of null
undefined.toString() // Uncaught TypeError: Cannot read property 'toString' of null
複製代碼
常常會遇到寫倒計時的需求。可是倒計時會遇到一個問題,好比還有1小時30分鐘6秒,咱們用數字顯示就是01:30:06
,這個時候就會遇到一個數字補全的問題須要咱們處理。
方法1:
使用padStart(),固然,與之對應的還有padEnd()
方法。
let a = 6
let second = a.toString().padStart(2, '0') // 目標長度爲2位,不知足則從左側開始添加'0',所以結果爲 '06'
複製代碼
方法2:
slice() 方法用於數組和字符串,此處用的是在字符串中的方法。在字符串中slice()
方法提取某個字符串的一部分,並返回一個新的字符串,且不會改動原字符串。
這裏只介紹第一個參數
beginIndex
從該索引(以 0 爲基數)處開始提取原字符串中的字符。若是值爲負數,會被當作 strLength + beginIndex
看待,這裏的strLength
是字符串的長度(例如, 若是 beginIndex
是 -3 則看做是:strLength - 3
此處就利用了beginIndex
參數爲負數的特性
let a = 6
let second = ('0' + a).slice(-2) // 結果爲 '06'
若是爲 36, 則 '0' + 36 = '036',從起始位置-2截取以後的結果仍爲 '36'
複製代碼
方法3:
此種方法比較low,用判斷大小來解決
let a = 6
let secoud = a < 10 ? '0' + a : a // '06'
複製代碼
49-34.3=14.700000000000003
複製代碼
circle.js
輸出兩個方法area
和circumference
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
複製代碼
加載模塊的兩種方法:
方法一:逐一加載
// main.js
import { area, circumference } from './circle';
console.log('圓面積:' + area(4));
console.log('圓周長:' + circumference(14));
複製代碼
方法二:總體加載
import * as circle from './circle';
console.log('圓面積:' + circle.area(4));
console.log('圓周長:' + circle.circumference(14));
複製代碼