談談數組Array在項目中常見的場景
- 求和,求最大(小)值,排序,多個數組合並(concat),去重,分割,find,indexOf,filter,join, toString等等
- 數組的concat,join,slice,toString方法不會改變原數組
- 數組的splice,push,pop,unshift,shift,sort,reverse方法會改變原數組
類數組怎麼轉化爲數組
什麼是類數組,就是屬性要爲索引(數字)屬性,同時又必須有一個length屬性來控制對象邊界的一個特殊對象,特點:
1.不能使用數組的方法,他們不能使用Array的方法
2.擁有length屬性,可以使用下標來訪問元素,這兩點和數組相同。
var obj = {
'0': 'a',
'1': 'b',
'2': 'c',
'length': 3
}
轉化爲數組的方法有3種:
- 第一種,採用es6中的新方法Array.from()
var myArray = ["value1", "value2", "value3"];
var mySet = new Set(myArray);
console.log(Array.from(mySet)); //第一種方法Array.from()
console.log([...mySet]); //第二種方法Array.from()
-
第二種,創建新數組,通過forEach遍歷把類數組的元素添加到新數組中
-
第三種,通過Array.prototype.slice.call(arrayLike, 0)或者[].slice.call(arrayLike, 0)將類數組對象傳入即可
-
第四種,擴展運算符
...
[...arguments]
// 將參數轉爲數組
var fn = function() {
console.log([...arguments]);
}
fn(1, 2, 3)
// 展開Set值
[...set.values()]
common.js和ES6模塊化的區別
詳情可見:
js的4種模塊化的使用方法和區別
import、require、export、module.exports 混合使用詳解
不同規範產生的來由啊,隨着技術的發展,js這門語言模塊化的需要也越來迫切,隨之而來就是各種規範,什麼CMD、AMD各種規範也應運而生,現在在服務端形成CommonJS規範,前端呢ES6模塊化的概念也深入人心。
畢竟一個是用於後端,一個是用於前端,所以還是有很大的不同,比較一下common.js和ES6模塊化的區別:
-
前者支持動態導入,也就是 require(${path}/xx.js),後者目前不支持。
-
前者是同步導入,因爲用於服務端,文件都在本地,同步導入即使卡住主線程影響也不大。而後者是異步導入,因爲用於瀏覽器,需要下載文件,如果也採用同步導入會對渲染有很大影響。
-
前者在導出時都是值拷貝,就算導出的值變了,導入的值也不會改變,所以如果想更新值,必須重新導入一次。但是後者採用實時綁定的方式,導入導出的值都指向同一個內存地址,所以導入值會跟隨導出值變化
-
require 在 ES6(bable將import轉化爲require) 和 CommonJS 中都支持,像Vue中一般引入CSS樣式的時候會用到require,是因爲樣式一般需要同步導入的原因所致
-
即使我們使用了 ES6 的模塊系統,如果藉助 Babel 的轉換,ES6 的模塊系統最終還是會轉換成 CommonJS 的規範。
兩個數據互換,比如a=6&b=5,怎麼做能a=5&b=6
// 第一種方法
var a = 10;
var b = 20;
var tmp = a; // tmp = 10;
a = b; // a = 20;
b = tmp; // b = 10;
// 第二種方法
var a = 10;
var b = 20;
a = a + b;
b = a - b;
a = a - b;
TS中枚舉的定義
枚舉(Enum)類型用於取值被限定在一定範圍內的場景,比如一週只能有七天,顏色限定爲紅綠藍等,通過enum關鍵字定義
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days[0] === 'Sun'); //true
console.log(Days['Sat'] === 6); //true
Set和Map
JavaScript的默認對象表示方式{}可以視爲其他語言中的Map或Dictionary的數據結構,即一組鍵值對。
但是JavaScript的對象有個小問題,就是鍵必須是字符串。但實際上Number或者其他數據類型作爲鍵也是非常合理的。爲了解決這個問題,最新的ES6規範引入了新的數據類型Map和set.
- Set [類數組] 集合是由一組無序且唯一(即不能重複)的項組成的,可以想象成集合是一個既沒有重複元素,也沒有順序概念的數組
var a = new Set([1, 2, 3, {"1": "2"}, ["3","4"]])
- Map [類對象] 它類似於對象,也是鍵值對的集合,但是“鍵”的範圍不限於字符串,各種類型的值(包括對象)都可以當作鍵,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的數據結構,Map 比 Object 更合適
var m = new Map([['name', 'zhangsan'],['sex', 'male']]);
console.log(m); //Map {"name" => "zhangsan", "sex" => "male"}
防抖與節流的原理及實現
節流 (throttle) 先執行,過一段時間再執行第二次
function throttlePro(delay, action) {
var tId;
return function () {
var context = this;
var arg = arguments;
if (tId) return;
tId = setTimeout(function () {
action.apply(context, arg);
clearTimeout(tId);
// setTimeout 返回一個整數,clearTimeout 之後,tId還是那個整數,setInterval同樣如此
tId = null;
}, delay);
}
}
去抖 (debounce) 直至事件結束後,在處理,比如resize/scroll
function debounce(delay, action) {
var tId;
return function () {
var context = this;
var arg = arguments;
if (tId) clearTimeout(tId);
tId = setTimeout(function () {
action.apply(context, arg);
}, delay);
}
}
window.onscroll = debounce(1000, print);
實現原理:利用定時器setTimeout
展開(擴展)運算符 ...
的使用場景
- 替換Apply的函數調用
// 使用擴展運算符
var array1 = [1, 2, 3];
Math.min(...array1);
- 數組合並
// 數組合並
var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
var array3 = [...array1, ...array2, 7, 8]; //[1,2,3,4,5,6,7,8]
array1.push(...array2 )// 當然也可以使用concat等數組合並方法,但是擴展運算符提供了一種新的方式
- 轉換類數組爲數組
// es5
var fn = function() {
console.log(Array.prototype.slice.apply(arguments));
}
fn(1,2,3)
//擴展運算符
var fn = function() {
console.log([...arguments]);
}
fn(1,2,3)
怎麼拍平一個二維數組(數組的扁平化)
核心原因, 用Array.isArray判斷每一項
js數組拍平(數組扁平化)的六種方式
怎麼判斷一個目標類型
-
typeof
- 對於數組、函數、對象來說,其關係錯綜複雜,使用 typeof 都會統一返回 “object” 字符串,
- null也會返回’object’
- 對NaN返回是’number’
-
instanceof
- 和typeof一樣,
[] instanceof Array
和[] instanceof Object
都是返回true
- 和typeof一樣,
-
Object.prototype.toString.call()
Object.prototype.toString.call("jerry"); //[object String]
Object.prototype.toString.call(12); //[object Number]
Object.prototype.toString.call(true)); //[object Boolean]
function Person(){};
Object.prototype.toString.call(new Person); //[object Object]
// 判斷是否爲函數
function isFunction(it) {
return Object.prototype.toString.call(it) === '[object Function]';
}
// 判斷是否爲數組:
function isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]';
}
babel常用到的工具包
* babel: ES6轉義的核心包
* babel-cli: 用於在終端使用babel,用命令行轉碼
* babel-core: 如果某些代碼需要調用Babel的API進行轉碼,就要使用`babel-core`模塊
* babel-loader: 執行轉義的核心包
* babel-plugin-react-transform: 代替react-hot-loader的插件
* babel-preset-es2015: 現在被babel-preset-env取代了,被babel未來不會過時的(future-proof)”解決方案
* babel-preset-react: 轉義react的jsx語法,
* babel-preset-stage-0: `stage-0`包含`stage-1`, `stage-2`以及`stage-3`的所有功能
Vue中一個頁面能否有多個插槽,怎麼區分
vue中的插槽(slot)
vue中的插槽,父組件向子組件傳遞填充的模板代碼,子組件中提供給父組件使用的一個佔位符,用<slot></slot>
標籤表示,比如HTML、組件等,填充的內容會替換掉子組件的<slot></slot>
標籤(替換佔位符)。
如果需要多個佔位符,則直接加name區分即可,即
// 子組件插槽
<div>
<span>我是第一個插槽</span>
<slot name="yanggb1"></slot>
<span>我是第二個插槽</span>
<slot name="yanggb2"></slot>
</div>
// 父組件插入模板
<template v-slot:yanggb1>
<span>yanggb1</span>
</template>
<template v-slot:yanggb2>
<span>yanggb2</span>
</template>
Vue中data爲啥不直接返回一個對象,而要return出去
不使用return包裹的數據會在項目的全局可見
,會造成變量污染,使用return包裹後數據中變量只在當前組件中生效
,不會影響其他組件
函數作用域和函數執行順序
作用域有全局變量
和局部變量
(也稱爲函數作用域
)兩類, ES6新增塊級作用域,通過新增命令let
和const
來體現
function func(args){
if (true) {
//let聲明i
let i = 6;
//在if內打印i值
console.log('inside: ' + i);//6
}
//在if外,再次打印i值
console.log('outside: ' + i);//i is not defined
};
關於執行順序,主要是由函數的定義
方式中函數聲明式定義 function student(){}
所引起的變量置頂提升
聊聊new Function這個陌生面孔及函數作用域和函數執行順序
js的深拷貝和淺拷貝
淺拷貝的時候如果數據是基本數據類型,那麼就如同直接賦值那種,會拷貝其本身,如果除了基本數據類型之外還有一層對象,那麼對於淺拷貝而言就只能拷貝其引用,對象的改變會反應到拷貝對象上;但是深拷貝就會拷貝多層,即使是嵌套了對象,也會都拷貝出來。
簡單的拷貝方法(即對象中的值沒有函數,數組,對象等複雜類型)
// 第一種 循環賦值
function simpleClone(initalObj) {
var obj = {};
for ( var i in initalObj) {
obj[i] = initalObj[i];
}
return obj;
}
// 第二種 Object.assign
var obj2 = { a: 10, b: 20, c: 30 };
var cloneObj2 = Object.assign({}, obj2);
cloneObj2.b = 100;
console.log(obj2);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(cloneObj2);
// { a: 10, b: 100, c: 30 }
// 第三種 JSON.parse(JSON.stringify(obj1)) 一般處理json數據時纔會使用
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
(終於弄清楚JS的深拷貝和淺拷貝了)[https://blog.csdn.net/QTFYING/article/details/90407551]
聊聊Promise
誕生的原因:在JavaScript的世界中,所有代碼都是單線程執行的。由於這個“缺陷”,導致JavaScript的所有網絡操作,瀏覽器事件,都必須是異步執行。異步執行可以用回調函數實現,最典型的就是ajax,最終被ES6納入標準,生成Promise
判斷下面函數的執行順序
console.log(1)
setTimeout(()=>{console.log(2)},1000)
async function fn(){
console.log(3)
setTimeout(()=>{console.log(4)},20)
return Promise.reject()
}
async function run(){
console.log(5)
await fn()
console.log(6)
}
run()
//需要執行150ms左右
for(let i=0;i<90000000;i++){}
setTimeout(()=>{
console.log(7)
new Promise(resolve=>{
console.log(8)
resolve()
}).then(()=>{console.log(9)})
},0)
console.log(10)
閉包
定義:
-
百度百科:
閉包就是能夠讀取其他函數內部變量的函數。例如在javascript中,只有函數內部的子函數才能讀取局部變量,所以閉包可以理解成“定義在一個函數內部的函數“。 -
《JavaScript高級編程指南》:
閉包是指有權訪問另外一個函數作用域中的變量的函數。 -
MDN:
閉包是由函數以及創建該函數的詞法環境組合而成,這個環境包含了這個閉包創建時所能訪問的所有局部變量
有什麼作用呢:
- 模仿塊級作用域(匿名函數)
- 儲存變量,延長變量生命週期
- 封裝私有變量
- 函數柯里化
// 正常正則驗證字符串 reg.test(txt)
// 函數封裝後
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// Currying後
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
關於閉包的this指向
var name = "聽風是風";
var obj = {
name: "行星飛行",
sayName: function () {
var that = this;
return function () {
console.log(this.name); //this -> window -> 聽風是風
console.log(that.name);
};
}
};
obj.sayName()(); // 行星飛行