開發過程中遇到的js知識點總結,面試題等,持續更新
1.Object.freeze()
方法用於凍結一個對象,即將對象設置為不可擴展、將對象的所有自有的屬性和方法(包括Symbol值的屬性和方法)配置為不可配置,不可寫。
Object.freeze(obj)
返回值是已凍結的對象。
這個操作不會影響從原型對象繼承來的屬性和方法,即隻影響自有的屬性和方法,一旦對象被凍結,其自身的所有屬性都不可能以任何方式被修改。
2.深度選擇器 >>> 與 /deep/
.fuck >>> .weui-cells { // ... }
有些想sass、less、scss之類的預處理器無法正確解析可以使用 /deep/ 取而代之,/deep/是 >>> 的別名
vue組件中,在style設置為scoped的時候,裡面在寫樣式對子組件是不生效的,如果想讓某些樣式對子組件都生效,可以使用/deep/深度選擇器
.wrap{ /deep/ .class{ font-size: 12px;// 對所有子組件生效 /deep/ .class2{};// 沒有必要寫多層deep,父類有deep,子類自動也會深度選擇,在firfox里會失效 } }
3.在循環中使用break、continue和return語句之間的區別
break:此語句將退出循環執行循環後面的語句。如果break語句包含在嵌套循環里,它只跳出最裡面的循環。
continue:程式運行到此語句時,不再執行循環體中continue後面的語句,而是跳到下一個循環入口執行下一個循環
return:表示被調函數返回到主調函數繼續執行,返回時可附帶一個返回值,由return後面的參數指定。return之後,函數就結束了,後面的語句不再執行
4.JS中數組函數的總結
push():像數組的末尾添加一個或多個元素,並返回新的長度
pop():刪除數組的最後一個元素,數組的長度減1,並且返回刪除元素的值(當數組為空的時候,不改變數組,並返回undefined)
unshift():向數組的頭部添加一個或多個元素,返回新的長度
shift():刪除並返回數組的第一個元素
reverse():倒敘
sort():對數組元素進行排序(默認按照ASCLL),可以傳遞一個函數,自定義排序規則
concat():連接兩個或多個數組
split(): 把字元串轉換成數組
join():把所有的元素放進一個字元串,通過指定的分隔符進行分割
splice(index, howmany, item1, …, itemX):可以做 刪除/插入/替換 操作(替換就是先刪除再添加,響應式的)
index 必需,規定添加刪除項目的位置,使用負數可從數組結尾處規定位置
howmany 必需,要刪除的項目數量,如果設置為 0,則不會刪除項目
item, …, itemX 可選,像數組添加的新的元素
substr(start, length)
substring(start, end)
slice(start, end):截取。從某個已有的數組返回選定的元素,不包含 end 元素(沒有 end 參數,就到數組的結尾)
indexOf(x, start):返回某個指定字元串再字元串中首次出現的位置
x 必需,規定檢索的字元串
start 可選,規定在字元串中開始檢索的位置,合法值是 0 至 arr.length – 1,如果省略該值,則從字元串的首字元開始檢索
lastIndexOf(x, fromindex):指定字元串值,最後出現的位置,在一個字元串中的指定位置從後向前搜索
x 必需,規定檢索的字元串
fromindex 可選,規定字元串開始檢索的位置,合法值 0 至 arr.length -1 ,若省略,則將從字元串最後一個字元處開始檢索
toString():轉換為字元串
valueOf():返回數組的原始值,返回數組本身
includes(x, start):判斷字元串或數組中是否包含指定的字元串,返回Boolean
x 必需,要查找的數據
start 可選,設置從那個位置開始查找,默認0
find(function(currentValue, index, arr), thisValue):返回通過判斷的數組的第一個元素的值,不會對空數組執行
currentValue 必需,當前元素
index 可選,當前元素的索引值
arr 可選,當前數組對象
thisValue 可選,傳遞給函數的值一般用 this 值
instanceof:檢測是否是數組
arr instanceof Array;// 返回Boolean
Array.isArray(obj):判斷obj是否是數組,返回Boolean
JS中數組高階函數的理解 some()、every()、fiflter()、map()、forEach()、reduce()
some((item, index, arr)=>{}):返回一個Boolean,判斷是否有元素符合func中的條件
item 必選,index 可選,索引值。arr 當前數組,可選
不會對空數組進行檢測,不會改變原始數組
const arr = [1,2,3,4,5]; arr.some(item=>item>2);// 返回true
every((item,index,arr)=>{}):返回一個Boolean,判斷每個元素是否符合func中的條件
item必選,index 可選,索引值。arr當前數組,可選
不會對空數組進行檢測,不會改變原始數組
const arr = [2,3,4,5,6]; arr.every(item=>item>2);// 返回false
filter((item,index,arr)=>{}):返回一個符合func條件的元素數組(es6)
item必選,index 可選,索引值。arr當前數組,可選
filter() 不會對空數組進行檢測。不會改變原始數組
let arr = [2,3,4,5]; arr.filter(item=>item>2);// 返回 [3,4,5]
map((item, index, arr)=>{}):返回一個新的array,數組元素由每一次調用函數產生結果組成
item必選,index 可選,索引值。arr當前數組,可選
map() 不會對空數組進行檢測,不會改變原始數組
const arr = [1,2,3,4,5]; arr.map(item=>item*2);// 返回 [2,4,6,8,10]
forEach((item, index, arr)=>{}):調用數組的每個元素,並將元素傳遞給回調函數(沒有返回值,將數組遍歷)
item 參數是必須的,當前元素。index 可選,當前索引值。arr 可選,當前元素所屬的數組對象
forEach() 對於空數組是不會執行回調函數的
const a = [1, 22, 333] a.forEach((item, index, arr)=>{ console.log(item);// 1 22 333 console.log(index);// 0 1 2 console.log(arr);// arr 為遍歷的數組 })
reduce():接收一個函數作為累加器,數組中的每個值(從左到右)開始縮減,最終計算為一個值
可以作為一個高階函數,用於函數的 compose ,對於空數組是不會執行回調函數的
let arr = [{count: 1, price: 2},{count: 3, price: 3}, {count: 2, price: 2}]; arr.reduce(function(total, currentValue, index, arr){}, initialValue)// initialValue 可選,傳遞給函數的初始值 // 參數 total 必需,初始值,或者計算結束後的返回值。currentValue必需,當前元素。index 可選,索引。arr 可選,數組對象 let sum = arr.reduce(function(total, item){ return total + item.count * item.price; },0)// 最終返回15
5.jquery獲取radio, checkbox, checked 的值的時候不能使用attr
要使用prop進行獲取和設置值
獲取選中的屬性
$("#id").prop('checked'); $("#id").get(0).checked document.getElementById("#id").checked $("#id").is(":checked")
設置選中
$("#id").prop('checked', true); $("#id").get(0).checked = true; document.getElementById("#id").checked = true;
6.多個非同步請求成功後在執行後續方法
1–jquery 中 $.when() 方法
$.when($.ajax(), $.ajax()).done(function(a1, a2){ // a1是第一個非同步返回的數據,a2是第二個非同步返回的數據 }).file(function(){ alert('file') })
// 以上程式碼是先執行兩個操作$.ajax,如果都成功了,就運行done(),如果有一個或都失敗了就執行fail()
$.when(deferreds)
Deferred 類型 一個或多個延遲對象,或者普通的js對象
2–使用promise中的all()方法實現
Promise.all([ new Promise(resolve => { setTimeout(() => { resolve('100') }, 1000) }), new Promise(resolve => { setTimeout(() => { resolve('200') }, 1000) }) ]).then(data => { console.log('5555') })
7.word-wrap: break-word;
允許長單詞換行到下一行
8.數組引用的時候,直接改變數據會影響原數據的值
let a = [1,2,3,4,5]; let b = a; b[2] = 22;// 此時a數組也是[1,2,22,4,5] 要想不改變原始數據的值, let c = JSON.parse(JSON.stringify(a)) c[2] = 2222;// 此時改變的只是c中的值
9.js淺拷貝與深拷貝的區別和實現
區別:假設 B 複製了 A ,當修改 A 時,看 B 是否會發生改變,如果 B 變了,說明是淺拷貝,若 B 沒有改變,那就是深拷貝
1.如果是基本數據類型,名字和值都會儲存在棧記憶體中
let a = 1; let b = a;// 棧記憶體會開闢一個新的記憶體空間,此時 a 和 b 都是相互獨立的 b=2;//此時a還是1
以上算不上深拷貝,因為深拷貝本身只針對比較複雜的 object 類型數據
2.如果是引用類型,名字存在棧記憶體中,值存在堆的記憶體中,但是棧記憶體會提供一個引用的地址指向堆記憶體中的值
let a = [1,2,3] let b = a;// 此時進行拷貝,其實複製的是 a 的引用地址,而並非堆裡面的值 a[1] = 222;// 在修改數組時,由於 a 與 b 指向的時同一個地址,所以 b 也跟著相應的改變,這就是淺拷貝
3.實現淺拷貝的方法
1.for…in 只循環第一層
// 只複製第一層的淺拷貝 function simpleCopy(obj1) { var obj2 = Array.isArray(obj1) ? [] : {}; for (let i in obj1) { obj2[i] = obj1[i]; } return obj2; } var obj1 = { a: 1, b: 2, c: { d: 3 } } var obj2 = simpleCopy(obj1); obj2.a = 3; obj2.c.d = 4; alert(obj1.a); // 1 alert(obj2.a); // 3 alert(obj1.c.d); // 4 alert(obj2.c.d); // 4
2.Object.assign方法
var obj = { a: 1, b: 2 } var obj1 = Object.assign(obj); obj1.a = 3; console.log(obj.a) // 3
3.使用等於號 =
let a = [0, 1, 2, 3, 4];
let b = a; console.log(a === b); a[0] = 1; console.log(a, b);
4.實現深拷貝的方法
1.使用遞歸去拷貝所有層級的屬性
let a = [1, 2, 3, 4, 5]; let b = deepClone(a); function deepClone(obj){ let objClone = Array.isArray(obj) ? [] : {}; if (obj && typeof obj === "object") { for(key in obj) { // 判斷對象是否包含特定的自身屬性,返回Boolean if(obj.hasOwnProperty(key)) { // 判斷 obj 子元素是否為對象,如果是,遞歸複製 if(obj[key] && typeof obj[key] === 'object') { objClone[key] = deepClone(obe[key]) } else { objClone[key] = obj[key] } } } } return objClone; }
2.通過 JSON 對象來實現深拷貝
// 缺點:無法實現對對象中方法的拷貝,會顯示為 undefined let a = [1,2,3,4,5]; let b = JSON.parse(JSON.stringify(a));
3.通過 jQuery 的 extend 方法實現深拷貝
let arr = [1,2,3,4]; let bbb = $.extend(true, [], array);// true 為深拷貝,false為淺拷貝
4.可以通過 lodash 函數庫實現深拷貝
let res = _.clone(arr);
5.Reflect方法
// 代理法 function deepClone(obj) { if(!isObject(obj)){// 判斷是否為對象 throw new Error('obj 不是一個對象') } let isArray = Array.isArray(obj); let cloneObj = isArray ? [...obj] : {...obj}; Reflect.ownKeys(cloneObj).forEach(key => { cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key] }) return cloneObj; }
6.手動實現深拷貝
let obj = {a: 1, b: 2};
let obj2 = {a: obj.a, b: obj.b};
7. 如果對象的 value 時基本類型的話,也可以用 Object.assign 來實現深拷貝,但是要把它賦值給一個空對象
var obj = {a: 1, b: 2} var obj1 = Object.assign({}, obj);// obj賦值給一個空{}
8.使用 slice 實現對數組的深拷貝
// 但數組裡面的值是基本數據類型,比如 String,Number,Boolean 時,屬於深拷貝 // 當數組裡面的值時引用數據類型比如 Object, Array時,屬於淺拷貝 let a = [1,2,3]; let b = a.slice(0);
9.用 concat 實現對數組的深拷貝
// 當數組裡面的值是基本數據類型,比如String, Number, Boolean時,屬於深拷貝 let a = [1,2,3]; let b = a.concat(); // 當數組裡面的值是引用數據類型,比如Object,Array時,屬於淺拷貝 let a = [{a: 1},{b: 2}]; let b = a.concat;
10.使用 let newObj = Object.create(oldObj) 可以達到深拷貝的效果
function deepClone(initalObj, finalObj){ var obj = finalObj || {}; for(var i in initalObj) { var prop = initalObj[i];// 難免相互引用對象導致死循環,比如initalObj.a = initalObj if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj; }
11.使用擴展運算符實現深拷貝
// 當value是基本數據類型,比如String,Number,Boolean時,是可以使用拓展運算符進行深拷貝的 // 當value是引用類型的值,比如Object,Array,引用類型進行深拷貝也只是拷貝了引用地址,所以屬於淺拷貝 var car = {sf: "james", pf: "davis", cf: "cf"} var car1 = { ...car, pf: "kobe" } console.log(car1); // { sf: "james", pf: "davis", cf: "cf" } console.log(car); // { sf: "james", pf: "kobe", cf: "cf" }
10.用on給動態添加的元素綁定hover事件,不會生效
在jQuery中,hover() 函數本身是對 mouseenter && mouseleave 的封裝,然而在原生 event 中,並沒有 hover 這一事件,所以在傳遞參數 hover 時,並不會有任何的效果。
如果我想綁定類似 hover 的效果,可以綁定兩個事件 mouseenter && mouseleave + function ,樣式就用類名來 toggle, 也可以在map裡面,一個事件對應一個function
$('ul.course').on('mouseenter mouseleave', 'li', function(){ $(this).toggleclass('borderClass');// 滑鼠移入移出改變 class })
11.JSON.stringify() 和 JSON.parse()
JSON.stringify({'a': '1', 'b': '2'}); // '{'a': '1', 'b': '2'}' 從一個對象中解析出字元串
JSON.parse('{'a': '1', 'b': '2'}'); // Object{a:'1', 'b': '2'} 從一個字元串中解析出JSON對象
12.for…in 與 for…of 的區別
對於數組的遍歷,for…in 會返回數組中所有可枚舉的屬性(包括原型鏈上可枚舉的屬性),for…of 只返回數組的下標對應的屬性值
for…of 循環的原理其實也是利用了遍歷對象內部的 iterator 介面,將 for…of 循環分解成最原始的 for 循環,內部實現的機制可以這麼理解
// 1.for...of 遍歷獲取的是對象的鍵值,for...in 獲取的是對象的鍵名 let arr = ['one', 'two', 'three']; for(let i in arr){ console.log(i);// 分別輸出 0 1 2 } for(let i of arr){ console.log(i);// 分別輸出 one two three } // 2.對於普通對象,沒有部署原生的 iterator 介面,直接使用 for...of 會報錯 let obj ={name: 'zhangning187', age: '24'} for(let key of obj) { console.log(key);// obj is not iterable } // 可以使用 for...in 循環遍歷鍵名 for(let key in obj) { console.log(key);// name age } // 也可以使用 Object.keys(obj) 方法將對象的鍵名生成一個數組,然後遍歷這個數組 for(let key of Object.keys(obj)) { console.log(key);// name age } // 3.for...in 循環不僅遍曆數字鍵名,還會遍歷手動添加的其他鍵,甚至包括原型鏈上的鍵。for...of則不會這樣 let array = [1, 2, 3]; arr.name = 'zhangning187';// 手動添加的鍵 Array.prototype.age = '23';// 原型鏈上的鍵 for(let i in arr) { console.log(i);// 1 2 3 name age } // 4.forEach 循環無法中途跳出,break 命令或 return 命令都不能奏效 let arr1 = [1, 2, 3, 4] arr.forEach(i=>{ if(i == 2) return;// 它會像 continue 一樣,跳過當前循環繼續執行下一次循環 console.log(i);// 1 3 4 }) for...of 循環可以與 break、continue、return 配合使用,跳出循環 // 5.無論是 for...in 還是 for...of 都不能遍歷出 Symbol 類型的值,遍歷 Symbol 類型的值需要用Object.getOwnPropertySymbols()方法 let a = Symbol('a') let b = Symbol('b') let obj = { [a]: 'zhang', [b]: 'ning', c: '23', d: 'web' } for(let key in obj) { console.log(key);// c d } let objSymbols = Object.getOwnPropertySymbols(obj) console.log(objSymbols);// [Symbol(a), Symbol(b)] objSymbols.forEach(i=>{ console.log(obj[i]);// zhang ning })
// Reflect.ownKeys 方法可以返回所有類型的鍵名,包括常規鍵名和 Symbol 鍵名 let keyArr = Reflect.ownKeys(obj) console.log(keyArr);// ["c", "d", Symbol(a), Symbol(b)]
for…in 循環主要是為了遍歷對象,不適用於遍曆數組
for…of 循環可以用來遍曆數組、類數組對象、字元串、Set、Map、Generator 對象
13.vue過濾器中的this值
過濾器中的this值為undefined
14.$nextTick()
在下次DOM更新循環結束之後執行延遲回調,在修改數據之後立即使用這個方法,獲取更新後的DOM
在 created() 鉤子函數進行的DOM操作,一定要放在this.$nextTick() 回調函數中,原因是在 create() 鉤子函數執行的時候 DOM 其實並未進行任何渲染,而此時進行DOM操作無異於徒勞,所以放在nextTice()回調函數中。與之對應的就是 mounted() 鉤子函數,該鉤子函數執行的時候,所有的 DOM 掛載已完成。