<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手寫前端高頻面試題

          共 14581字,需瀏覽 30分鐘

           ·

          2021-11-25 09:38

          建議優(yōu)先掌握:

          • instanceof (考察對(duì)原型鏈的理解)
          • new (對(duì)創(chuàng)建對(duì)象實(shí)例過(guò)程的理解)
          • call&apply&bind (對(duì)this指向的理解)
          • 手寫promise (對(duì)異步的理解)
          • 手寫原生ajax (對(duì)http請(qǐng)求方式的理解,重點(diǎn)是get和post請(qǐng)求)

          1、手寫 instanceof

          instanceof作用:判斷一個(gè)實(shí)例是否是其父類或者祖先類型的實(shí)例。instanceof 在查找的過(guò)程中會(huì)遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype查找失敗,返回 false

          let instanceof = (target, origin) => {
          while (target) {
          if(target.__proto__ === origin.prototype) {
          return true
          }
          target = target.__proto__
          }
          return false
          }
          let a = [1, 2, 3]
          console.log(instanceof(a, Array)); // true
          console.log(instanceof(a, Object)); // true

          2、實(shí)現(xiàn)數(shù)組的map方法

          Array.prototype.myMap = function(fn, thisValue) {
          let res = []
          thisValue = thisValue || []
          let arr = this
          for (let i in arr) {
          res.push(fn(arr[i]))
          }
          return res
          }

          3、reduce實(shí)現(xiàn)數(shù)組的map方法

          Array.prototype.myMap = function(fn, thisValue) {
          var res = [];
          thisValue = thisValue || [];
          this.reduce(function(pre, cur, index, arr) {
          return res.push(fn.call(thisValue, cur, index, arr));
          },[]);
          return res;
          }

          var arr = [2,3,1,5];
          arr.myMap(function(item, index, arr) {
          console.log(item, index, arr);
          })

          4、 手寫數(shù)組的reduce方法

          reduce() 方法接收一個(gè)函數(shù)作為累加器,數(shù)組中的每個(gè)值(從左到右)開始縮減,最終為一個(gè)值,是ES5中新增的又一個(gè)數(shù)組逐項(xiàng)處理方法

          參數(shù):

          • callback(一個(gè)在數(shù)組中每一項(xiàng)上調(diào)用的函數(shù),接受四個(gè)函數(shù):)
            • previousValue(上一次調(diào)用回調(diào)函數(shù)時(shí)的返回值,或者初始值)
            • currentValue(當(dāng)前正在處理的數(shù)組元素)
            • currentIndex(當(dāng)前正在處理的數(shù)組元素下標(biāo))
            • array(調(diào)用reduce()方法的數(shù)組)
            • initialValue(可選的初始值。作為第一次調(diào)用回調(diào)函數(shù)時(shí)傳給previousValue的值)
          function reduce(arr, cb, initialValue) {
          var num = initValue === undefined ? num = arr[0]: initValue;
          var i = initValue == undefined ? 1: 0
          for (i; i< arr.length; i++) {
          num = cb(num,arr[i],i)
          }
          return num
          }

          function fn(result, currentValue, index) {
          return result + currentValue
          }

          var arr = [2, 3, 4, 5]
          var b = reduce(arr, fn, 10)
          var c = reduce(arr, fn)
          console.log(b) // 24

          5、數(shù)組扁平化

          數(shù)組扁平化就是把多維數(shù)組轉(zhuǎn)化成一維數(shù)組

          5.1、es6提供的新方法 flat(depth)

          let a = [1, [2, 3]]; 
          a.flat(); // [1, 2, 3]
          a.flat(1); //[1, 2, 3]

          5.2、無(wú)需知道數(shù)組的維度,直接將目標(biāo)數(shù)組變成1維數(shù)組。depth的值設(shè)置為Infinity。

          let a = [1, [2, 3, [4, [5]]]]; 
          a.flat(Infinity);
          // [1, 2, 3, 4, 5] a是4維數(shù)組

          5.3、利用concat

          var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
          function flatten(arr) {
          var res = [];
          for (let i = 0, length = arr.length; i < length; i++) {
          if (Array.isArray(arr[i])) {
          res = res.concat(flatten(arr[i])); //concat 并不會(huì)改變?cè)瓟?shù)組
          //res.push(...flatten(arr[i])); //擴(kuò)展運(yùn)算符
          } else {
          res.push(arr[i]);
          }
          }
          return res;
          }
          flatten(arr1);
          //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

          6、函數(shù)柯里化

          柯里化的定義:接收一部分參數(shù),返回一個(gè)函數(shù)接收剩余參數(shù),接收足夠參數(shù)后,執(zhí)行原函數(shù)。當(dāng)柯里化函數(shù)接收到足夠參數(shù)后,就會(huì)執(zhí)行原函數(shù),如何去確定何時(shí)達(dá)到足夠的參數(shù)呢?

          有兩種思路:

          1. 通過(guò)函數(shù)的 length 屬性,獲取函數(shù)的形參個(gè)數(shù),形參的個(gè)數(shù)就是所需的參數(shù)個(gè)數(shù)
          2. 在調(diào)用柯里化工具函數(shù)時(shí),手動(dòng)指定所需的參數(shù)個(gè)數(shù)

          將這兩點(diǎn)結(jié)合一下,實(shí)現(xiàn)一個(gè)簡(jiǎn)單 curry 函數(shù):

          /**
          * 將函數(shù)柯里化
          * @param fn 待柯里化的原函數(shù)
          * @param len 所需的參數(shù)個(gè)數(shù),默認(rèn)為原函數(shù)的形參個(gè)數(shù)
          */

          function curry(fn, len = fn.length) {
          return _curry.call(this, fn, len)
          }

          /**
          * 中轉(zhuǎn)函數(shù)
          * @param fn 待柯里化的原函數(shù)
          * @param len 所需的參數(shù)個(gè)數(shù)
          * @param args 已接收的參數(shù)列表
          */

          function _curry(fn, len, ...args) {
          return function (...params) {
          let _args = [...args, ...params];
          if (_args.length >= len) {
          return fn.apply(this, _args);
          } else {
          return _curry.call(this, fn, len, ..._args)
          }
          }
          }

          我們來(lái)驗(yàn)證一下:

          let _fn = curry(function(a, b, c, d, e){
          console.log(a, b, c, d, e)
          });

          _fn(1, 2, 3, 4, 5); // print: 1, 2, 3, 4, 5
          _fn(1)(2)(3, 4, 5); // print: 1, 2, 3, 4, 5
          _fn(1, 2)(3, 4)(5); // print: 1, 2, 3, 4, 5
          _fn(1)(2)(3)(4)(5); // print: 1, 2, 3, 4, 5

          7、實(shí)現(xiàn)深拷貝

          淺拷貝和深拷貝的區(qū)別: 淺拷貝:只拷貝一層,更深層的對(duì)象級(jí)別的只拷貝引用 深拷貝:拷貝多層,每一級(jí)別的數(shù)據(jù)都會(huì)拷貝。這樣更改拷貝值就不影響另外的對(duì)象

          ES6淺拷貝方法:Object.assign(target, ...sources)

          let obj = {
          id : 1,
          name : 'Tom',
          msg : {
          age : 18
          }
          }
          let o = {}
          //實(shí)現(xiàn)深拷貝 遞歸 可以用于生命游戲那個(gè)題對(duì)二維數(shù)組的拷貝,
          //但比較麻煩,因?yàn)橐阎囟际侵担苯訌?fù)制就行,無(wú)需判斷
          function deepCopy(newObj, oldObj) {
          for(var k in oldObj) {
          let item = oldObj[k]
          //判斷是數(shù)組?對(duì)象?簡(jiǎn)單類型?
          if(item instanceof Array) {
          newObj[k] = []
          deepCopy(newObj[k], item)
          } else if (item instanceof Object) {
          newObj[k] = {}
          deepCopy(newObj[k], item)
          } else {
          //簡(jiǎn)單數(shù)據(jù)類型,直接賦值
          newObj[k] = item
          }
          }
          }

          8、手寫call, apply

          8.1、手寫call

          // 函數(shù)的方法,所以寫在Function原型對(duì)象上
          Function.prototype.myCall = function(context = window){
          // 這里if其實(shí)沒(méi)必要,會(huì)自動(dòng)拋出錯(cuò)誤
          if(typeof this !== "function"){
          throw new Error("不是函數(shù)")
          }
          //這里可用ES6方法,為參數(shù)添加默認(rèn)值,js嚴(yán)格模式全局作用域this為undefined
          const obj = context || window
          //this為調(diào)用的上下文,this此處為函數(shù),將這個(gè)函數(shù)作為obj的方法
          obj.fn = this
          //第一個(gè)為obj所以刪除,偽數(shù)組轉(zhuǎn)為數(shù)組
          const arg = [...arguments].slice(1)
          res = obj.fn(...arg)
          // 不刪除會(huì)導(dǎo)致context屬性越來(lái)越多
          delete obj.fn
          return res
          }

          //用法:f.call(obj, arg1)
          function f(a, b){
          console.log(a + b)
          console.log(this.name)
          }
          let obj = {
          name : 1
          }
          //否則this指向window
          f.myCall(obj, 1, 2)

          //打出來(lái)的是 Spike
          obj.greet.call({name: 'Spike'})

          8.2、手寫apply

          // 箭頭函數(shù)從不具有參數(shù)對(duì)象!!!!!這里不能寫成箭頭函數(shù)
          Function.prototype.myApply = function(context){
          let obj=context || window
          obj.fn = this
          //若有參數(shù),得到的是數(shù)組
          const arg = arguments[1] || []
          let res = obj.fn(...arg)
          delete obj.fn
          return res
          }

          // 驗(yàn)證一下
          function f(a, b){
          console.log(a, b)
          console.log(this.name)
          }
          let obj = {
          name:'張三'
          }
          //arguments[1]
          f.myApply(obj, [1,2])

          8.3、手寫bind

          Function.prototype.bind2 = function (context) {
          const self = this;
          const args = Array.prototype.slice.call(arguments, 1);
          const NOOP = function(){};
          NOOP.prototype = this.prototype;
          const func = function () {
          const args2 = Array.prototype.slice.call(arguments);
          return self.apply(this instanceof NOOP ? this : context, args2.concat(args));
          };
          func.prototype = new NOOP();
          return func;
          }

          9、手動(dòng)實(shí)現(xiàn)new

          new的過(guò)程文字描述:

          1. 創(chuàng)建一個(gè)空對(duì)象 obj;
          2. 將空對(duì)象的隱式原型(proto)指向構(gòu)造函數(shù)的prototype。
          3. 使用 call 改變 this 的指向
          4. 如果無(wú)返回值或者返回一個(gè)非對(duì)象值,則將 obj 返回作為新對(duì)象;如果返回值是一個(gè)新對(duì)象的話那么直接直接返回該對(duì)象。
          function Person(name, age) {
          this.name = name
          this.age = age
          }
          Person.prototype.sayHi = function () {
          console.log('Hi!我是' + this.name)
          }
          let p1 = new Person('張三',18)

          ////手動(dòng)實(shí)現(xiàn)new
          function create() {
          let obj = {}
          //獲取構(gòu)造函數(shù)
          //將arguments對(duì)象提出來(lái)轉(zhuǎn)化為數(shù)組,arguments并不是數(shù)組而是對(duì)象 !!!這種方法刪除了arguments數(shù)組的第一個(gè)元素,!!這里的空數(shù)組里面填不填元素都沒(méi)關(guān)系,不影響arguments的結(jié)果 或者let arg = [].slice.call(arguments,1)
          let fn = [].shift.call(arguments)
          obj.__proto__ = fn.prototype
          //改變this指向,為實(shí)例添加方法和屬性
          let res = fn.apply(obj,arguments)
          //確保返回的是一個(gè)對(duì)象(萬(wàn)一fn不是構(gòu)造函數(shù))
          return typeof res === 'object' ? res:obj
          }

          let p2 = create(Person, '李四', 19)
          p2.sayHi()

          10、手寫promise(常見promise.all, promise.race)

          (function (window, factory) {
          if (typeof exports === 'object') {
          module.exports = factory();
          } else if (typeof define === 'function' && define.amd) {
          define(['Promise'], factory);
          } else {
          window.Promise = factory();
          }
          })(this, function () {
          var PENDING = 0;
          var FULFILLED = 1;
          var REJECTED = 2;

          var async = (function () {
          if (typeof process === 'object' && process !== null && typeof process.nextTick === 'function') {
          return process.nextTick;
          } else if (typeof setImmediate === 'function') {
          return setImmediate;
          }
          return setTimeout;
          })();

          function isFunction(func) {
          return typeof func === 'function';
          }

          function isArray(arr) {
          return Object.prototype.toString.call(arr) === '[object Array]';
          }

          function Promise(executor) {
          var self = this;
          this._state = PENDING;
          this._value = undefined;
          this._onResolvedCallback = [];
          this._onRejectedCallback = [];

          function resolve(value) {
          if (self._state === PENDING) {
          self._state = FULFILLED;
          self._value = value;
          for (var i = 0; i < self._onResolvedCallback.length; i++) {
          self._onResolvedCallback[i](value);
          }
          self._onResolvedCallback = [];
          }
          }

          function reject(reason) {
          if (self._state === PENDING) {
          self._state = REJECTED;
          self._value = reason;
          for (var i = 0; i < self._onRejectedCallback.length; i++) {
          self._onRejectedCallback[i](reason);
          }
          self._onRejectedCallback = [];
          }
          }

          try {
          // async(executor, null, resolve, reject);
          executor(resolve, reject);
          } catch (reason) {
          // async(reject, null, reason);
          reject(reason);
          }
          }

          Promise.prototype.then = function (onResolved, onRejected) {
          var self = this;
          onResolved = isFunction(onResolved)
          ? onResolved
          : function (v) {
          return v;
          };
          onRejected = isFunction(onRejected)
          ? onRejected
          : function (r) {
          throw r;
          };

          return new self.constructor(function (resolve, reject) {
          function _resolve(value) {
          try {
          var p = onResolved(value);
          if (p instanceof Promise) {
          p.then(resolve, reject);
          } else {
          resolve(p);
          }
          } catch (e) {
          reject(e);
          }
          }

          function _reject(reason) {
          try {
          var p = onRejected(reason);
          if (p instanceof Promise) {
          p.then(resolve, reject);
          } else {
          resolve(p);
          }
          } catch (e) {
          reject(e);
          }
          }

          if (self._state === PENDING) {
          self._onResolvedCallback.push(_resolve);
          self._onRejectedCallback.push(_reject);
          } else if (self._state === FULFILLED) {
          async(_resolve, null, self._value);
          // _resolve(self._value);
          } else if (self._state === REJECTED) {
          async(_reject, null, self._value);
          // _reject(self._value);
          }
          });
          };

          Promise.prototype.catch = function (onRejected) {
          return this.then(null, onRejected);
          };

          Promise.resolve = function (data) {
          return new Promise(function (resolve) {
          resolve(data);
          });
          };

          Promise.reject = function (data) {
          return new Promise(function (resolve, reject) {
          reject(data);
          });
          };

          Promise.all = function (promiseArr) {
          if (!isArray(promiseArr)) {
          throw new TypeError('Promise.all need Array object as argument');
          }
          return new Promise(function (resolve, reject) {
          var count = (len = promiseArr.length);
          var result = [];

          for (var i = 0; i < len; i++) {
          var promise = promiseArr[i];
          promise.then(
          (function (index) {
          return function (value) {
          result[index] = value;
          if (--count === 0) {
          resolve(result);
          }
          };
          })(i),
          reject
          );
          }
          });
          };

          Promise.race = function (promiseArr) {
          if (!isArray(promiseArr)) {
          throw new TypeError('Promise.race need Array object as argument');
          }

          return new Promise(function (resolve, reject) {
          var len = promiseArr.length;
          for (var i = 0; i < len; i++) {
          var promise = promiseArr[i];
          promise.then(resolve, reject);
          }
          });
          };

          return Promise;
          });

          11、手寫原生AJAX并支持jsonp

          步驟

          1. 創(chuàng)建 XMLHttpRequest 實(shí)例
          2. 發(fā)出 HTTP 請(qǐng)求
          3. 服務(wù)器返回 XML 格式的字符串
          4. JS 解析 XML,并更新局部頁(yè)面不過(guò)隨著歷史進(jìn)程的推進(jìn),XML 已經(jīng)被淘汰,取而代之的是 JSON。

          了解了屬性和方法之后,根據(jù) AJAX 的步驟,手寫最簡(jiǎn)單的 GET 請(qǐng)求。

          var ajax = (function () {
          var GLOBAL = {};

          //獲取XMLHttpRequest對(duì)象
          var getXMLHttpReq = function () {
          var xhr = null;
          try {
          xhr = new ActiveXObject('Msxml2.XMLHTTP'); //IE高版本創(chuàng)建XMLHTTP
          } catch (E) {
          try {
          xhr = new ActiveXObject('Microsoft.XMLHTTP'); //IE低版本創(chuàng)建XMLHTTP
          } catch (E) {
          xhr = new XMLHttpRequest(); //兼容非IE瀏覽器,直接創(chuàng)建XMLHTTP對(duì)象
          }
          }
          return xhr;
          };

          //格式化用戶輸入數(shù)據(jù)參數(shù),防止不合法參數(shù),返回json對(duì)象
          //TODO 試圖格式化數(shù)據(jù)時(shí)使用eval,可能運(yùn)行不合法的腳本,造成安全問(wèn)題
          var parseData = function (data) {
          if (!data) return {};
          if (typeof data != 'object') {
          try {
          data = JSON.parse(data);
          } catch (e) {
          data = eval('(' + data + ')');
          }
          }
          return data;
          };

          //判斷空對(duì)象
          var isEmptyObject = function (obj) {
          var name;
          for (name in obj) {
          return false;
          }
          return true;
          };

          return (the = {
          //ajax查詢方法
          request: function (arg) {
          var url = arg.url || null;
          var dataType = arg.dataType || 'text';
          var type = arg.type || 'post';
          var async = arg.async || true;
          var callback = arg.callback || null;
          var data = arg.data || null;

          if (!url) return null;
          var XMLHttpReq = getXMLHttpReq();
          data = parseData(data);
          if (type.toLowerCase() === 'jsonp') {
          if (!arg.jsonp) arg.jsonp = 'jsonp';
          url += url.indexOf('?') == -1 ? '?' : '&';
          if (!isEmptyObject(data)) {
          for (var o in data) {
          url += o + '=' + data[o] + '&';
          }
          url = url.substring(0, url.length - 1);
          }
          url += '&' + arg.jsonp + '=ajax.jsonpCallback';
          var JSONP = document.createElement('script');
          JSONP.type = 'text/javascript';
          JSONP.id = 'jsonp';
          JSONP.src = url;
          document.getElementsByTagName('head')[0].appendChild(JSONP);
          if (callback) GLOBAL.callback = callback;
          return;
          } else if (type.toLowerCase() === 'get') {
          if (!isEmptyObject(data)) {
          url += url.indexOf('?') == -1 ? '?' : '&';
          for (var o in data) {
          url += o + '=' + data[o] + '&';
          }
          url = url.substring(0, url.length - 1);
          }
          var sendData = null;
          XMLHttpReq.open(type, url, async);
          } else {
          XMLHttpReq.open(type, url, async);
          XMLHttpReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
          if (!isEmptyObject(data)) {
          var sendData = '';
          for (var o in data) {
          sendData += o + '=' + data[o] + '&';
          }
          sendData = sendData.substring(0, sendData.length - 1);
          } else {
          var sendData = null;
          }
          }
          XMLHttpReq.send(sendData);
          XMLHttpReq.onreadystatechange = function () {
          //指定響應(yīng)函數(shù)
          if (XMLHttpReq.readyState == 4) {
          if (XMLHttpReq.status == 200) {
          switch (dataType.toLowerCase()) {
          case 'json':
          var res = XMLHttpReq.responseText;
          res = JSON.parse(res);
          break;
          case 'xml':
          var res = XMLHttpReq.responseXML;
          break;
          default:
          var res = XMLHttpReq.responseText;
          }
          if (callback) {
          callback(res);
          }
          }
          }
          };
          },

          //jsonp回調(diào)函數(shù)
          jsonpCallback: function (data) {
          document.getElementsByTagName('head')[0].removeChild(document.getElementById('jsonp'));
          if (GLOBAL.callback) {
          GLOBAL.callback(data);
          }
          },
          });
          })();

          12、手寫節(jié)流防抖函數(shù)

          防抖:

          function debounce(fn, delay) {
          if(typeof fn !== 'function') {
          throw new TypeError('fn不是函數(shù)')
          }
          let timer; // 維護(hù)一個(gè) timer
          return function () {
          var _this = this; // 取debounce執(zhí)行作用域的this(原函數(shù)掛載到的對(duì)象)
          var args = arguments;
          if (timer) {
          clearTimeout(timer);
          }
          timer = setTimeout(function () {
          fn.apply(_this, args); // 用apply指向調(diào)用debounce的對(duì)象,相當(dāng)于_this.fn(args);
          }, delay);
          };
          }

          input1.addEventListener('keyup', debounce(() => {
          console.log(input1.value)
          }), 600)

          節(jié)流:

          function throttle(fn, delay) {
          let timer;
          return function () {
          var _this = this;
          var args = arguments;
          if (timer) {
          return;
          }
          timer = setTimeout(function () {
          // 這里args接收的是外邊返回的函數(shù)的參數(shù),不能用arguments
          fn.apply(_this, args);
          // fn.apply(_this, arguments); 需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受類數(shù)組對(duì)象。如果傳入類數(shù)組對(duì)象,它們會(huì)拋出異常。
          timer = null; // 在delay后執(zhí)行完fn之后清空timer,此時(shí)timer為假,throttle觸發(fā)可以進(jìn)入計(jì)時(shí)器
          }, delay)
          }
          }

          div1.addEventListener('drag', throttle((e) => {
          console.log(e.offsetX, e.offsetY)
          }, 100))

          13、手寫Promise加載圖片

          function getData(url) {
          return new Promise((resolve, reject) => {
          $.ajax({
          url,
          success(data) {
          resolve(data)
          },
          error(err) {
          reject(err)
          }
          })
          })
          }
          const url1 = './data1.json'
          const url2 = './data2.json'
          const url3 = './data3.json'
          getData(url1)
          .then(data1 => {
          console.log(data1)
          return getData(url2)
          }).then(data2 => {
          console.log(data2)
          return getData(url3)
          }).then(data3 =>
          console.log(data3)
          ).catch(err =>
          console.error(err)
          )

          14、函數(shù)實(shí)現(xiàn)一秒鐘輸出一個(gè)數(shù)

          // 用var打印的都是11
          for(let i = 0; i <= 10; i++){
          setTimeout(()=>{
          console.log(i);
          }, 1000*i)
          }

          文章轉(zhuǎn)載自知乎 ,原文地址: https://zhuanlan.zhihu.com/p/436920660

          更多面試相關(guān)文章

          1. 手寫一個(gè)bind實(shí)現(xiàn)
          2. 詳解js中的new操作符
          3. 如何優(yōu)雅的處理Promise錯(cuò)誤


          瀏覽 31
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文一级久久黄色 | 蜜桃视频在线观看一区 | 狠夜干| 成人国产精品秘 久久久 | 成人AⅤ精品一区二区三区 |