亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

「中高級前端面試」JavaScript手寫代碼無敵秘籍(推薦)

2024-05-06 16:49:40
字體:
來源:轉載
供稿:網友

1. 實現一個new操作符

new操作符做了這些事:

  1. 它創建了一個全新的對象。
  2. 它會被執行[[Prototype]](也就是__proto__)鏈接。
  3. 它使this指向新創建的對象。。
  4. 通過new創建的每個對象將最終被[[Prototype]]鏈接到這個函數的prototype對象上。
  5. 如果函數沒有返回對象類型Object(包含Functoin, Array, Date, RegExg, Error),那么new表達式中的函數調用將返回該對象引用。
function New(func) { var res = {}; if (func.prototype !== null) {  res.__proto__ = func.prototype; } var ret = func.apply(res, Array.prototype.slice.call(arguments, 1)); if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {  return ret; } return res;}var obj = New(A, 1, 2);// equals tovar obj = new A(1, 2);

2. 實現一個JSON.stringify

JSON.stringify(value[, replacer [, space]]):

  1. Boolean | Number| String 類型會自動轉換成對應的原始值。
  2. undefined、任意函數以及symbol,會被忽略(出現在非數組對象的屬性值中時),或者被轉換成 null(出現在數組中時)。
  3. 不可枚舉的屬性會被忽略
  4. 如果一個對象的屬性值通過某種間接的方式指回該對象本身,即循環引用,屬性也會被忽略。
function jsonStringify(obj) { let type = typeof obj; if (type !== "object") {  if (/string|undefined|function/.test(type)) {   obj = '"' + obj + '"';  }  return String(obj); } else {  let json = []  let arr = Array.isArray(obj)  for (let k in obj) {   let v = obj[k];   let type = typeof v;   if (/string|undefined|function/.test(type)) {    v = '"' + v + '"';   } else if (type === "object") {    v = jsonStringify(v);   }   json.push((arr ? "" : '"' + k + '":') + String(v));  }  return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}") }}jsonStringify({x : 5}) // "{"x":5}"jsonStringify([1, "false", false]) // "[1,"false",false]"jsonStringify({b: undefined}) // "{"b":"undefined"}"

3. 實現一個JSON.parse

JSON.parse(text[, reviver])

用來解析JSON字符串,構造由字符串描述的JavaScript值或對象。提供可選的reviver函數用以在返回之前對所得到的對象執行變換(操作)。

3.1 第一種:直接調用 eval

function jsonParse(opt) { return eval('(' + opt + ')');}jsonParse(jsonStringify({x : 5}))// Object { x: 5}jsonParse(jsonStringify([1, "false", false]))// [1, "false", falsr]jsonParse(jsonStringify({b: undefined}))// Object { b: "undefined"}

避免在不必要的情況下使用 eval,eval() 是一個危險的函數, 他執行的代碼擁有著執行者的權利。如果你用 eval()運行的字符串代碼被惡意方(不懷好意的人)操控修改,您最終可能會在您的網頁/擴展程序的權限下,在用戶計算機上運行惡意代碼。

它會執行JS代碼,有XSS漏洞。

如果你只想記這個方法,就得對參數json做校驗。

var rx_one = /^[/],:{}/s]*$/;var rx_two = ///(?:["////bfnrt]|u[0-9a-fA-F]{4})/g;var rx_three = /"[^"///n/r]*"|true|false|null|-?/d+(?:/./d*)?(?:[eE][+/-]?/d+)?/g;var rx_four = /(?:^|:|,)(?:/s*/[)+/g;if ( rx_one.test(  json   .replace(rx_two, "@")   .replace(rx_three, "]")   .replace(rx_four, "") )) { var obj = eval("(" +json + ")");}

3.2 第二種:Function

來源神奇的eval()與new Function()

核心:Function與eval有相同的字符串參數特性。

var func = new Function(arg1, arg2, ..., functionBody);

在轉換JSON的實際應用中,只需要這么做。

var jsonStr = '{ "age": 20, "name": "jack" }'var json = (new Function('return ' + jsonStr))();

eval 與 Function 都有著動態編譯js代碼的作用,但是在實際的編程中并不推薦使用。

這里是面向面試編程,寫這兩種就夠了。至于第三,第四種,涉及到繁瑣的遞歸和狀態機相關原理,具體可以看:

《JSON.parse 三種實現方式》

 4. 實現一個call或 apply

實現改編來源:JavaScript深入之call和apply的模擬實現 #11

call語法:

fun.call(thisArg, arg1, arg2, ...),調用一個函數, 其具有一個指定的this值和分別地提供的參數(參數的列表)。

