前端面試資料整理【javascript篇】
作者:donggg
來源:SegmentFault 思否社區(qū)
塊級作用域(執(zhí)行結(jié)果題)
塊級作用域:https://blog.poetries.top/browser-working-principle/guide/part2/lesson09.html
阮一峰 塊級作用域:https://www.bookstack.cn/read/es6-3rd/spilt.2.docs-let.md
var a = 1999;
{
console.log(a); // function a(){}
a = 2020;
function a() {}
a = 2021;
}
console.log(a); // 2020
我的理解:(原理可能錯(cuò)誤,但是便于理解,錯(cuò)誤點(diǎn)在于塊級不能聲明函數(shù),瀏覽器會(huì)有自己的支持行為,類似于函數(shù)表達(dá)式聲明——參考阮一峰。聲明也不要用函數(shù)聲明的方式,用函數(shù)表達(dá)式的方式。)
在塊級中,編譯過程,函數(shù)聲明變量提升,執(zhí)行過程無影響。因此是 function a(){}在塊級外部,編譯過程,外部有a,無影響。執(zhí)行過程時(shí),開始查找,由于 a 的查找順序是從詞法環(huán)境({})到變量環(huán)境(var),查找到最近的,因此是 2020(注意此處是執(zhí)行階段,function a(){} 的變量提升是編譯階段)
ps.函數(shù)和變量相比,會(huì)被優(yōu)先提升。(我的理解:這意味著函數(shù)會(huì)替換掉變量提升)
事件冒泡、事件捕獲、事件代理(事件委托)
事件冒泡:略
事件捕獲:略
事件代理:利用事件冒泡,將事件綁定在父元素中
target.addEventListener(type, listener, useCapture); ,其中useCapture 為 false 時(shí),為事件冒泡,默認(rèn)是 false。
Object
常見的方法:
Object.defineProperty 定義的 description: { value, writable, configurable, emunable },或者 { set, get, configurable, emunable }
Object.create 、Object.keys、Object.values、Object.entries、Object.toString
Object.preventExtensions 組織擴(kuò)展對象
作用域、作用域鏈和上下文
作用域是指在函數(shù)定義時(shí),聲明變量的空間。
函數(shù)執(zhí)行時(shí),AO對象,包含內(nèi)部函數(shù)、變量、形參、內(nèi)部this,掛載到作用域鏈上,
原形鏈
待補(bǔ)充
Promise 與異步
常見的異步請求:
var request = new HttpXMLRequest()
request.open('GET', url);
request.responseType = 'json'
request.onload = function(){}
request.send()
// Promise
// Promise.prototype.then
// Promise.prototype.all
// Promise.prototype.resolve
// Promise.prototype.race
事件循環(huán)與事件隊(duì)列
事件循環(huán)
事件隊(duì)列(單位是消息,消息關(guān)聯(lián)著回調(diào)函數(shù),從消息隊(duì)列中彈出后會(huì)調(diào)用回調(diào)函數(shù),形成執(zhí)行幀)
執(zhí)行棧(單位是幀,包含函數(shù)中的變量與參數(shù))
堆(保存對象結(jié)構(gòu))
同步任務(wù)與異步任務(wù) window.requestAnimationFrame() 既不是宏任務(wù)也不是微任務(wù),而是在瀏覽器下次重繪的時(shí)候執(zhí)行
閉包
通過函數(shù)阻止外部函數(shù)對內(nèi)部變量的引用 函數(shù)可以使用外部的變量
由于閉包是執(zhí)行在內(nèi)存中,所以 this 通常指向全局,可以通過 call 改變。
通過立即執(zhí)行函數(shù),模擬塊級作用域,減少向全局作用域聲明變量,另外由于立即執(zhí)行函數(shù)在執(zhí)行完后外部沒有引用,那么內(nèi)存會(huì)立即釋放
使用 var 聲明時(shí),利用立即執(zhí)行函數(shù)遍歷時(shí) i 能準(zhǔn)確獲取
實(shí)現(xiàn)面向?qū)ο缶幊蹋ú皇峭ㄟ^ new 構(gòu)造)
function Person(){
var name = 'default';
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
};
var p1 = Person();
console.log(p1.getName()); // default
p1.setName('wang');
console.log(p1.getName()); // wang
在閉包中引用 dom 會(huì)導(dǎo)致循環(huán)引用,無法 GC(引用計(jì)數(shù))
// IE9 之前甚至無法銷毀 dom
function closure(){
var element = document.getElementById(ID);
element.onclick = function(){
console.log(element.id);
}
// 銷毀 element
// element = null
}
this 指向 閉包返回局部作用域變量時(shí),內(nèi)存不會(huì)立即釋放
宏任務(wù) 和 微任務(wù)
常見異步考題:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7
es5/es6/es7/es8
待補(bǔ)充
class 中的 super
super 既可以當(dāng)函數(shù)也可以當(dāng)對象使用。
class A {
constructor() {
this.show();
}
show(){
console.log('A 實(shí)例');
}
}
class B extends A {
constructor() {
super();
}
show(){
console.log('B 實(shí)例');
}
}
new B() // B 實(shí)例
class A {
constructor() {
this.x = 'A';
}
say() {
console.log(this.x)
}
}
class B extends A {
constructor() {
super();
this.x = 'B'
}
}
let b = new B();
console.log(b.say()) // B
class A {
constructor() { // 在構(gòu)造函數(shù)上定義的屬性和方法相當(dāng)于定義在父類實(shí)例上的,而不是原型對象上
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
console.log(b.m) // undefined
// 引申題
function A(x) {
this.p = x
}
A.prototype.p = 2
// 此時(shí)的 p 通過構(gòu)造函數(shù)已經(jīng)聲明
new A().p // undefined
常見的代碼
防抖與節(jié)流
function debounce(fn, delay) {
let timer
return function () {
const self = this;
const args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function(){
fn.apply(self, args);
}, delay)
}
}
let log = debounce(function(){ console.log('!')}, 5000)
window.addEventListener('resize',log)
function throttle(fn, delay) {
let timer
return function () {
const self = this;
const args = arguments;
if (timer) {
return;
}
timer = setTimeout(function() {
self.apply(fn, args)
timer = null;
}, delay)
}
}
let log = throttle(function(){ console.log('!')}, 3000)
window.addEventListener('click',log)
節(jié)流當(dāng)?shù)谝淮螆?zhí)行是 arg 就固定了,也就是說如果用節(jié)流放到輸入框 onChange 場景時(shí),值將是第一個(gè)輸入的數(shù)字。
防抖,通過不斷的 clearTimeout,來更新要執(zhí)行的函數(shù),直到不觸發(fā)后,等待 delay 后執(zhí)行,delay 的作用是在此期間如果再次觸發(fā),則會(huì)再次 clearTimeout
手寫new
// Object.create 會(huì)更新 __proto__,也就是 [[Prototype]],維持原形鏈
function create (proto) {
if (typeof proto !== 'object' && typeof proto !== 'function' ) {
throw new TypeError("原型只能是對象")
}
if (proto === null) {
throw new TypeError("不能為空")
}
// function F() {}
//F.prototype = proto;
// return new F();
proto.constructor.prototype = proto
return new proto.constructor()
}
function newOperator(ctor) {
if (typeof ctor !== 'function') {
throw '構(gòu)造函數(shù)必須是方法'
}
newOperator.target = ctor;
// es6 可以直接使用 Object.create
// const instance = Object.create(ctor.prototype)
const instance = create(ctor.prototype)
const args = [].slice.call(arguments, 1)
// 綁定 this,并執(zhí)行構(gòu)造函數(shù)
const r = ctor.apply(instance, args);
// 如果構(gòu)造函數(shù)有返回,則返回構(gòu)造函數(shù)
if (r) {
return r;
}
// 實(shí)例
return instance;
}
function Person (name) {
this.name = name
}
const w = newOperator(Person, "zs")
console.log(w)
手寫bind
function bind(fn, obj) {
const args = [].slice.call(arguments, 1);
const selef = this;
return function bound() {
return fn.call(obj, [].slice.call(arguments, 1).concat(args))
}
}
const h = {
name: 'zs',
}
function say() {
console.log(this.name)
}
const bound = bind(say, h)
bound()
Object.is Polyfill
if (!Object.is) {
Object.defineProperty(Object, "is", {
value: function (x, y) {
if (x === y) {
// 1. 如果 x === y,且均不為0時(shí)
// 2. 如果 x,y 均為 0 ,判斷符號是否相等
return x !== 0 || 1 / x === 1 / y;
} else {
// NaN 與自己比較。包含:Number.NaN, 0/0, NaN
return x != x && y != y;
}
}
})
}
如何實(shí)現(xiàn) Array.reduce()
待補(bǔ)充
curry 與 compose
待補(bǔ)充
Object.assign()
待補(bǔ)充
實(shí)現(xiàn)字符串 repeat
// 原生repeat 'ni'.repeat(3);
// 'ninini'
// 實(shí)現(xiàn)一
String.prototype.repeatString1 = function (n) {
return Array(n + 1).join(this);
}
console.log('ni'.repeatString1(3));
// 實(shí)現(xiàn)二
String.prototype.repeatString2 = function (n) {
return Array(n).fill(this).join('');
}
console.log('ni'.repeatString2(3));
js 模板引擎
Function('let a = 1');
其他

