整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          JavaScript中各種源碼實現(xiàn)(前端面試筆試必備)

          能夠手撕各種JavaScript原生函數(shù),可以說是進(jìn)大廠必備!同時對JavaScript源碼的學(xué)習(xí)和實現(xiàn)也能幫助我們快速扎實地提升自己的前端編程能力。

          最近很多人和我一樣在積極地準(zhǔn)備前端面試筆試,所以就整理了一些前端面試筆試中非常容易被問到的原生函數(shù)實現(xiàn)和各種前端原理實現(xiàn),其中部分源碼戳這里。

          實現(xiàn)一個new操作符

          我們首先知道new做了什么:

          1. 創(chuàng)建一個空的簡單JavaScript對象(即{});
          2. 鏈接該對象(即設(shè)置該對象的構(gòu)造函數(shù))到另一個對象 ;
          3. 將步驟(1)新創(chuàng)建的對象作為this的上下文 ;
          4. 如果該函數(shù)沒有返回對象,則返回this。

          知道new做了什么,接下來我們就來實現(xiàn)它

          function create(Con, ...args){
            // 創(chuàng)建一個空的對象
            this.obj = {};
            // 將空對象指向構(gòu)造函數(shù)的原型鏈
            Object.setPrototypeOf(this.obj, Con.prototype);
            // obj綁定到構(gòu)造函數(shù)上,便可以訪問構(gòu)造函數(shù)中的屬性,即this.obj.Con(args)
            let result = Con.apply(this.obj, args);
            // 如果返回的result是一個對象則返回
            // new方法失效,否則返回obj
            return result instanceof Object ? result : this.obj;
          }

          實現(xiàn)一個Array.isArray

          思路很簡單,就是利用Object.prototype.toString

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

          實現(xiàn)一個Object.create()方法

          function create =  function (o) {
              var F = function () {};
              F.prototype = o;
              return new F();
          };

          實現(xiàn)一個EventEmitter

          真實經(jīng)歷,最近在字節(jié)跳動的面試中就被面試官問到了,要求手寫實現(xiàn)一個簡單的Event類。

          class Event {
            constructor () {
              // 儲存事件的數(shù)據(jù)結(jié)構(gòu)
              // 為查找迅速, 使用對象(字典)
              this._cache = {}
            }
          
            // 綁定
            on(type, callback) {
              // 為了按類查找方便和節(jié)省空間
              // 將同一類型事件放到一個數(shù)組中
              // 這里的數(shù)組是隊列, 遵循先進(jìn)先出
              // 即新綁定的事件先觸發(fā)
              let fns = (this._cache[type] = this._cache[type] || [])
              if(fns.indexOf(callback) === -1) {
                fns.push(callback)
              }
              return this
              }
          
            // 解綁
            off (type, callback) {
              let fns = this._cache[type]
              if(Array.isArray(fns)) {
                if(callback) {
                  let index = fns.indexOf(callback)
                  if(index !== -1) {
                    fns.splice(index, 1)
                  }
                } else {
                  // 全部清空
                  fns.length = 0
                }
              }
              return this
            }
            // 觸發(fā)emit
            trigger(type, data) {
              let fns = this._cache[type]
              if(Array.isArray(fns)) {
                fns.forEach((fn) => {
                  fn(data)
                })
              }
              return this
            }
          
            // 一次性綁定
            once(type, callback) {
              let wrapFun = () => {
                callback.call(this);
                this.off(type, wrapFun); // 執(zhí)行完以后立即解綁
              };
              this.on(type, wrapFun); // 綁定
              return this;
            }
          }
          
          let e = new Event()
          
          e.on('click',function(){
            console.log('on')
          })
          // e.trigger('click', '666')
          console.log(e)
          

          實現(xiàn)一個Array.prototype.reduce

          先回憶一下Array.prototype.reduce語法:

          Array.prototype.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

          然后就可以動手實現(xiàn)了:

          Array.prototype.myReduce = function(callback, initialValue) {
            let accumulator = initialValue ? initialValue : this[0];
            for (let i = initialValue ? 0 : 1; i < this.length; i++) {
              let _this = this;
              accumulator = callback(accumulator, this[i], i, _this);
            }
            return accumulator;
          };
          
          // 使用
          let arr = [1, 2, 3, 4];
          let sum = arr.myReduce((acc, val) => {
            acc += val;
            return acc;
          }, 5);
          
          console.log(sum); // 15

          實現(xiàn)一個call或apply

          先來看一個call實例,看看call到底做了什么:

          let foo = {
            value: 1
          };
          function bar() {
            console.log(this.value);
          }
          bar.call(foo); // 1

          從代碼的執(zhí)行結(jié)果,我們可以看到,call首先改變了this的指向,使函數(shù)的this指向了foo,然后使bar函數(shù)執(zhí)行了??偨Y(jié)一下:

          1. call改變函數(shù)this指向
          2. 調(diào)用函數(shù)

          思考一下:我們?nèi)绾螌崿F(xiàn)上面的效果呢?代碼改造如下:

          Function.prototype.myCall = function(context) {
            context = context || window;
            //將函數(shù)掛載到對象的fn屬性上
            context.fn = this;
            //處理傳入的參數(shù)
            const args = [...arguments].slice(1);
            //通過對象的屬性調(diào)用該方法
            const result = context.fn(...args);
            //刪除該屬性
            delete context.fn;
            return result
          };

          我們看一下上面的代碼:

          1. 首先我們對參數(shù)context做了兼容處理,不傳值,context默認(rèn)值為window;
          2. 然后我們將函數(shù)掛載到context上面,context.fn = this;
          3. 處理參數(shù),將傳入myCall的參數(shù)截取,去除第一位,然后轉(zhuǎn)為數(shù)組;
          4. 調(diào)用context.fn,此時fn的this指向context;
          5. 刪除對象上的屬性 delete context.fn;
          6. 將結(jié)果返回。

          以此類推,我們順便實現(xiàn)一下apply,唯一不同的是參數(shù)的處理,代碼如下:

          Function.prototype.myApply = function(context) {
            context = context || window
            context.fn = this
            let result
            // myApply的參數(shù)形式為(obj,[arg1,arg2,arg3]);
            // 所以myApply的第二個參數(shù)為[arg1,arg2,arg3]
            // 這里我們用擴展運算符來處理一下參數(shù)的傳入方式
            if (arguments[1]) {
              result = context.fn(…arguments[1])
            } else {
              result = context.fn()
            }
            delete context.fn;
            return result
          };

          以上便是call和apply的模擬實現(xiàn),唯一不同的是對參數(shù)的處理方式。

          實現(xiàn)一個Function.prototype.bind

          function Person(){
            this.name="zs";
            this.age=18;
            this.gender="男"
          }
          let obj={
            hobby:"看書"
          }
          //  將構(gòu)造函數(shù)的this綁定為obj
          let changePerson = Person.bind(obj);
          //  直接調(diào)用構(gòu)造函數(shù),函數(shù)會操作obj對象,給其添加三個屬性;
          changePerson();
          //  1、輸出obj
          console.log(obj);
          //  用改變了this指向的構(gòu)造函數(shù),new一個實例出來
          let p = new changePerson();
          // 2、輸出obj
          console.log(p);

          仔細(xì)觀察上面的代碼,再看輸出結(jié)果。

          我們對Person類使用了bind將其this指向obj,得到了changeperson函數(shù),此處如果我們直接調(diào)用changeperson會改變obj,若用new調(diào)用changeperson會得到實例 p,并且其__proto__指向Person,我們發(fā)現(xiàn)bind失效了。

          我們得到結(jié)論:用bind改變了this指向的函數(shù),如果用new操作符來調(diào)用,bind將會失效。

          這個對象就是這個構(gòu)造函數(shù)的實例,那么只要在函數(shù)內(nèi)部執(zhí)行 this instanceof 構(gòu)造函數(shù) 來判斷其結(jié)果是否為true,就能判斷函數(shù)是否是通過new操作符來調(diào)用了,若結(jié)果為true則是用new操作符調(diào)用的,代碼修正如下:

          // bind實現(xiàn)
          Function.prototype.mybind = function(){
            // 1、保存函數(shù)
            let _this = this;
            // 2、保存目標(biāo)對象
            let context = arguments[0]||window;
            // 3、保存目標(biāo)對象之外的參數(shù),將其轉(zhuǎn)化為數(shù)組;
            let rest = Array.prototype.slice.call(arguments,1);
            // 4、返回一個待執(zhí)行的函數(shù)
            return function F(){
              // 5、將二次傳遞的參數(shù)轉(zhuǎn)化為數(shù)組;
              let rest2 = Array.prototype.slice.call(arguments)
              if(this instanceof F){
                // 6、若是用new操作符調(diào)用,則直接用new 調(diào)用原函數(shù),并用擴展運算符傳遞參數(shù)
                return new _this(...rest2)
              }else{
                //7、用apply調(diào)用第一步保存的函數(shù),并綁定this,傳遞合并的參數(shù)數(shù)組,即context._this(rest.concat(rest2))
                _this.apply(context,rest.concat(rest2));
              }
            }
          };

          實現(xiàn)一個JS函數(shù)柯里化

          Currying的概念其實并不復(fù)雜,用通俗易懂的話說:只傳遞給函數(shù)一部分參數(shù)來調(diào)用它,讓它返回一個函數(shù)去處理剩下的參數(shù)。

          function progressCurrying(fn, args) {
          
              let _this = this
              let len = fn.length;
              let args = args || [];
          
              return function() {
                  let _args = Array.prototype.slice.call(arguments);
                  Array.prototype.push.apply(args, _args);
          
                  // 如果參數(shù)個數(shù)小于最初的fn.length,則遞歸調(diào)用,繼續(xù)收集參數(shù)
                  if (_args.length < len) {
                      return progressCurrying.call(_this, fn, _args);
                  }
          
                  // 參數(shù)收集完畢,則執(zhí)行fn
                  return fn.apply(this, _args);
              }
          }
          

          手寫防抖(Debouncing)和節(jié)流(Throttling)

          節(jié)流

          防抖函數(shù) onscroll 結(jié)束時觸發(fā)一次,延遲執(zhí)行

          function debounce(func, wait) {
            let timeout;
            return function() {
              let context = this; // 指向全局
              let args = arguments;
              if (timeout) {
                clearTimeout(timeout);
              }
              timeout = setTimeout(() => {
                func.apply(context, args); // context.func(args)
              }, wait);
            };
          }
          // 使用
          window.onscroll = debounce(function() {
            console.log('debounce');
          }, 1000);
          
          節(jié)流

          節(jié)流函數(shù) onscroll 時,每隔一段時間觸發(fā)一次,像水滴一樣

          function throttle(fn, delay) {
            let prevTime = Date.now();
            return function() {
              let curTime = Date.now();
              if (curTime - prevTime > delay) {
                fn.apply(this, arguments);
                prevTime = curTime;
              }
            };
          }
          // 使用
          var throtteScroll = throttle(function() {
            console.log('throtte');
          }, 1000);
          window.onscroll = throtteScroll;

          手寫一個JS深拷貝

          乞丐版

          JSON.parse(JSON.stringfy));

          非常簡單,但缺陷也很明顯,比如拷貝其他引用類型、拷貝函數(shù)、循環(huán)引用等情況。

          基礎(chǔ)版

          function clone(target){
            if(typeof target === 'object'){
              let cloneTarget = {};
              for(const key in target){
                cloneTarget[key] = clone(target[key])
              }
              return cloneTarget;
            } else {
              return target
            }
          }

          寫到這里已經(jīng)可以幫助你應(yīng)付一些面試官考察你的遞歸解決問題的能力。但是顯然,這個深拷貝函數(shù)還是有一些問題。

          一個比較完整的深拷貝函數(shù),需要同時考慮對象和數(shù)組,考慮循環(huán)引用:

          function clone(target, map = new WeakMap()) {
            if(typeof target === 'object'){
              let cloneTarget = Array.isArray(target) ? [] : {};
              if(map.get(target)) {
                return target;
              }
              map.set(target, cloneTarget);
              for(const key in target) {
                cloneTarget[key] = clone(target[key], map)
              }
              return cloneTarget;
            } else {
              return target;
            }
          }

          實現(xiàn)一個instanceOf

          原理: L 的 proto 是不是等于 R.prototype,不等于再找 L.__proto__.__proto__ 直到 proto 為 null

          // L 表示左表達(dá)式,R 表示右表達(dá)式
          function instance_of(L, R) {
              var O = R.prototype;
            L = L.__proto__;
            while (true) {
                  if (L === null){
                      return false;
                  }
                  // 這里重點:當(dāng) O 嚴(yán)格等于 L 時,返回 true
                  if (O === L) {
                      return true;
                  }
                  L = L.__proto__;
            }
          }

          實現(xiàn)一個原型鏈繼承

          function myExtend(C, P) {
              var F = function(){};
              F.prototype = P.prototype;
              C.prototype = new F();
              C.prototype.constructor = C;
              C.super = P.prototype;
          }

          實現(xiàn)一個async/await

          原理

          就是利用 generator(生成器)分割代碼片段。然后我們使用一個函數(shù)讓其自迭代,每一個yield 用 promise 包裹起來。執(zhí)行下一步的時機由 promise 來控制

          實現(xiàn)
          function _asyncToGenerator(fn) {
            return function() {
              var self = this,
                args = arguments;
              // 將返回值promise化
              return new Promise(function(resolve, reject) {
                // 獲取迭代器實例
                var gen = fn.apply(self, args);
                // 執(zhí)行下一步
                function _next(value) {
                  asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
                }
                // 拋出異常
                function _throw(err) {
                  asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
                }
                // 第一次觸發(fā)
                _next(undefined);
              });
            };
          }

          實現(xiàn)一個Array.prototype.flat()函數(shù)

          最近字節(jié)跳動的前端面試中也被面試官問到,要求手寫實現(xiàn)。

          Array.prototype.myFlat = function(num = 1) {
            if (Array.isArray(this)) {
              let arr = [];
              if (!Number(num) || Number(num) < 0) {
                return this;
              }
              this.forEach(item => {
                if(Array.isArray(item)){
                  let count = num
                  arr = arr.concat(item.myFlat(--count))
                } else {
                  arr.push(item)
                }  
              });
              return arr;
            } else {
              throw tihs + ".flat is not a function";
            }
          };

          實現(xiàn)一個事件代理

          這個問題一般還會讓你講一講事件冒泡和事件捕獲機制

          <ul id="color-list">
              <li>red</li>
              <li>yellow</li>
              <li>blue</li>
              <li>green</li>
              <li>black</li>
              <li>white</li>
            </ul>
            <script>
              (function () {
                var color_list = document.getElementById('color-list');
                color_list.addEventListener('click', showColor, true);
                function showColor(e) {
                  var x = e.target;
                  if (x.nodeName.toLowerCase() === 'li') {
                    alert(x.innerHTML);
                  }
                }
              })();
            </script>

          實現(xiàn)一個Vue的雙向綁定

          Vue 2.x的Object.defineProperty版本

          // 數(shù)據(jù)
          const data = {
            text: 'default'
          };
          const input = document.getElementById('input');
          const span = document.getElementById('span');
          // 數(shù)據(jù)劫持
          Object.defineProperty(data, 'text', {
            // 數(shù)據(jù)變化 —> 修改視圖
            set(newVal) {
              input.value = newVal;
              span.innerHTML = newVal;
            }
          });
          // 視圖更改 --> 數(shù)據(jù)變化
          input.addEventListener('keyup', function(e) {
            data.text = e.target.value;
          });

          Vue 3.x的proxy 版本

          // 數(shù)據(jù)
          const data = {
            text: 'default'
          };
          const input = document.getElementById('input');
          const span = document.getElementById('span');
          // 數(shù)據(jù)劫持
          const handler = {
            set(target, key, value) {
              target[key] = value;
              // 數(shù)據(jù)變化 —> 修改視圖
              input.value = value;
              span.innerHTML = value;
              return value;
            }
          };
          const proxy = new Proxy(data, handler);
          
          // 視圖更改 --> 數(shù)據(jù)變化
          input.addEventListener('keyup', function(e) {
            proxy.text = e.target.value;
          });
          

          思考:Vue雙向綁定的實現(xiàn),使用 ES6 的 Proxy 相比 Object.defineProperty 有什么優(yōu)勢?

          實現(xiàn)一個Array.prototype.map()

          先看看reduce和map的使用方法

          let new_array = arr.map(function callback(currentValue[, index[,array) {}[, thisArg])
          
          let result = arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

          最常見的方式我們可以用一個for循環(huán)來實現(xiàn):

          Array.prototype.myMap = function(callback, thisArg) {
            let arr = [];
            for (let i = 0; i < this.length; i++) {
              arr.push(callback.call(thisArg, this[i], i, this));
            }
            return arr;
          };

          同樣的我們也可以用數(shù)組的reduce方法實現(xiàn)

          Array.prototype.myMap2 = function(callback, thisArg) {
            let result = this.reduce((accumulator, currentValue, index, array) => {
              accumulator.push(callback.call(thisArg, currentValue, index, array));
              return accumulator;
            }, []);
            return result;
          };

          結(jié)語

          看完如果覺得對你有幫助,勞煩點個贊哈,你的鼓勵就是我更新最大的動力!

          學(xué)習(xí)使我快樂!

          avaScript 類是在 ECMAScript 2015 中引入的,它們通常被描述為 JavaScript 現(xiàn)有原型繼承結(jié)構(gòu)的語法糖。 因此,雖然類沒有向 JavaScript 引入新的繼承模型 - 但它們確實提供了語法上的簡單性。 這種簡單性可以幫助我們生成不易出錯且可讀性更高的代碼。

          類就像函數(shù)

          類與函數(shù)非常相似。 與同時具有函數(shù)表達(dá)式和函數(shù)聲明的函數(shù)非常相似,類具有兩個組件:類表達(dá)式和類聲明。


          類聲明

          讓我們看看如何使用類聲明來定義一個類。 我們在類名后面使用 class 關(guān)鍵字:

          class Image {
            constructor(height, width) {
              this.height = height;
              this.width = width;
            }
          }


          吊裝

          函數(shù)聲明和類聲明之間的一個重要區(qū)別是那些函數(shù)聲明是提升的,而類聲明不是。 你首先需要聲明你的類然后訪問它,否則,像下面這樣的代碼會拋出一個ReferenceError:

          const p = new Image(); // ReferenceErrorclass Image{}


          類表達(dá)式

          類表達(dá)式是定義類的另一種方式。 類表達(dá)式可以命名或未命名。 請注意,賦予命名類表達(dá)式的名稱是類主體的本地名稱。 (所以它可以通過類名屬性來檢索):

          // An unnamed class expression
          let Image = class {
            constructor(height, width) {
              this.height = height;
              this.width = width;
            }
          };
          console.log(Image.name);
          // output: "Image"// A named class expression
          let MyImage = class Image {
            constructor(height, width) {
              this.height = height;
              this.width = width;
            }
          };
          console.log(MyImage.name);
          // output: "Image"

          注意:類表達(dá)式受到與前面類聲明部分中描述的相同的提升限制。


          構(gòu)造函數(shù)

          構(gòu)造函數(shù)方法是 JavaScript 中的一種特殊方法,我們使用它來創(chuàng)建和初始化使用類創(chuàng)建的對象。 我們只能在一個類中使用一個名為“constructor”的方法。

          我們的構(gòu)造函數(shù)可以使用 super 關(guān)鍵字來調(diào)用超類的構(gòu)造函數(shù)(稍后會詳細(xì)介紹?。?/p>


          實例屬性

          實例屬性必須在我們的類方法中定義:

          class Image {
            constructor(height, width) {    
              this.height = height;
              this.width = width;
            }
          }

          如果我們希望使用靜態(tài)類端屬性和原型數(shù)據(jù)屬性,這些必須在類主體聲明之外定義:

          Image.staticWidth = 50;
          Image.prototype.prototypeWidth = 55;


          字段聲明

          雖然語法仍被認(rèn)為是實驗性的(許多瀏覽器尚未采用),但公共和私有字段聲明也值得了解 - 因為您通常會使用 Babel 進(jìn)行開發(fā),它會為您轉(zhuǎn)換語法。


          公共字段聲明

          讓我們用 JavaScript 字段聲明語法重新審視我們的示例:

          class Image {
            height = 0;
            width;
            constructor(height, width) {    
              this.height = height;
              this.width = width;
            }
          }

          不同之處在于我們的字段已預(yù)先聲明。 所以我們的類定義變得更加自文檔化,并且字段總是存在的。

          注意:可以使用或不使用默認(rèn)值來聲明字段!


          私有字段聲明

          當(dāng)我們使用私有字段時,可以像這樣細(xì)化定義:

          class Image {
            #height = 0;
            #width;
            constructor(height, width) {    
              this.#height = height;
              this.#width = width;
            }
          }

          私有字段(用# 聲明)不能在類外引用,只能在類體內(nèi)引用。 這確保了你的類的用戶不能依賴內(nèi)部,這可能會隨著版本的變化而改變。

          注意:以后不能通過分配創(chuàng)建私有字段。 它們只能在字段聲明中預(yù)先聲明。


          使用“擴展”的子類

          我們可以將 extends 關(guān)鍵字與類聲明或類表達(dá)式一起使用來創(chuàng)建一個類作為另一個類的子類。

          class Vehicle{ 
            constructor(name) {
              this.name = name;
            }  sound() {
              console.log(`${this.name} makes a sound.`);
            }
          }
          class Car extends Vehicle{
            constructor(name) {
              super(name); // call the super class constructor and pass in the name parameter
            }
            sound() {
              console.log(`The ${this.name} tooted its horn!`);
            }
          }
          let c = new Car('Volkswagen');
          c.sound(); // The Volkswagen tooted its horn!

          如果子類中存在構(gòu)造函數(shù),則在使用“this”之前需要先調(diào)用 super()。

          基于函數(shù)的“類”也可以擴展:

          function Vehicle (name) {
            this.name = name;  
          }
          Vehicle.prototype.sound = function () {
            console.log(`${this.name} makes a sound.`);
          }
          class Car extends Vehicle{
            speak() {
              console.log(`The ${this.name} tooted its horn!`);
            }
          }
          let c = new Car('Volkswagen');
          c.sound(); // The Volkswagen tooted its horn!

          注意:類不能擴展常規(guī)對象! 如果要從對象繼承,請使用 Object.setPrototypeOf():

          const Vehicle = {
            sound() {
              console.log(`${this.name} makes a sound.`);
            }
          };class Car{
            constructor(name) {
              this.name = name;
            }
          }let c = new Car('Volkswagen');
          c.sound(); // Volkswagen makes a sound.


          物種

          如果你想從數(shù)組類 MyArray 中返回 Array 對象。 你可以使用“species”模式來做到這一點,它可以讓你覆蓋默認(rèn)的構(gòu)造函數(shù)。

          如果使用諸如 map() 之類的方法,它將返回默認(rèn)構(gòu)造函數(shù)。 然后你會希望這些方法返回一個父 Array 對象,而不是 MyArray 對象。 Symbol.species 讓你這樣做,就像這樣:

          class MyArray extends Array {
            // Overwrite species to the parent Array constructor
            static get [Symbol.species]() { return Array; }
          }let a = new MyArray(1,2,3);
          let mapped = a.map(x => x * x);console.log(mapped instanceof MyArray); // false
          console.log(mapped instanceof Array);   // true


          “超級”關(guān)鍵字

          super 關(guān)鍵字用于調(diào)用超類的相應(yīng)方法。 這是基于原型的繼承的優(yōu)勢之一。 讓我們看一個例子:

          class Volkswagen { 
            constructor(name) {
              this.name = name;
            }  sound() {
              console.log(`${this.name} makes a sound.`);
            }
          }class Beetle extends Volkswagen {
            sound() {
              super.sound();
              console.log(`${this.name} toots it's horn.`);
            }
          }let b = new Beetle('Herbie');
          b.sound(); // Herbie makes a sound.
          // Herbie toots it's horn.


          混音

          混入是類的模板。 一個 ECMAScript 類只能有一個超類,因此不能從工具類中進(jìn)行多重繼承。 該功能必須由超類提供。

          具有超類作為輸入和擴展該超類作為輸出的子類的函數(shù)可用于在 ECMAScript 中實現(xiàn)混合:

          let calculatorMixin = Base => class extends Base {
            calc() { }
          };let randomizerMixin = Base => class extends Base {
            randomize() { }
          };

          然后可以像這樣編寫使用這些混合的類:

          class First { }
          class Second extends calculatorMixin(randomizerMixin(First)) { ...

          我們深入研究了 JavaScript 類,包括類聲明、類表達(dá)式、構(gòu)造函數(shù)、實例屬性、字段聲明、擴展、物種、超級和混合。


          關(guān)注七爪網(wǎng),獲取更多APP/小程序/網(wǎng)站源碼資源!

          家好,今天給大家介紹一款,JavaScript實現(xiàn)的多功能側(cè)邊導(dǎo)航菜單源碼(圖1)。送給大家哦,獲取方式在本文末尾。

          圖1

          可以菜單在左邊彈出,也可以設(shè)置菜單在右邊彈出(圖2)

          圖2

          圖2

          可以設(shè)置為子菜單重疊樣式(圖3)

          圖3

          可以設(shè)置為擴展樣式(圖4)

          圖4

          可以設(shè)置為直接展開樣式(圖5)

          圖5

          自適應(yīng)各種分辨率(圖6)

          圖6

          本模板編碼:10138,需要的朋友,點擊下面的鏈接后,搜索10138,即可獲取。

          「鏈接」


          主站蜘蛛池模板: 国产aⅴ一区二区| 国偷自产Av一区二区三区吞精| 狠狠综合久久AV一区二区三区 | 久久人妻av一区二区软件| 又硬又粗又大一区二区三区视频| 无码日韩人妻av一区免费| 亚洲av一综合av一区| 无码人妻av一区二区三区蜜臀| 91久久精一区二区三区大全 | 无码人妻一区二区三区在线水卜樱 | 文中字幕一区二区三区视频播放| 无码AV中文一区二区三区| 亚洲av无码一区二区三区网站| 少妇激情一区二区三区视频 | 三级韩国一区久久二区综合| 久久精品国产一区二区| 无码人妻精品一区二区在线视频| 国产免费一区二区三区| 风流老熟女一区二区三区| 美女一区二区三区| 尤物精品视频一区二区三区 | 精品国产一区二区三区久| 国产高清一区二区三区四区| 亚洲AV乱码一区二区三区林ゆな| 欧美av色香蕉一区二区蜜桃小说 | 成人国内精品久久久久一区| 精品一区二区三区四区在线播放| 成人欧美一区二区三区在线视频| 国产在线观看一区二区三区精品| 亚洲日韩国产欧美一区二区三区 | 国产自产对白一区| 色综合视频一区二区三区44| 亚洲日韩激情无码一区| 日韩精品电影一区| 国产成人精品亚洲一区| 日韩制服国产精品一区| 精品性影院一区二区三区内射| 极品人妻少妇一区二区三区| 亚洲国产激情一区二区三区| 精品国产亚洲一区二区三区| 中文字幕精品无码一区二区三区 |