apply語法:

func.apply(thisArg, [argsArray]),調用一個函數,以及作為一個數組(或類似數組對象)提供的參數。

 4.1 Function.call按套路實現

call核心:

  1. 將函數設為對象的屬性
  2. 執行&刪除這個函數
  3. 指定this到函數并傳入給定參數執行函數
  4. 如果不傳入參數,默認指向為 window

為啥說是套路實現呢?因為真實面試中,面試官很喜歡讓你逐步地往深考慮,這時候你可以反套路他,先寫個簡單版的:

4.1.1 簡單版

var foo = { value: 1, bar: function() {  console.log(this.value) }}foo.bar() // 1

4.1.2 完善版

當面試官有進一步的發問,或者此時你可以假裝思考一下。然后寫出以下版本:

Function.prototype.call2 = function(content = window) { content.fn = this; let args = [...arguments].slice(1); let result = content.fn(...args); delete content.fn; return result;}let foo = { value: 1}function bar(name, age) { console.log(name) console.log(age) console.log(this.value);}bar.call2(foo, 'black', '18') // black 18 1

4.2 Function.apply的模擬實現

apply()的實現和call()類似,只是參數形式不同。直接貼代碼吧:

Function.prototype.apply2 = function(context = window) { context.fn = this let result; // 判斷是否有第二個參數 if(arguments[1]) {  result = context.fn(...arguments[1]) } else {  result = context.fn() } delete context.fn return result}

5. 實現一個Function.bind()

bind()方法:

會創建一個新函數。當這個新函數被調用時,bind() 的第一個參數將作為它運行時的 this,之后的一序列參數將會在傳遞的實參前傳入作為它的參數。(來自于 MDN )

此外,bind實現需要考慮實例化后對原型鏈的影響。

Function.prototype.bind2 = function(content) { if(typeof this != "function") {  throw Error("not a function") } // 若沒問參數類型則從這開始寫 let fn = this; let args = [...arguments].slice(1);  let resFn = function() {  return fn.apply(this instanceof resFn ? this : content,args.concat(...arguments) ) } function tmp() {} tmp.prototype = this.prototype; resFn.prototype = new tmp();  return resFn;}

6. 實現一個繼承

寄生組合式繼承

一般只建議寫這種,因為其它方式的繼承會在一次實例中調用兩次父類的構造函數或有其它缺點。

核心實現是:用一個 F 空的構造函數去取代執行了 Parent 這個構造函數。

function Parent(name) { this.name = name;}Parent.prototype.sayName = function() { console.log('parent name:', this.name);}function Child(name, parentName) { Parent.call(this, parentName);  this.name = name; }function create(proto) { function F(){} F.prototype = proto; return new F();}Child.prototype = create(Parent.prototype);Child.prototype.sayName = function() { console.log('child name:', this.name);}Child.prototype.constructor = Child;var parent = new Parent('father');parent.sayName(); // parent name: fathervar child = new Child('son', 'father');

7. 實現一個JS函數柯里化

什么是柯里化?

在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數且返回結果的新函數的技術。

函數柯里化的主要作用和特點就是參數復用、提前返回和延遲執行。

7.1  通用版

function curry(fn, args) { var length = fn.length; var args = args || []; return function(){  newArgs = args.concat(Array.prototype.slice.call(arguments));  if (newArgs.length < length) {   return curry.call(this,fn,newArgs);  }else{   return fn.apply(this,newArgs);  } }}function multiFn(a, b, c) { return a * b * c;}var multi = curry(multiFn);multi(2)(3)(4);multi(2,3,4);multi(2)(3,4);multi(2,3)(4);

7.2 ES6騷寫法

const curry = (fn, arr = []) => (...args) => ( arg => arg.length === fn.length ? fn(...arg) : curry(fn, arg))([...arr, ...args])let curryTest=curry((a,b,c,d)=>a+b+c+d)curryTest(1,2,3)(4) //返回10curryTest(1,2)(4)(3) //返回10curryTest(1,2)(3,4) //返回10

8. 手寫一個Promise(中高級必考)

我們來過一遍Promise/A+規范:

  1. 三種狀態pending| fulfilled(resolved) | rejected
  2. 當處于pending狀態的時候,可以轉移到fulfilled(resolved)或者rejected狀態
  3. 當處于fulfilled(resolved)狀態或者rejected狀態的時候,就不可變。

必須有一個then異步執行方法,then接受兩個參數且必須返回一個promise:

// onFulfilled 用來接收promise成功的值// onRejected 用來接收promise失敗的原因promise1=promise.then(onFulfilled, onRejected);

8.1 Promise的流程圖分析

前端面試,JavaScript,代碼

來回顧下Promise用法:

var promise = new Promise((resolve,reject) => { if (操作成功) {  resolve(value) } else {  reject(error) }})promise.then(function (value) { // success},function (value) { // failure})

8.2 面試夠用版

來源:實現一個完美符合Promise/A+規范的Promise

function myPromise(constructor){ let self=this; self.status="pending" //定義狀態改變前的初始狀態 self.value=undefined;//定義狀態為resolved的時候的狀態 self.reason=undefined;//定義狀態為rejected的時候的狀態 function resolve(value){  //兩個==="pending",保證了狀態的改變是不可逆的  if(self.status==="pending"){   self.value=value;   self.status="resolved";  } } function reject(reason){  //兩個==="pending",保證了狀態的改變是不可逆的  if(self.status==="pending"){   self.reason=reason;   self.status="rejected";  } } //捕獲構造異常 try{  constructor(resolve,reject); }catch(e){  reject(e); }}

 

同時,需要在myPromise的原型上定義鏈式調用的then方法: 

myPromise.prototype.then=function(onFullfilled,onRejected){ let self=this; switch(self.status){  case "resolved":  onFullfilled(self.value);  break;  case "rejected":  onRejected(self.reason);  break;  default:   }}

測試一下:

var p=new myPromise(function(resolve,reject){resolve(1)});p.then(function(x){console.log(x)})//輸出1

8.3 大廠專供版

直接貼出來吧,這個版本還算好理解

const PENDING = "pending";const FULFILLED = "fulfilled";const REJECTED = "rejected";function Promise(excutor) { let that = this; // 緩存當前promise實例對象 that.status = PENDING; // 初始狀態 that.value = undefined; // fulfilled狀態時 返回的信息 that.reason = undefined; // rejected狀態時 拒絕的原因 that.onFulfilledCallbacks = []; // 存儲fulfilled狀態對應的onFulfilled函數 that.onRejectedCallbacks = []; // 存儲rejected狀態對應的onRejected函數 function resolve(value) { // value成功態時接收的終值  if(value instanceof Promise) {   return value.then(resolve, reject);  }  // 實踐中要確保 onFulfilled 和 onRejected 方法異步執行,且應該在 then 方法被調用的那一輪事件循環之后的新執行棧中執行。  setTimeout(() => {   // 調用resolve 回調對應onFulfilled函數   if (that.status === PENDING) {    // 只能由pending狀態 => fulfilled狀態 (避免調用多次resolve reject)    that.status = FULFILLED;    that.value = value;    that.onFulfilledCallbacks.forEach(cb => cb(that.value));   }  }); } function reject(reason) { // reason失敗態時接收的拒因  setTimeout(() => {   // 調用reject 回調對應onRejected函數   if (that.status === PENDING) {    // 只能由pending狀態 => rejected狀態 (避免調用多次resolve reject)    that.status = REJECTED;    that.reason = reason;    that.onRejectedCallbacks.forEach(cb => cb(that.reason));   }  }); } // 捕獲在excutor執行器中拋出的異常 // new Promise((resolve, reject) => { //  throw new Error('error in excutor') // }) try {  excutor(resolve, reject); } catch (e) {  reject(e); }}Promise.prototype.then = function(onFulfilled, onRejected) { const that = this; let newPromise; // 處理參數默認值 保證參數后續能夠繼續執行 onFulfilled =  typeof onFulfilled === "function" ? onFulfilled : value => value; onRejected =  typeof onRejected === "function" ? onRejected : reason => {   throw reason;  }; if (that.status === FULFILLED) { // 成功態  return newPromise = new Promise((resolve, reject) => {   setTimeout(() => {    try{     let x = onFulfilled(that.value);     resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一個onFulfilled的返回值    } catch(e) {     reject(e); // 捕獲前面onFulfilled中拋出的異常 then(onFulfilled, onRejected);    }   });  }) } if (that.status === REJECTED) { // 失敗態  return newPromise = new Promise((resolve, reject) => {   setTimeout(() => {    try {     let x = onRejected(that.reason);     resolvePromise(newPromise, x, resolve, reject);    } catch(e) {     reject(e);    }   });  }); } if (that.status === PENDING) { // 等待態  // 當異步調用resolve/rejected時 將onFulfilled/onRejected收集暫存到集合中  return newPromise = new Promise((resolve, reject) => {   that.onFulfilledCallbacks.push((value) => {    try {     let x = onFulfilled(value);     resolvePromise(newPromise, x, resolve, reject);    } catch(e) {     reject(e);    }   });   that.onRejectedCallbacks.push((reason) => {    try {     let x = onRejected(reason);     resolvePromise(newPromise, x, resolve, reject);    } catch(e) {     reject(e);    }   });  }); }};

emmm,我還是乖乖地寫回進階版吧。

9. 手寫防抖(Debouncing)和節流(Throttling)

scroll 事件本身會觸發頁面的重新渲染,同時 scroll 事件的 handler 又會被高頻度的觸發, 因此事件的 handler 內部不應該有復雜操作,例如 DOM 操作就不應該放在事件處理中。
針對此類高頻度觸發事件問題(例如頁面 scroll ,屏幕 resize,監聽用戶輸入等),有兩種常用的解決方法,防抖和節流。

9.1 防抖(Debouncing)實現

典型例子:限制 鼠標連擊 觸發。
一個比較好的解釋是:

當一次事件發生后,事件處理器要等一定閾值的時間,如果這段時間過去后 再也沒有 事件發生,就處理最后一次發生的事件。假設還差 0.01 秒就到達指定時間,這時又來了一個事件,那么之前的等待作廢,需要重新再等待指定時間。

前端面試,JavaScript,代碼

// 防抖動函數function debounce(fn,wait=50,immediate) { let timer; return function() {  if(immediate) {   fn.apply(this,arguments)  }  if(timer) clearTimeout(timer)  timer = setTimeout(()=> {   fn.apply(this,arguments)  },wait) }}

結合實例:滾動防抖

// 簡單的防抖動函數// 實際想綁定在 scroll 事件上的 handlerfunction realFunc(){ console.log("Success");}// 采用了防抖動window.addEventListener('scroll',debounce(realFunc,500));// 沒采用防抖動window.addEventListener('scroll',realFunc);

9.2 節流(Throttling)實現

可以理解為事件在一個管道中傳輸,加上這個節流閥以后,事件的流速就會減慢。實際上這個函數的作用就是如此,它可以將一個函數的調用頻率限制在一定閾值內,例如 1s,那么 1s 內這個函數一定不會被調用兩次

前端面試,JavaScript,代碼

簡單的節流函數:

function throttle(fn, wait) {	let prev = new Date();	return function() { 	 const args = arguments;		const now = new Date();		if (now - prev > wait) {			fn.apply(this, args);			prev = new Date();		}	}

9.3 結合實踐

通過第三個參數來切換模式。

const throttle = function(fn, delay, isDebounce) { let timer let lastCall = 0 return function (...args) { if (isDebounce) {  if (timer) clearTimeout(timer)  timer = setTimeout(() => {  fn(...args)  }, delay) } else {  const now = new Date().getTime()  if (now - lastCall < delay) return  lastCall = now  fn(...args) } }}

10. 手寫一個JS深拷貝

有個最著名的乞丐版實現,在《你不知道的JavaScript(上)》里也有提及:

前端面試,JavaScript,代碼

10.1 乞丐版

 var newObj = JSON.parse( JSON.stringify( someObj ) );

10.2 面試夠用版

function deepCopy(obj){ //判斷是否是簡單數據類型, if(typeof obj == "object"){  //復雜數據類型  var result = obj.constructor == Array ? [] : {};  for(let i in obj){   result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];  } }else {  //簡單數據類型 直接 == 賦值  var result = obj; } return result;}

關于深拷貝的討論天天有,這里就貼兩種吧,畢竟我...

前端面試,JavaScript,代碼

11.實現一個instanceOf

function instanceOf(left,right) { let proto = left.__proto__; let prototype = right.prototype while(true) {  if(proto === null) return false  if(proto === prototype) return true  proto = proto.__proto__; }}

以上所述是小編給大家介紹的JavaScript手寫代碼詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美激情精品久久久久| 国产成人精品午夜| 成人午夜两性视频| 日韩中文字幕在线视频| 国产精品www| 中文字幕国产精品久久| 一区二区亚洲欧洲国产日韩| 91久热免费在线视频| 91精品久久久久久久久青青| 亚洲剧情一区二区| 深夜福利日韩在线看| 亚洲人成电影网站色…| 亚洲第一区中文99精品| 国产日韩换脸av一区在线观看| 国产精品视频最多的网站| 久久亚洲私人国产精品va| 亚洲图片在区色| 精品日韩视频在线观看| 高清视频欧美一级| 久久夜色精品国产亚洲aⅴ| 亚洲黄色有码视频| 上原亚衣av一区二区三区| 精品国偷自产在线视频| 懂色av一区二区三区| 亚洲a∨日韩av高清在线观看| 欧美日韩在线视频观看| 亚洲综合自拍一区| 国产精品成人一区二区三区吃奶| 欧美中文字幕第一页| 欧洲s码亚洲m码精品一区| 日韩美女免费观看| 91久久久久久久久久| 欧美在线性爱视频| 亚洲日本成人女熟在线观看| 疯狂欧美牲乱大交777| 亚洲视频免费一区| 亚洲第一精品福利| 亚洲区免费影片| 欧美激情免费观看| 久久久久女教师免费一区| 欧美日韩亚洲天堂| 久久99久久99精品中文字幕| 日韩最新av在线| 在线播放国产一区中文字幕剧情欧美| 欧美午夜视频在线观看| 欧美日韩一区二区三区在线免费观看| 久久久久久国产精品久久| 欧美中文在线观看| 亚洲综合最新在线| 亚洲精品在线视频| 日韩成人在线视频| 欧美成人午夜剧场免费观看| 国产一区视频在线播放| 亚洲精品视频播放| 国产成人中文字幕| 91久久国产精品91久久性色| 日本国产一区二区三区| 日韩有码在线电影| 日韩亚洲国产中文字幕| 亚洲激情久久久| 久久在线免费视频| 中文字幕在线国产精品| 欧美性xxxx18| 欧美老少做受xxxx高潮| 亚洲性无码av在线| 亚洲成人国产精品| 国产精品扒开腿爽爽爽视频| 亚洲成人精品久久久| 亚洲欧美日韩精品久久亚洲区| 日韩av在线免费观看一区| 亚洲免费视频一区二区| 欧美视频在线观看免费| 国产精品福利片| 亚洲人成啪啪网站| 欧美成人h版在线观看| 欧美三级免费观看| 欧美www视频在线观看| 亚洲九九九在线观看| 国产欧美精品一区二区三区介绍| 久久久久日韩精品久久久男男| 久久香蕉国产线看观看网| 亚洲美女av网站| 欧美日韩国产精品一区| 九九久久久久99精品| 色妞一区二区三区| 日韩视频在线观看免费| 国产精品视频专区| 欧美自拍视频在线观看| 蜜臀久久99精品久久久久久宅男| 日韩精品在线观看网站| 欧美日韩福利视频| 亚洲精品国产精品乱码不99按摩| 97在线精品国自产拍中文| 日韩av免费一区| 国产成人短视频| 亚洲 日韩 国产第一| 97国产精品免费视频| 国产精品美女无圣光视频| 97久久精品视频| 亚洲精美色品网站| 成人免费看吃奶视频网站| 国产一区二区三区视频免费| 中文字幕日韩综合av| 26uuu国产精品视频| 欧美电影免费在线观看| 国内精品免费午夜毛片| 欧美日韩一区二区免费在线观看| 亚洲天堂男人天堂女人天堂| 国产亚洲欧洲在线| 亚洲精品中文字幕女同| 国产精品入口夜色视频大尺度| 热久久视久久精品18亚洲精品| 久久久精品2019中文字幕神马| 91久久精品国产91久久性色| 久久精品成人动漫| 久久久久久97| 亚洲自拍在线观看| 韩国国内大量揄拍精品视频| 97**国产露脸精品国产| 热re99久久精品国产66热| 日韩成人中文字幕| 日本不卡高字幕在线2019| 欧美成人第一页| 国产精品久久久久久久久久久久| 亚洲日韩中文字幕| 日韩av三级在线观看| 亚洲成人久久网| 欧美成人剧情片在线观看| 日韩欧美成人免费视频| 精品美女久久久久久免费| 久久久久国产一区二区三区| 国产精品欧美亚洲777777| 欧美国产日韩中文字幕在线| 992tv在线成人免费观看| 97超碰蝌蚪网人人做人人爽| 久久久999精品免费| 亚洲综合一区二区不卡| 91在线国产电影| 亚洲精品99久久久久中文字幕| 国产一区二区三区在线| 久久久久久噜噜噜久久久精品| 亚洲欧洲日本专区| 欧美在线一区二区三区四| 亚洲新中文字幕| 亚洲一区二区中文| 91精品国产91久久久久久不卡| 日韩精品在线免费观看视频| 久久久久久国产免费| 亚洲色图偷窥自拍| 福利一区视频在线观看| 91精品视频在线| 亚洲乱码一区av黑人高潮| 91久久精品国产91久久| 亚洲午夜国产成人av电影男同| 57pao精品| 久久久精品电影| 九九综合九九综合| 久久免费视频在线观看| 精品国内亚洲在观看18黄| 91久久精品日日躁夜夜躁国产| 91大神福利视频在线| 日本亚洲精品在线观看| 国外成人在线视频| 国产精品1区2区在线观看|