js高频面试题
一、延遲加載JS有哪些方式?
延遲加載:async、defer
- 例如:<script defer type="text/javascript" src='script.js'></script>
defer : 等html全部解析完成,才會執行js代碼,順次執行js腳本。
async : async是和html解析同步的(一起的),不是順次執行js腳本(誰先加載完誰先執行)。
?二、JS數據類型有哪些?
基本類型:string、number、boolean、undefined、null、symbol、bigint
引用類型:object
NaN是一個數值類型,但是不是一個具體的數字。
三、JS數據類型考題
考題一:
console.log( true + 1 ); ? ? ?? ??? ??? ?//2 console.log( 'name'+true ); ??? ??? ??? ?//nametrue console.log( undefined + 1 ); ?? ??? ? ? ?//NaN console.log( typeof undefined ); ? ? ? ?//undefined考題二:
console.log( typeof(NaN) ); ? ? ? //number console.log( typeof(null) );? ? ? //object?四、null和undefined的區別
1. 作者在設計js的都是先設計的null(為什么設計了null:最初設計js的時候借鑒了java的語言)
2. null會被隱式轉換成0,很不容易發現錯誤。
3. 先有null后有undefined,出來undefined是為了填補之前的坑。
具體區別:JavaScript的最初版本是這樣區分的:null是一個表示"無"的對象(空對象指針),轉為數值時為0;undefined是一個表示"無"的原始值,轉為數值時為NaN。
五、==和===有什么不同? ?
== ?: ?比較的是值
????????string == number || boolean || number ....都會隱式轉換
?? ??? ?通過valueOf轉換(valueOf() 方法通常由 JavaScript 在后臺自動調用,并不顯式地出現在代碼中。)
=== : 除了比較值,還比較類型
六、JS微任務和宏任務
1. js是單線程的語言。
2. js代碼執行流程:同步執行完==》事件循環
?? ?同步的任務都執行完了,才會執行事件循環的內容
?? ?進入事件循環:請求、定時器、事件....
3. 事件循環中包含:【微任務、宏任務】
微任務:promise.then
宏任務:setTimeout..
要執行宏任務的前提是清空了所有的微任務
流程:同步==》事件循環【微任務和宏任務】==》微任務==》宏任務=》微任務...
七、JS作用域考題
1. 除了函數外,js是沒有塊級作用域。
2. 作用域鏈:內部可以訪問外部的變量,但是外部不能訪問內部的變量。
?? ? ????????注意:如果內部有,優先查找到內部,如果內部沒有就查找外部的。
3. 注意聲明變量是用var還是沒有寫(window.)
4. 注意:js有變量提升的機制【變量懸掛聲明】
5. 優先級:聲明變量 > 聲明普通函數 > 參數 > 變量提升
?考題一:
function c(){var b = 1;function a(){console.log( b );var b = 2;console.log( b );}a();console.log( b ); } c();考題二:
var name = 'a'; (function(){if( typeof name == 'undefined' ){var name = 'b';console.log('111'+name);}else{console.log('222'+name);} })()考題三:
function fun( a ){var a = 10;function a(){}console.log( a ); }fun( 100 );八、JS對象考題
JS對象注意點:
1. 對象是通過new操作符構建出來的,所以對象之間不想等(除了引用外);
2. 對象注意:引用類型(共同一個地址);
3. 對象的key都是字符串類型;
4. 對象如何找屬性|方法;
?? ?查找規則:先在對象本身找 ===> 構造函數中找 ===> 對象原型中找 ===> 構造函數原型中找 ===> 對象上一層原型查找
考題一:
[1,2,3] === [1,2,3] //false?考題二:
var obj1 = {a:'hellow' } var obj2 = obj1; obj2.a = 'world'; console.log(obj1); //{a:world} (function(){console.log(a); //undefinedvar a = 1; })();考題三:
var a = {} var b = {key:'a' } var c = {key:'c' }a[b] = '123'; a[c] = '456';console.log( a[b] ); // 456九、JS作用域+this指向+原型的考題
考題一:
function Foo(){getName = function(){console.log(1)} //注意是全局的window.return this; }Foo.getName = function(){console.log(2)} Foo.prototype.getName = function(){console.log(3)} var getName = function(){console.log(4)} function getName(){console.log(5) }Foo.getName(); //2 getName(); //4 Foo().getName(); //1 getName(); //1 new Foo().getName();//3考題二:
var o = {a:10,b:{a:2,fn:function(){console.log( this.a ); // 2console.log( this ); //代表b對象}} } o.b.fn();考題三:
window.name = 'ByteDance'; function A(){this.name = 123; } A.prototype.getA = function(){console.log( this );return this.name + 1; } let a = new A(); let funcA = a.getA; funcA(); //this代表window考題四:
var length = 10; function fn(){return this.length + 1; } var obj = {length:5,test1:function(){return fn();} } obj.test2 = fn; console.log( obj.test1() ); //1 console.log( fn()===obj.test2() ); //false console.log( obj.test1() == obj.test2() ); //false十、JS判斷變量是不是數組,你能寫出哪些方法?
方式一:isArray
var arr = [1,2,3]; console.log( Array.isArray( arr ) );方式二:instanceof 【可寫,可不寫】
var arr = [1,2,3]; console.log( arr instanceof Array );方式三:原型prototype
var arr = [1,2,3]; console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 );方式四:isPrototypeOf()
var arr = [1,2,3]; console.log( Array.prototype.isPrototypeOf(arr) )方式五:constructor
var arr = [1,2,3]; console.log( arr.constructor.toString().indexOf('Array') > -1 )十一、slice是干嘛的、splice是否會改變原數組
1. slice是來截取的
- 參數可以寫slice(3)、slice(1,3)、slice(-3)
- 返回的是一個新的數組
2. splice 功能有:插入、刪除、替換
- 返回:刪除的元素
- 該方法會改變原數組
?十二、JS數組去重
方式一:new set
var arr1 = [1,2,3,2,4,1]; function unique(arr){return [...new Set(arr)] } console.log( unique(arr1) );方式二:indexOf
var arr2 = [1,2,3,2,4,1]; function unique( arr ){var brr = [];for( var i=0;i<arr.length;i++){if( brr.indexOf(arr[i]) == -1 ){brr.push( arr[i] );}}return brr; } console.log( unique(arr2) );方式三:sort
var arr3 = [1,2,3,2,4,1]; function unique( arr ){arr = arr.sort();var brr = [];for(var i=0;i<arr.length;i++){if( arr[i] !== arr[i-1]){brr.push( arr[i] );}}return brr; } console.log( unique(arr3) );?十三、找出多維數組最大值
function fnArr(arr){var newArr = [];arr.forEach((item,index)=>{newArr.push( Math.max(...item) )})return newArr; } console.log(fnArr([[4,5,1,3],[13,27,18,26],[32,35,37,39],[1000,1001,857,1] ]));?十四、給字符串新增方法實現功能
給字符串對象定義一個addPrefix函數,當傳入一個字符串str時,它會返回新的帶有指定前綴的字符串,例如:
console.log( 'world'.addPrefix('hello') ) 控制臺會輸出helloworld
String.prototype.addPrefix = function(str){return str + this; } console.log( 'world'.addPrefix('hello') )?十五、找出字符串出現最多次數的字符以及次數
var str = 'aaasdwqeasdaqwd';var o = {};for (var i = 0; i < str.length; i++) {var index = str[i];if (!o[index]) {o[index] = 1;} else {o[index]++;}}var max = 0;var str1 = '';for (var k in o) {if (o[k] > max) {max = o[k];str1 = k;}}console.log('出現最多的次數:' + max + '及其字母為:' + str1);?十六、new操作符具體做了什么
?十七、閉包
1. 閉包是什么
?? ?閉包是一個函數加上到創建函數的作用域的連接,閉包“關閉”了函數的自由變量。
?優點:
- 可以訪問到函數內部的局部變量,
- 可以避免全局變量的污染,
- 這些變量的值始終保持在內存中,不會在外層函數調用后被自動清除(不會被垃圾回收)。
缺點:會增大內存使用量,濫用閉包會影響性能,導致內存泄漏【如果說一定要提到ie】等問題。
解決方法:在退出函數之前,將不使用的局部變量全部刪除,可以使變量賦值為null。
2. 什么是垃圾?
- 沒有被引用的對象或變量
- 無法訪問到的對象
垃圾回收機制:
執行環境負責管理代碼執行過程中使用的內存。JS的垃圾回收機制是為了以防內存泄漏,內存泄漏的含義就是當已經不需要某塊內存時這塊內存還存在著,沒有被釋放,導致該內存無法被使用,垃圾回收機制就是間歇的不定期的尋找到不再使用的變量,并釋放掉它們所指向的內存。
?十八、原型鏈
1. 原型可以解決什么問題
- ?? ?對象共享屬性和共享方法
2. 誰有原型
- 函數擁有:prototype
- 對象擁有:__proto__
3. 對象查找屬性或者方法的順序
- 先在對象本身查找 --> 構造函數中查找 --> 對象的原型 --> 構造函數的原型中 --> 當前原型的原型中查找
4. 原型鏈
?? ?4.1 是什么?:就是把原型串聯起來
?? ?4.2 原型鏈的最頂端是null
?十九、JS繼承有哪些方式
方式一:ES6
class Parent{constructor(){this.age = 18;} }class Child extends Parent{constructor(){super();this.name = '張三';} } let o1 = new Child(); console.log( o1,o1.name,o1.age );方式二:原型鏈繼承
function Parent(){this.age = 20; } function Child(){this.name = '張三' } Child.prototype = new Parent(); let o2 = new Child(); console.log( o2,o2.name,o2.age );方式三:借用構造函數繼承
function Parent(){this.age = 22; } function Child(){this.name = '張三'Parent.call(this); } let o3 = new Child(); console.log( o3,o3.name,o3.age );方式四:組合式繼承
function Parent(){this.age = 100; } function Child(){Parent.call(this);this.name = '張三' } Child.prototype = new Parent(); let o4 = new Child(); console.log( o4,o4.name,o4.age );?二十、call、apply、bind區別
共同點:功能一致
可以改變this指向
語法: 函數.call()、函數.apply()、函數.bind()
區別:
- 1. call、apply可以立即執行。bind不會立即執行,因為bind返回的是一個函數需要加入()執行。
- 2. 參數不同:apply第二個參數是數組。call和bind有多個參數需要挨個寫。
場景:
1. 用apply的情況 var arr1 = [1,2,4,5,7,3,321]; console.log( Math.max.apply(null,arr1) )2. 用bind的情況 var btn = document.getElementById('btn'); var h1s = document.getElementById('h1s'); btn.onclick = function(){console.log( this.id ); }.bind(h1s)?二十一、sort背后原理是什么?
V8 引擎 sort 函數只給出了兩種排序 InsertionSort 和 QuickSort,數量小于10的數組使用 InsertionSort,比10大的數組則使用 QuickSort。
之前的版本是:插入排序和快排,現在是冒泡
原理實現鏈接:https://github.com/v8/v8/blob/ad82a40509c5b5b4680d4299c8f08d6c6d31af3c/src/js/array.js
***710行代碼開始***
二十二、深拷貝和淺拷貝
共同點:復制
?
1. 淺拷貝:只復制引用,而未復制真正的值。
var arr1 = ['a','b','c','d']; var arr2 = arr1;var obj1 = {a:1,b:2} var obj2 = Object.assign(obj1);2. 深拷貝:是復制真正的值 (不同引用)
var obj3 = {a:1,b:2 } var obj4 = JSON.parse(JSON.stringify( obj3 ));//遞歸的形式
var oldObj = {a: 1,b: 2,arr: ['1', '2', '3']};var newObj = {};function recursion(newObj, oldObj) {for (var k in oldObj) {var item = oldObj[k];if (item instanceof Array) {newObj[k] = [];recursion(newObj[k], item);} else if (item instanceof Object) {newObj[k] = {};recursion(newObj[k], item);} else {newObj[k] = item;}}return newObj;}recursion(newObj, oldObj);newObj.a = '我是你爸爸真偉大'console.log(newObj);console.log(oldObj);二十三、localStorage、sessionStorage、cookie的區別
公共點:在客戶端存放數據
區別:
1. 數據存放有效期
- sessionStorage : 僅在當前瀏覽器窗口關閉之前有效。【關閉瀏覽器就沒了】
- localStorage? ? ? : 始終有效,窗口或者瀏覽器關閉也一直保存,所以叫持久化存儲。
- cookie?? ??? ??? ??? ?: 只在設置的cookie過期時間之前有效,即使窗口或者瀏覽器關閉也有效。
2. localStorage、sessionStorage不可以設置過期時間
- cookie 有過期時間,可以設置過期(把時間調整到之前的時間,就過期了)
3. 存儲大小的限制
- cookie存儲量不能超過4k
- localStorage、sessionStorage不能超過5M
????????****根據不同的瀏覽器存儲的大小是不同的。
二十四、http狀態碼的了解
- 2開頭的表示請求成功
- 200表示一切正常
- 3開頭的表示重定向
- 301永久重定向
- 302臨時重定向
- 4開頭表示客戶端錯誤
- 400表示請求報文中存在語法錯誤
- 403常見的跨域
- 404請求資源不存在
- 5開頭表示服務器端錯誤
- 500表明服務器端在執行請求時發生了錯誤
二十五、防抖和節流的作用
共同點:限制函數的執行次數
防抖:通過setTimeout的方式,在一定的時間間隔內,將多次觸發只執行最后一次觸發;
節流:減少一段時間的觸發頻率
?二十六、var、let、const區別
var、let、const 共同點都是可以聲明變量的
區別一:
- ?? ?var 具有變量提升的機制
- ?? ?let和const沒有變量提升的機制
區別二:
- ?? ?var 可以多次聲明同一個變量
- ?? ?let和const不可以多次聲明同一個變量
區別三:
- ?? ?var、let聲明變量的
- ?? ?const聲明常量
- ?? ?var和let聲明的變量可以再次賦值,但是const不可以再次賦值了。
區別四:
- ?? ?var聲明的變量沒有自身作用域
- ?? ?let和const聲明的變量有自身的作用域
?二十七、作用域考題
考題一:let和const沒有變量提升性
console.log( str );//undefined var str = '你好';console.log( num );//報錯 let num = 10;考題二:
function demo(){var n = 2;if( true ){var n = 1;}console.log( n );//1 } demo();function demo(){let n = 2;if( true ){let n = 1;}console.log( n );//2 } demo();考題三:可以修改
const obj = {a:1 } obj.a = 11111; console.log( obj )const arr = ['a','b','c']; arr[0]= 'aaaaa'; console.log( arr );?二十八、將下列對象進行合并
方式一:Object.assign
const a = {a:1,b:4}; const b = {b:2,c:3};let obj1 = Object.assign(a,b); console.log( obj1 );方式二:...
let obj2 = {...a,...b}; console.log( obj2 );方式三:自己封裝方法
function extend( target, source ){for(var key in source){target[key] = source[key];}return target; } console.log( extend(a,b) );?二十九、箭頭函數和普通函數有什么區別?
1. this指向的問題
- 箭頭函數中的this只在箭頭函數定義時就決定的,而且不可修改的(call、apply、bind)
- ****箭頭函數的this指向定義時候、外層第一個普通函數的this
2. 箭頭函數不能new(不能當作構造函數)
3. 箭頭函數prototype
4. 箭頭函數arguments
?三十、Promise有幾種狀態
有三種狀態:
- pending(進行中)
- fulfilled(已成功)
- rejected(已失敗)
Promise 是es6引入的異步編程解決方案
?可以鏈式調用,解決回調地獄的問題
?三十一、find和filter的區別??
區別一:返回的內容不同
- filter? 返回是新數組
- find ? 返回具體的內容
區別二:
- find? ? 匹配到第一個即返回
- filter? ?返回整體(沒一個匹配到的都返回)?
?三十二、some和every的區別 ?
- some ?? 如果有一項匹配則返回true
- every? ? 全部匹配才會返回true
?三十三、利用遞歸求斐波那契數列
function fb(n) {if (n == 0) {return 0;}if (n <= 2) {return 1;}return fb(n - 1) + fb(n - 2);}console.log(fb(9));?三十四、利用遞歸求階乘
function fn(n) {if (n == 1) {return 1;}return n * fn(n - 1);}console.log(fn(3));總結
- 上一篇: PyPI 推送自己的
- 下一篇: pypi 清华源