-
原生javaScript是中大公司挑人的核心,也是決定你未來發展高度的核心。
-
氣泡排序,快速排序,深度克隆,深度凍結,陣列操作,本章都有。
-
走遍大江南北,還是原生javaScript最美
-
感冒給大家更新,希望大家多多點贊,謝謝!
-
下面是本人一些其他文章和學習的文件 , 全棧工程師一起加油!
快速排序演算法
'這應該是最簡單的快速排序實現,什麼是快速排序呢?'
'快速排序(Quicksort)是對氣泡排序的一種改進。
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:
通過一趟排序將要排序的資料分割成獨立的兩部分,其中一
部分的所有資料都比另外一部分的所有資料都要小,然後再
按此方法對這兩部分資料分別進行快速排序,整個排序過程
可以遞迴進行,以此達到整個資料變成有序序列。'
function quickSort(arr) {
if (arr.length <= 1) { return arr; }
var left = [],
right = [],
baseDot = Math.round(arr.length / 2)
base = arr.splice(baseDot, 1)[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < base) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([base], quickSort(right));
}
let arr = [5, 3, 4, 12];
const newarr = quickSort(arr);
console.log(newarr);
'每次遞迴呼叫,都會直到陣列中只有一個數字為止,然後
執行上下文棧出棧,返回上一個執行上下文迴圈遞迴,拼接陣列'
複製程式碼
氣泡排序演算法
'什麼是氣泡排序演算法?'
'氣泡排序(Bubble Sort),是一種電腦科學領域的較簡單的排序演算法。
它重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,如果他們的順
序(如從大到小、首字母從A到Z)錯誤就把他們交換過來。走訪元素的工作
是重複地進行直到沒有相鄰元素需要交換,也就是說該元素已經排序完成。
這個演算法的名字由來是因為越大的元素會經由交換慢慢“浮”到數列的頂端
(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端
一樣,故名“氣泡排序”。'
bubbleSortSoul1 = (arr = []) => {
let count = 0;
// i為輪數(因i從0開始 即i<arr.length-1)
for (let i = 0; i < arr.length - 1; i++) {
count++;
// 第i輪僅需比較length-1-i次
for (let j = 0; j < arr.length - 1 - i; j++) {
// 這裡能不能寫成arr[j-1]>arr[j]? 如果有這種特殊癖好 那麼j就從1開始吧,然後j<arr.length-i
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
console.log(`bubbleSortSoul1排序完成用了${count}輪`);
return arr;
}
複製程式碼
深度克隆
'為什麼我們需要深度克隆?而且面試必問?'
'因為引用資料型別儲存在堆空間中,當兩個變數同時指向同一個地址時,
只要一個改變,那麼另外一個也會跟著變,我們的原意是想一個改變,另
一個不變,那麼就需要重新開拓一個堆空間出來,所以就有了深度克隆。'
'第一種方法(只適用於基礎型別)'
const newObj = JSON.parse(JSON.stringify(oldObj));
'第二種方法,涵蓋所有的型別'
const getType = (obj)=> {
var toString = Object.prototype.toString;
var map = {
'[object Boolean]' : 'boolean',
'[object Number]' : 'number',
'[object String]' : 'string',
'[object Function]' : 'function',
'[object Array]' : 'array',
'[object Date]' : 'date',
'[object RegExp]' : 'regExp',
'[object Undefined]': 'undefined',
'[object Null]' : 'null',
'[object Object]' : 'object',
'[object Symbol]' : 'symbol'
};
if(obj instanceof Element) {//因為對不同標籤,toString會返回對應不同標籤的建構函式
return 'element';
}
return map[toString.call(obj)];
}
const getRegExp = re => {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
};
/**
* deep clone
* @param {[type]} parent object 需要進行克隆的物件
* @return {[type]} 深克隆後的物件
*/
const deepClone = oldObj => {
// 維護兩個儲存迴圈引用的陣列
const oldObjArr = [];
const newObjArr = [];
const clone = oldObj => {
let newObj, proto;
const type = getType(oldObj);
switch(type){
case 'boolean':
case 'number':
case 'string':
case 'null':
case 'undefined':
case 'function':{
return oldObj;
break;
}
case 'symbol':{
return Symbol(Symbol.keyFor(oldObj).toString());
break;
}
case 'array':{
newObj = [];
break;
}
case 'regExp':{
newObj = new RegExp(oldObj.source, getRegExp(oldObj));
if (oldObj.lastIndex) newObj.lastIndex = oldObj.lastIndex;
break;
}
case 'date':{
newObj = new Date(oldObj.getTime());
break;
}
//case 'obj':
default:{
// 處理物件原型
proto = Object.getPrototypeOf(oldObj);
// 利用Object.create切斷原型鏈
newObj = Object.create(proto);
break;
}
}
// 處理迴圈引用
const index = oldObjArr.indexOf(oldObj);
if (index != -1) {// 如果父陣列存在本物件,說明之前已經被引用過,直接返回此物件
return newObjArr[index];
}
oldObjArr.push(oldObj);
newObjArr.push(newObj);
/*陣列和物件都可以用forin語句,雖然陣列使用forin會有一個問題(具體看最下面)。
但是這裡不會影響,所以這麼用
*/
for (let i in oldObj) {// 遞迴
newObj[i] = clone(oldObj[i]);
}
return newObj;
};
return clone(oldObj);
}
/*
測試成功
*/
function person(pname) {
this.name = pname;
}
const Messi = new person('Messi');
function say() {
console.log('hi');
};
const oldObj = {
a: say,
b: new Array(1),
c: new RegExp('ab+c', 'i'),
d: Messi
};
const newObj = deepClone(oldObj);
console.log(newObj.a, oldObj.a); //[Function: say] [Function: say]
console.log(newObj.b[0], oldObj.b[0]); // undefined undefined
console.log(newObj.c, oldObj.c); // /ab+c/i /ab+c/i
console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: person][Function: person]
'所有的型別都可以被克隆,完美版'
複製程式碼
* 判斷物件是否一個陣列的條件是:
* Array.isArray(),這個在安卓的瀏覽器中相容性一般
* arr instanceof Array 這個相容性不錯,可以使用
* 還有就是arr. \___proto\___ .consturctor == Array 這個也是可以的 相容性應該還不錯
* Object.prototype.tostring.call(arr) == [object Array] 這個也可以
## 如何將一個多維陣列平鋪開來? 使用最原生的JS方法解決
ES6中有了解構賦值,下面是原生的辦法
整體來說,利用了函式的執行上下文棧,然後避免了forEach的在IE中的相容性問題,
雖然說現在IE不怎麼考慮,但是我覺得底層原生javaScript能力強總歸不是壞事
let newarr = [];
let arr = [
[
[1, 2, 3],
['a', 'b', 'c', ['demo1', 'demo2', 'demo3', ['f', 'g', 'h', [4, 5, 6]]]],
]
];
/* function check(arr) {
arr.forEach(function (item) {
if (Array.isArray(item)) {
check(item);
} else {
newarr.push(item)
}
})
}
check(arr);
console.log(newarr)*/
function check(arr) {
for (var i = 0; i < arr.length; i++) {
//if裡面的條件也可以寫成 arr[i] instanceof Array
或者 Object.prototype.toString.call(arr[i]) == [object Array]
if (arr[i].__proto__.constructor == Array) {
check(arr[i])
} else {
newarr.push(arr[i])
}
}
}
check(arr);
console.log(newarr)
複製程式碼
如何提取用最原生的方法提取一個物件內部所有屬性的值,並且放在不同陣列中?
- 個人認為第一題和第二題結合起來,可以用來處理前後臺互動的資料,如果格式很複雜,也可以使用這兩者的模式結合,然後把他們分別提取出來進行操作。
const obj = {
name: 'json',
age: 1,
friend: '夢露',
info: {
name: 'Aron',
age: 2,
friend: '傑西卡',
info: {
name: 'rose',
age: 3,
friend: '黴黴',
info: {
name: 'jerry',
age: 4,
friend: '比伯',
info: {
name: 'damu',
age: 5,
friend: 'XJ',
},
},
},
}
}
let namearr, agearr, friendarr;
namearr = [];
agearr = [];
friendarr = [];
check(obj);
function check(obj) {
const items = Object.getOwnPropertyNames(obj)
items.forEach(function (item) {
if (Object.prototype.toString.call(obj[item]) == '[object Object]') {
check(obj[item]);
} else {
if (item.toString() === 'name') {
namearr.push(obj[item])
} else if (item.toString() === 'age') {
agearr.push(obj[item])
} else if (item.toString() === 'friend') {
friendarr.push(obj[item])
}
}
})
}
/* 這種方法也是一樣的效果 使用for in迴圈代替的Object.getOwnPropertyNames(obj)方法
function check(obj) {
for (var item in obj) {
if (Object.prototype.toString.call(obj[item]) == '[object Object]') {
check(obj[item]);
} else {
if (item == 'name') {
namearr.push(obj[item])
}
else if (item == 'age') {
agearr.push(obj[item])
}
else if (item == 'friend') {
friendarr.push(obj[item])
}
}
}
}*/
console.log(`namearr:${namearr}`)
console.log(`agearr:${agearr}`)
console.log(`friendarr:${friendarr}`)
複製程式碼
請手寫一個陣列由小到大排序而且去重,不得使用高階函式
let arr = [1, 1, 2, 2, 5, 5, 'a', 'a', '3', '3']
arr = arr.sort();
let realarr = [];
for (let i = 0; i < arr.length; i++) {
if (i == 0) {
realarr.push(arr[i])
} else if (i !== 0 && arr[i] !== arr[i - 1]) {
realarr.push(arr[i])
}
}
console.log(realarr)
複製程式碼
如何將一個物件深度凍結?
跟上面的陣列面試題一樣,利用了執行上下文棧,先進的後出,最先凍結最深層裡面的那個屬性,
再依次返回上一層繼續凍結
var obj = {
name:"王寶強",
age:18,
wife:{
name:"陳羽凡",
age:17,
son:{
name:"賈乃亮",
age:48,
girlFriend:{
name:"吳秀波",
age:50,
zuo:function () {
console.log("翻舊賬")
},
foods:["棉花糖","粉色的棉花糖","各種棉花糖",{a:"a"}]
}
}
}
};
Object.prototype.deepFreeze = function () {
var keys = Object.getOwnPropertyNames(this);
var that = this;
keys.forEach(function (key) {
var val = that[key];
if(Object.prototype.toString.call(val) === "[object Object]" || Object.prototype.toString.call(val) === "[object Array]"){
val.deepFreeze()
}
});
return Object.freeze(this)
}
obj.deepFreeze()
複製程式碼
請使用定時器和canvas寫一個隨機生成多個彩色泡
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body,
html {
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
border: 1px solid;
background: white;
}
</style>
</head>
<body>
<canvas width="400" height="400"></canvas>
</body>
<script>
window.onload = function () {
var canvasnode = document.querySelector("canvas");
var arr=[];
if (canvasnode.getContext) {
var ctx = canvasnode.getContext("2d");
}
setInterval(function(){
console.log(arr)
ctx.clearRect(0,0,canvasnode.width,canvasnode.height)
arr.forEach(function(item,index){
item.r++;
item.opa-=0.01;
if(item.opa<=0){
arr.splice(index,1)
}
} )
arr.forEach(function(item){
ctx.save()
ctx.fillStyle="rgba("+item.red+","+item.green+","+item.blue+","+item.opa+")";
ctx.beginPath()
ctx.arc(item.x,item.y,item.r,0,2*Math.PI)
ctx.fill()
ctx.restore()
} )
} ,10)
setInterval(function(){
var obj={x:0,y:0,r:0,red:0,green:0,blue:0,opa:0};
obj.x=Math.random()*400;
obj.y=Math.random()*400;
obj.r=10;
obj.opa=1;
obj.red =Math.round(Math.random()*255);
obj.green =Math.round(Math.random()*255);
obj.blue =Math.round(Math.random()*255);
arr.push(obj);
},100 )
}
</script>
</html>
複製程式碼
請自己定義一個DOM節點的R操作,以此作為queryselector在IE8以下的polifill。
(function(w){
w.app = {};
w.app.getElementByClassName=function(className){
var allnode=document.getElementsByTagName("*");
console.log(allnode)
var arr=[];
for(var i=0;i<allnode.length;i++){
var newclass=" "+allnode[i].className+" ";
var reg=new RegExp("\\s+"+className+"\\s+","i");
if(reg.test(newclass)){
arr.push(allnode[i]);
}
}
return arr
}
}(window)
複製程式碼
請你手寫一個ajax原生javaScript請求?
'由於ajax一般用於比較舊的技術,這裡不適用ES6語法'
var xhr = new XMLHttpRuest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 0) {
//xhr物件建立好了 初始化狀態
console.log(0)
}
if (xhr.readyState == 1) {
//代表xhr.send方法還未呼叫(還未傳送請求),還可以設定請求頭相關資訊
console.log(1)
}
if (xhr.readyState == 2) {
//此時send方法被呼叫了 響應頭和首行已經回來了
console.log(xhr.getResponseHeader('etag'))
console.log(2)
}
if (xhr.readyState == 3) {
console.log(3)
}
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(4)
console.log(xhr.responseText);
}
}
xhr.open('GET', 'http://localhost:3000/ajax?username=123&password=456');
// xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
// xhr.send('username=123&password=456');
xhr.send()
}*/
'//上面是原生的ajax寫法,下面是jQuery中的兩種ajax寫法'
' 1. 原生jQuery寫法 '
$('#btn').click(function () {
$.ajax({
url: 'http://localhost:3000/ajax',
data: 'username=jack&password=123',
method: 'POST',
success: function (data) {
console.log(data)
},
error: function (error) {
console.log(error)
}
})
'2.簡寫'
$.post('http://localhost:3000/ajax', 'username=rose&age=12', (data) => {
console.log(data)
})
'//如果是get請求直接上面改成get就可以了,data是伺服器響應回來的資料'
複製程式碼
然後這邊給一下伺服器 Node.js的express程式碼 響應上面的ajax請求的
const express = require('express');
const app = express();
app.use(express.static('public'))
app.use(express.urlencoded({ extended: true }));
app.get('/ajax', (req, res) => {
console.log(req.query)
res.send('這是ajax的get請求')
})
app.post('/ajax', (req, res) => {
res.send('這是ajax的post請求')
console.log(req.body)
})
app.listen(3000, err => {
if (!err) {
console.log('埠監聽成功');
} else {
console.log('埠監聽失敗' + err);
}
})
複製程式碼
如何使用原生javaScript方法提取一個多重物件的值,並且塞到不同對應的陣列中?
'這裡使用了遞迴,還有Object原型上的方法,執行上下文棧先進後出的知識。'
const obj = {
name: 'json',
age: 1,
friend: '夢露',
info: {
name: 'Aron',
age: 2,
friend: '傑西卡',
info: {
name: 'rose',
age: 3,
friend: '黴黴',
info: {
name: 'jerry',
age: 4,
friend: '比伯',
info: {
name: 'damu',
age: 5,
friend: 'XJ',
},
},
},
}
}
let namearr, agearr, friendarr;
namearr = [];
agearr = [];
friendarr = [];
check(obj);
/* function check(obj) {
const items = Object.getOwnPropertyNames(obj)
items.forEach(function (item) {
if (Object.prototype.toString.call(obj[item]) == '[object Object]') {
check(obj[item]);
} else {
if (item.toString() === 'name') {
namearr.push(obj[item])
} else if (item.toString() === 'age') {
agearr.push(obj[item])
} else if (item.toString() === 'friend') {
friendarr.push(obj[item])
}
}
})
}*/
function check(obj) {
for (var item in obj) {
if (Object.prototype.toString.call(obj[item]) == '[object Object]') {
check(obj[item]);
} else {
if (item == 'name') {
namearr.push(obj[item])
}
else if (item == 'age') {
agearr.push(obj[item])
}
else if (item == 'friend') {
friendarr.push(obj[item])
}
}
}
}
console.log(`namearr:${namearr}`)
console.log(`namearr:${agearr}`)
console.log(`namearr:${friendarr}`)
複製程式碼
請手寫一個jsonp和cors 解決跨域問題的程式碼 ?
'jsonp'
document.getElementById('btn').onclick = function () {
/*
1. jsonp
- 特點:
1. 利用script標籤天然跨域跨域的特性解決跨域的, 民間推出的
2. 相容性極好
*/
//建立一個script標籤
const script = document.createElement('script');
//設定了響應成功的回撥函式
window.jsoncallback = function (data) {
console.log(data);
}
//設定script的src屬性, 向指定伺服器傳送請求
script.src = 'http://localhost:3000/?callback=jsoncallback';
//新增到body中生效
document.body.appendChild(script);
}
------
'cors的解決方法:在Node.js的伺服器程式碼中設定一個響應頭'
app.get('/cors', (req, res) => {
/*
1. cors
特點:
- 官方推出解決跨域的方案,使用起來及其簡單,只需在伺服器設定一個響應頭
- 相容性較差
*/
//設定響應頭
res.set('access-control-allow-origin', '*'); //允許所有網址跨域
複製程式碼
瀏覽器的輪詢機制請簡述
'1.瀏覽器的事件輪詢機制
瀏覽器中對於js依靠js引擎實現,js引擎是單執行緒,不像java,php這些可以是多執行緒,高併發。如果要說到瀏覽器的輪詢機制,那麼我們首先要說的
就是單執行緒的js引擎,前端的核心程式設計思維模式是非同步程式設計,無論是頁面效果、前後端的資料互動,都是以非同步為核心,每個需要非同步的場景,
往往伴隨著回撥函式去執行,而單執行緒的JS引擎是無法自身做這麼多工作,還需要非同步執行緒。
1.每當JS引擎解析程式碼時遇到非同步程式碼時,交給非同步執行緒,繼續往下解析程式碼。
2.非同步執行緒處理這些非同步程式碼時,一旦他們的所對應的回撥函式達到執行條件便會塞進非同步佇列中,等待JS引擎的輪詢。
3.JS引擎會在解析完下面的所有程式碼後,再去輪詢非同步佇列,從左到右,依次執行,這也是說為什麼定時器的時間不準確的原因,在JS
解析程式碼時,如果遇到下面程式碼特別多的時候,那麼它就沒時間去輪詢非同步佇列的程式碼。
瀏覽器中的輪詢機制有一個特殊的 requestAnimationFrame(callbackname),它所對應的回撥函式,是在瀏覽器下一次重繪重排時執行,它是一個巨集任務,有待考證
,目前看只要觸發重繪重排就會呼叫回撥函式,可以避免掉幀,優化效能,減少重繪重排次數,即使一個空白頁面,它也會重繪重排,所以只要運用好,
它是完全可以替代定時器,還可以使用cancelAnimationFrame(callbackname)清除。
'
複製程式碼
Node.js中的事件輪詢機制 Event loop
'Node.js的事件輪詢機制外還有同步程式碼,微任務,
要想徹底弄懂Node的程式碼執行,得結合下面的微任
務一起學習。'
'1.執行已經到時間的setTimeout 和 setInterval
2.執行延遲到一個迴圈迭代的i/o回撥
3.系統內部的 idle prepare等
4.poll 輪詢回撥佇列,依次取出,同步執行,與JS的非同步佇列執行有點相像 直到回撥佇列為空 或者系統奔潰了 如果回撥佇列沒有內容,那麼看
之前是否設定過setImmadiate(),如果有就去下一個階段,如果沒有,就在當前等待新的回撥函式。 如果定時器的時間到了,那麼也會去下一個階段
5. setImmediate
6.關閉的回撥函式 ,一些準備關閉的函式等.
Node.js的事件輪詢機制也可以看成是單執行緒,由上往下執行,但是到了第6階段,又會返回第一階段,死迴圈。 '
複製程式碼
什麼是微任務什麼是巨集任務?
'想得太多反而不好,把每個巨集任務看成銀行排隊的老大爺,把微任務看成老大爺需要的業務,
可能他需要辦存款,理財,買紀念幣等,櫃檯人員不幫老大爺辦完
他所需要的任務 -- 微任務,就不會切換到下一個老大爺 -- 巨集任務, 但是程式設計的邏輯不
能完全抽象成現實生活,
照這種說法,只能先有老大爺才會有業務需要,
。可是在Node中,先執行的是微任務,只有微任務如果有多層,先執行最頂層,再往下依次執
行)執行完後才能去執行巨集任務,微任務有兩種,一種是process.nextTick()
中的函式,一種是Promise.then()中的函式,只有他們執行完後,才會去執行巨集任務:setTim
eout ,setIneterval,setImmadie。(即執行完了微任務才會遵循Node.js的輪詢機制去執行,
一切微任務優先)'
複製程式碼