<address id="ttjl9"></address>

      <noframes id="ttjl9"><address id="ttjl9"><nobr id="ttjl9"></nobr></address>
      <form id="ttjl9"></form>
        <em id="ttjl9"><span id="ttjl9"></span></em>
        <address id="ttjl9"></address>

          <noframes id="ttjl9"><form id="ttjl9"></form>

          首頁

          死磕javascript的手寫面試題

          前端達人

          1.實現lodash的_.get方法

          function _getValue(target, valuePath, defalutVal) {
            let valueType = Object.prototype.toString.call(target)
            console.log(valueType)
            // if (valueType == "[object Array]") {
              let paths = valuePath.replace(/\[(\d+)\]/, `.$1`).split('.')
              let result = target
              for(const path of paths){
                result = Object(result)[path]
                if(result == undefined){
                  return defalutVal
                }
              }
              return result
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          測試:
          let obj = {
            a:{
              b:[
                {
                  c:2
                }
              ]
            }
          }
          
          console.log(_getValue(obj, 'a.b[0].c')) //2 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          2.寫一個函數判斷兩個變量是否相等

          function isEqual(res1, res2) {
            let a = getTypeOf(res1)
            let b = getTypeOf(res2)
            if(a !== b){
              return false
            }else if(a === 'base'){
              console.log('base',res1,res2)
              return res1 === res2
            } else if(a === 'array'){
              if(res1.length !== res2.length){
                console.log('array',res1,res2)
                return false
              }else{
                //遍歷數組的值比較
                for(let i =0;i<res1.length;i++){
                  if(!isEqual(res1[i],res2[i])){
                    console.log('array',res1[i],res2[i])
                    return false
                  }
                }
                return true
              }
              return true
            }else if(a === 'object'){
              let ak = Object.keys(a)
              let bk = Object.keys(b)
              if(ak.length !== bk.length){
                return false
              }else{
                for(let o in res1){
                  console.log(res1[o])
                  if(!isEqual(res1[o],res2[o])){
                    console.log('object',res1[o],res2[o])
                    return false
                  }
                }
                return true
              } 
            }else if(a === 'null' || a === 'undefined'){
              console.log('null')
              return true
            }else if(a === 'function'){
              console.log('function')
              return a === b
            }
          }
          
          function getTypeOf(res) {
            let type = Object.prototype.toString.call(res)
            switch (type) {
              case "[object Array]":
                return 'array'
              case "[object Object]":
                return 'object'
              case "[object Null]":
                return 'null'
              case "[object Undefined]":
                return 'undefined'
              case "[object Number]"||"[object String]"||"[object Boolean]":
                return 'base'
              case "[object Function]":
                return 'function'
              default:
                return 'typeError'
            }
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
          • 49
          • 50
          • 51
          • 52
          • 53
          • 54
          • 55
          • 56
          • 57
          • 58
          • 59
          • 60
          • 61
          • 62
          • 63
          • 64
          • 65
          • 66
          測試:
          let a = {
            a:20,
            b:{
              c:30,
              d:[1,2,3]
            }
          }
          let b = {
            a:20,
            b:{
              c:30,
              d:[1,2,3]
            }
          }
          console.log(isEqual(a,b)) //true 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16

          3.數組扁平化的方法

          function _flat(arr){
            let result = []
            for(let i = 0;i<arr.length;i++){
              if(Array.isArray(arr[i])){
                result = result.concat(_flat(arr[i]))
              }else{
                result.push(arr[i])
              }
            }
            return result;
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          let arr = [1,2,[3,4,[5,6]]]
          _flat(arr) //[1,2,3,4,5,6] 
          
          • 1
          • 2
          //es6
          function _flat2(arr){
            while(arr.some(item=>Array.isArray(item))){
              arr = [].concat(...arr)
            }
            return arr
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          let arr = [1,2,[3,4,[5,6]]]
          _flat2(arr) //[1,2,3,4,5,6] 
          
          • 1
          • 2

          4.深克隆

          簡單深克隆,不考慮內置對象和函數

          function deepClone(obj){
            if(typeof obj !== 'object') return
            let newObj = obj instanceof Array?[]:{}
            for(let key in obj){
                if(obj.hasOwnProperty(key)){
                    newObj[key] = typeof obj[key] === 'object'?deepClone(obj[key]):obj[key]
                }
            }
            return newObj
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          復雜版深度克隆 考慮內置對象 比如date regexp 函數 以及對象的循環引用的問題

          const isObject = (target) => typeof target === "object"&& target !== null;
          
          function deepClone2(target, map = new WeakMap()) {
            console.log(target)
              if (map.get(target)) {
                  return target;
              }
              // 獲取當前值的構造函數:獲取它的類型
              let constructor = target.constructor;
              // 檢測當前對象target是否與正則、日期格式對象匹配
              if (/^(RegExp|Date)$/i.test(constructor.name)) {
                  // 創建一個新的特殊對象(正則類/日期類)的實例
                  return new constructor(target);  
              }
              if (isObject(target)) {
                  map.set(target, true);  // 為循環引用的對象做標記
                  const cloneTarget = Array.isArray(target) ? [] : {};
                  for (let prop in target) {
                      if (target.hasOwnProperty(prop)) {
                          cloneTarget[prop] = deepClone(target[prop], map);
                      }
                  }
                  return cloneTarget;
              } else {
                  return target;
              }
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27

          5.數組去重

          filter去重

          function _unique(arr){
            return arr.filter((item,index,array)=>{
              return array.indexOf(item) === index
            })
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          es6 Set

          function _unique2(arr){
            return [...new Set(arr)]
          } 
          
          • 1
          • 2
          • 3

          includes

          function _unique3(arr){
            let newArr = []
            arr.forEach(item => {
                if(!newArr.includes(item)){
                  newArr.push(item)
                }
            });
            return newArr
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          雙層for循環

          function _unique4(arr){
            for(let i =0;i<arr.length;i++){
              for(let j =i+1;j<arr.length;j++){
                if(arr[i] === arr[j]){
                  arr.splice(j,1)
                  j--
                }
              }
            }
            return arr
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          indexof

          function _unique5(arr){
            let newArr = []
            for(let i = 0;i<arr.length;i++){
              if(newArr.indexOf(arr[i] === -1){
                newArr.push(arr[i])
              })
            }
            return newArr
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          6.判斷數據的類型

          function _typeOf(obj){
            let res = Object.prototype.toString.call(obj).split(' ')[1]
            let mold = res.substring(0,res.length-1).toLowerCase()
            return mold
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          _typeOf(5) //number
          _typeOf('5') //string 
          
          • 1
          • 2

          7.解析url參數為對象

          function getParamsObj(params){
            let paramsStr = params.replace(/^.+\?(.+)/,"$1")
            let paramsArr = paramsStr.split('&')
            let paramsObj = {}
          
            for(let [key,value] of paramsArr.entries()){
                if(/=/.test(value)){
                    let valArr = value.split('=')
                    val = decodeURIComponent(valArr[1]) //解碼
                    val = /^\d+$/.test(val)?parseFloat(val):val //判斷是不是數字
                    if(paramsObj.hasOwnProperty(valArr[0])){
                        paramsObj[valArr[0]] = [].concat(paramsObj[valArr[0]],val)
                    }else{
                        paramsObj[valArr[0]] = val
                    }
                }  
          
            }
            return paramsObj
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20

          8.函數柯里化

          //從一次傳入多個參數  編程多次調用每次傳入一個參數
          function add(a, b, c, d, e) {
            return a + b + c + d + e
          }
          
          function curry(fn) {
             let dFn = (...args)=>{
               if(args.length == fn.length) return fn(...args)
               return (...arg)=>{
                 return dFn(...args,...arg)
               }
             }
             return dFn
          }
          let addCurry = curry(add)
          addCurry(1,2,3)(2)(3) 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          9.圖片懶加載

          //添加了兩個功能
          // 圖片加載完成后 移除事件監聽
          // 加載完的圖片從imgList中移除
          let imgList = [...document.querySelectorAll('img')]
          let length = imgList.length
          
          const imgLazyLoad = function () {
            let count = 0
            let deleteIndexList = []
            imgList.forEach((img, index) => {
              let rect = img.getBoundingClientRect() 
              //獲取元素到視圖的距離 top元素上邊到視圖上邊的距離 left元素左邊到視圖左邊的距離  right... bottom...
              if (rect.top < window.innerHeight) {
                // img.src = img.dataset.src
                img.src = img.getAttribute('data-src')
                deleteIndexList.push(index)
                count++
                if (count === length) {
                  document.removeEventListener('scroll', imgLazyLoad)
                }
              }
            })
            imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
          }
          imgLazyLoad()
          
          document.addEventListener('scroll', imgLazyLoad) 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27

          圖片懶加載:https://juejin.cn/post/6844903856489365518#heading-19

          10節流防抖

          函數防抖 觸發高頻事件 事件在n后執行,如果n秒鐘重復執行了 則時間重置

          //簡易版
          function debounce(func,wait){
            let timer; 
            return function(){
              let context = this;
              let args = arguments;
              console.log(timer)
              clearTimeout(timer)
              timer = setTimeout(function(){
                func.apply(context,args)
              },wait)
            }
          
          }
          let btn = document.querySelector('button');
          function aa(){
            console.log(111)
          }
          btn.onclick = debounce(aa,2000) 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          // 復雜版
          // 1.取消防抖  
          // 2.立即執行功能(點擊之后立即執行函數  但是 wait時間之后在點擊才能在立即執行)  
          // 3.函數可能有返回值
          function debounce(func,wait,immediate){
            let timer,result;
          
            const debounce = function () {
              const context = this
              const args = arguments
          
              if(timer) clearTimeout(timer)
              if(immediate){
                console.log(timer)
                var callNow = !timer
                timer = setTimeout(function () {
                    timer =null
                },wait)
                if(callNow) result = func.apply(context,args)
              }else{
                timer = setTimeout(function (params) {
                  result = func.apply(context,args)
                },wait)
              }
              return result
            }
          
            debounce.cance = function () {
              clearTimeout(timer)
              timer=null
            }
          
            return debounce
          
          }
          
          let btn = document.querySelector('button');
          function aa(){
            console.log(111)
          }
          btn.onclick = debounce(aa,2000,true)``` 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41

          函數節流 觸發高頻事件 且n秒只執行一次

          //使用時間戳
          function  throttle(func,wait) {
            var context,args;
            var previous = 0
          
            return function () {
              context = this;
              args = arguments;
              let nowDate = +new Date()
              if(nowDate-previous>wait){
                func.apply(context,arguments)
                previous = nowDate
              }
            }
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          //定時器
          function throttle(func,wait) {
            var context,args;
            var timer;
            return function(){
              context = this;
              args = arguments;
              if(!timer){
                timer = setTimeout(function () {
                  timer = null;
                  func.apply(context,args)
                },wait)
              }
            }
          
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          //組合版 options.leading 為true 立即執行一次 options.trailing為true  結束之后執行一次 默認為true
          function throttle(func, wait ,options = {}) {
            var context, args, timer,result;
            var previous = 0;
          
            var later = function () {
              previous = options.leading === false ? 0 : new Date().getTime();
              timer = null;
              func.apply(context, args)
              if (!timer) context = args = null;
            }
          
            var throttle = function () {
              var now = new Date().getTime()
              if (!previous && options.leading === false) previous = now;
              context = this;
              args = arguments;
          
              //下次觸發 func 剩余的時間
              var remaining = wait - (now - previous);
              if (remaining <= 0 || remaining > wait) {
                // if (timer) {
                //   clearTimeout(timer);
                //   timer = null;
                // }
                previous = now;
                func.apply(context, args);
                if (!timer) context = args = null;
              } else if (!timer&& options.trailing !== false) {
                timer = setTimeout(later, remaining);
              }
            }
          
            throttled.cancel = function() {
              clearTimeout(timer);
              previous = 0;
              timer = null;
            }
          
            return throttle
          }
          
          function aa(e) {
            console.log(111)
            console.log(e)
          }
          
          let btn = document.querySelector('button');
          btn.onclick = throttle(aa, 2000,{
            leading:false,
            trailing:true 
          

          })

          轉自:csdn論壇 作者:Selfimpr歐

          Vue雙向數據綁定原理

          前端達人

          1. vue雙向數據綁定是通過 數據劫持,并結合 發布-訂閱模式的方法來實現的,也就是說數據和視圖同步,數據發生變化,視圖跟著變化,視圖變化,數據也隨之發生改變

          2. 核心:關于vue實現雙向數據綁定,其核心是Object.defineProperty()方法

          3. 介紹一下Object.defineProperty()方法

          1.Object.defineProperty(obj,prop,descriptor)這個語法內有三個參數,分別是obj(要定義其上屬性的對象) prop (要定義或修改的屬性)descriptor (具體的改變方法)
          2.簡單的說 就是用這個方法來定義一個值。當調用時我們使用了它里面的get方法,當我們給這個屬性賦值的時候,又用到了它里面的set方法

          var obj = {}; Object.defineProperty(obj,'hello',{ get: function(){ console.log('調用了get方法') }, set: function(newValue){ console.log('調用了set方法,方法的值是' + newValue); } }); obj.hello; // => '調用了get方法' obj.hello = 'hi'; // => '調用了set方法,方法的值是hi' 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          4. 再介紹一下發布-訂閱模式

          在這里插入圖片描述

          原文來自于這里,我說一下我自己的理解,其實發布-訂閱模式和觀察者模式很像,但是不同的是,觀察者模式只有兩個角色,而且Obsever是知道Subject的,但是在發布-訂閱模式中,他們兩卻彼此不了解,他們是在一種類似于中間件的幫助下進行通信的,換句話說,還有第三個組件,稱為代理或消息代理或事件總線,Observer和Subject都知道該組件,該組件過濾所有傳入消息并相應的分發他們。

          5. 然后就是簡單模擬實現一下Vue的雙向數據綁定

          <input type="text"> <p></p> 
          
          • 1
          • 2

          我們要對上面兩個DOM元素實現雙向數據綁定,就是當輸入inputValue時下面的p可以及時更新inputValue內容

          <script> let input = document.querySelector('input') let p = document.querySelector('p') let obj = {} let value = '' Object.defineProperty(obj, 'inputvalue', { get() { return value }, set(newValue) { input.value = newValue
                      p.innerHTML = newValue } }) // 訂閱者 DOM元素 input.value = obj.inputvalue
              p.innerHTML = obj.inputvalue // 監聽輸入的事件 input.addEventListener('keyup', function (e) { // 修改inputvalue 達到修改input.value 以及input.innerHTML // 發布者 obj.inputvalue = e.target.value // 觸發了set }) </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24

          所以在我們的代碼中,訂閱者就是頁面中的DOM元素,因為他會訂閱我們的inputvalue,而發布者就是監聽事件中的數據,一旦監聽到了數據有修改,就要發布給我們的訂閱者,也就是說輸入的數據一旦發生了變化,我們的頁面DOM元素的數據也會發生變化,所以這個中間件就是Object.defineProperty中的set方法

          6. 結果演示

          在這里插入圖片描述


          轉自:csdn 論壇 作者:Y shǔ shǔ


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服

          JavaScript學習(三十八)—面向過程與面向對象

          前端達人

          JavaScript學習(三十八)—面向過程與面向對象

          一、程序設計語言中的兩大編程思想:面向對象、面向過程

          (一)、面向過程

          • 就是指完成某個需求的時候,先分析出完成該需求時所需要經歷的步驟有哪些,然后按照步驟依次執行,最終實現我們想要的效果。這種編程思想就是面向過程,典型的面向過程的編程語言有C語言。

          (二)、面向對象

          • 就是在完成某個需求的時候,先分析完成該需求所需要涉及的對象有哪些,然后找出這些對象所具有的屬性和方法,利用這些屬性和方法幫助我們完成需求。這種編程思想就是面向對象。典型的面向對象的語言有Java、C++。

          在這里插入圖片描述

          (三)、什么是對象?

          • 所謂的對象就是指具有特定功能的無序的屬性的集合。JS中的對象有內置對象,也可以根據需求自定義對象。

          自定義對象的方式主要有以下幾種:
          字面量形式、工廠形式、構造方法

          (四)、字面量形式的創建
          格式:

          var 對象名稱={ 屬性名稱1:屬性,1,
          
              屬性名稱2:屬性值2,
          
              屬性名稱3:屬性值3,
          
              屬性名稱n:屬性值n, }; 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          沙場練兵:

          <!-- 創建一個汽車對象 1、屬性:品牌、價格、顏色等、 2、方法(功能):跑、停 --> <body> <script> var car = { brand: '寶馬', price: '100萬', color: 'red', run: function() { console.log('汽車跑起來了'); }, stop: function() { console.log('汽車停下來了'); } }; console.log(car); </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25

          在這里插入圖片描述

           <script> var person = { name: '小王', age: '18', gender: '女', eat: function() { console.log('方便面'); }, play: function() { console.log('王者榮耀'); }, study: function() { console.log('web前端'); } }; console.log(person); </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          在這里插入圖片描述

          轉自:csdn論壇 作者:乘風破浪的程序媛


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服

          JavaScript操作符in:由一個問題引發的探究

          前端達人

          事情是這樣的:大家都知道“內存泄露”這回事吧。它有幾個常見的場景:

          1. 閉包使用不當引起內存泄漏
          2. (未聲明的)全局變量
          3. 分離的DOM節點
          4. (隨意的)控制臺的打印
          5. 遺忘的定時器
          6. 循環引用

          內存泄漏需要重視,它是如此嚴重甚至會導致頁面卡頓,影響用戶體驗!

          其中第 3 點引起了我的注意 —— 我當然清楚地知道它說的是比如:“假設你手動移除了某個dom節點,本應釋放該dom節點所占用的內存,但卻因為疏忽導致某處代碼仍對該被移除節點有引用,最終導致該節點所占內存無法被釋放”的情況

          <div id="root"> <div class="child">我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') let child = document.querySelector('.child') let root = document.querySelector('#root') btn.addEventListener('click', function() { root.removeChild(child) }) </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14

          該代碼所做的操作就是點擊按鈕后移除.child的節點,雖然點擊后,該節點確實從dom被移除了,但全局變量child仍對該節點有引用,所以導致該節點的內存一直無法被釋放。

          解決辦法:我們可以將對.child節點的引用移動到click事件的回調函數中,那么當移除節點并退出回調函數的執行上文后就會自動清除對該節點的引用,自然也就不會存在內存泄漏的情況了。(這實際上是在事件中實時檢測該節點是否存在,如果不存在則瀏覽器必不會觸發remove函數的執行)

          <div id="root"> <div class="child">我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') btn.addEventListener('click', function() { let child = document.querySelector('.child') let root = document.querySelector('#root') root.removeChild(child) }) </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15

          這段代碼很完美么?不。因為它在每次事件觸發后都創建了對child和root節點的引用。消耗了內存(你完全可以想象一些人會狂點按鈕的情況…)。
          其實還有一種辦法:我們在click中去判斷當前root節點中是否還存在child子節點,如果存在,則執行remove函數,否則什么也不做!

          這就引發了標題中所說的行為。

          怎么判斷?
          遍歷?不,太過麻煩!
          不知怎的,我突然想到了 for...in 中的 in 操作符,它可以基于原型鏈遍歷對象!

          我們來還原一下當時的場景:打開GitHub,隨便找一個父節點,并獲取它:
          mygithub
          圖中畫紅框的就是我們要取的父元素,橘紅色框的就是要判斷是否存在的子元素。

          let parent=document.querySelector('.position-relative'); let child=document.querySelector('.progress-pjax-loader'); 
          
          • 1
          • 2

          這里注意,因為獲取到的是DOM節點(類數組對象),所以我們在操作前一定要先處理一下:
          object

          let p_child=[...parent.children]; 
          
          • 1

          array
          然后

          console.log(child in p_child); 
          
          • 1

          not
          ?。?!
          為什么呢?(此時筆者還沒有意識到事情的嚴重性)
          我想,是不是哪里出了問題,用es6的includes API驗證一下:

          console.log(p_child.includes(child)); 
          
          • 1

          yes
          沒錯??!
          再用一般的數組驗證一下:
          Verification
          ???
          此時,筆者才想起到MDN上查閱一番:
          mdn
          進而我發現:in操作符單獨使用時它檢測的是左側的值(作為索引)對應的值是否在右側的對象內部(屬性 & 原型上)

          回到上面的代碼中,我們發現:
          vertification_2
          這驗證了我們的結論。
          很顯然,“子元素”并不等同于“存在于原型鏈上” —— 這又引出了一個知識點:attribute和property的區別!

          所以經過一番“折騰”,源代碼還是應該直接這樣寫:

          <div id="root"> <div class="child">我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') let child = document.querySelector('.child') let root = document.querySelector('#root') let r_child = [...root.children] btn.addEventListener('click', function() { if(r_child.includes(child)){ // 或者你這里直接判斷child是否為null也可以...吧 root.removeChild(child) } }) </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17



















          轉自:csdn論壇   作者:恪愚


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服



          JavaScript逐點突破系列之this是什么?了解完這7點很多疑惑都解決

          前端達人

          前言

          本章將專門介紹與執行上下文創建階段直接相關的最后一個細節——this是什么?以及它的指向到底是什么。

          了解this

          也許你在其他面向對象的編程語言曾經看過this,也知道它會指向某個構造器(constructor)所建立的對象。但事實上在JavaScript里面,this所代表的不僅僅是那個被建立的對象。

          先來看看ECMAScript 標準規范對this 的定義:

          「The this keyword evaluates to the value of the ThisBinding of the current execution context.」
          「this 這個關鍵字代表的值為當前執行上下文的ThisBinding?!?

          然后再來看看MDN 對this 的定義:

          「In most cases, the value of this is determined by how a function is called.」
          「在大多數的情況下,this 其值取決于函數的調用方式。」

          好,如果上面兩行就看得懂的話那么就不用再往下看了,Congratulations!

          … 我想應該不會,至少我光看這兩行還是不懂。

          先來看個例子吧:

          var getGender = function() {
              return people1.gender;
          };
          
          var people1 = {
              gender: 'female',
              getGender: getGender
          };
          
          var people2 = {
              gender: 'male',
              getGender: getGender
          };
          
          console.log(people1.getGender());    // female
          console.log(people2.getGender());    // female 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          what?怎么people2變性了呢,這不是我想要的結果啊,為什么呢?

          因為getGender()返回(return)寫死了people1.gender的關系,結果自然是’female’。

          那么,如果我們把getGender稍改一下:

          var getGender = function() {
              return this.gender;
          }; 
          
          • 1
          • 2
          • 3
          • 4

          這個時候,你應該會分別得到femalemale兩種結果。

          所以回到前面講的重點,從這個例子可以看出,即便people1people2getGender方法參照的都是同一個getGender function,但由于調用的對象不同,所以執行的結果也會不同

          現在我們知道了第一個重點,**this實際上是在函數被調用時發生的綁定,它指向什么完全取決于函數的調用方式。**如何的區分this呢?

          this到底是誰

          看完上面的例子,還是有點似懂非懂吧?那接下來我們來看看不同的調用方式對 this 值的影響。

          情況一:全局對象&調用普通函數

          在全局環境中,this 指向全局對象,在瀏覽器中,它就是 window 對象。下面的示例中,無論是否是在嚴格模式下,this 都是指向全局對象。

          var x = 1
          
          console.log(this.x)               // 1
          console.log(this.x === x)         // true
          console.log(this === window)      // true 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          如果普通函數是在全局環境中被調用,在非嚴格模式下,普通函數中 this 也指向全局對象;如果是在嚴格模式下,this 將會是 undefined。ES5 為了使 JavaScript 運行在更有限制性的環境而添加了嚴格模式,嚴格模式為了消除安全隱患,禁止了 this 關鍵字指向全局對象。

          var x = 1
          
          function fn() {
              console.log(this);   // Window 全局對象
              console.log(this.x);  // 1
          }
          
          fn(); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          使用嚴格模式后:

          "use strict"     // 使用嚴格模式
          var x = 1
          
          function fn() {
              console.log(this);   // undefined
              console.log(this.x);  // 報錯 "Cannot read property 'x' of undefined",因為此時 this 是 undefined
          }
          
          fn(); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          情況二:作為對象方法的調用

          我們知道,在對象里的值如果是原生值(primitive type;例如,字符串、數值、布爾值),我們會把這個新建立的東西稱為「屬性(property)」;如果對象里面的值是函數(function)的話,我們則會把這個新建立的東西稱為「方法(method)」。

          如果函數作為對象的一個方法時,并且作為對象的一個方法被調用時,函數中的this指向這個上一級對象。

          var x = 1
          var obj = {
              x: 2,
              fn: function() {
                  console.log(this);    
                  console.log(this.x);
              }
          }
          
          obj.fn()     
          
          // obj.fn()結果打印出;
          // Object {x: 2, fn: function}
          // 2
          
          var a = obj.fn
          a()   
          
          // a()結果打印出:   
          // Window 全局對象
          // 1 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22

          在上面的例子中,直接運行 obj.fn() ,調用該函數的上一級對象是 obj,所以 this 指向 obj,得到 this.x 的值是 2;之后我們將 fn 方法首先賦值給變量 a,a 運行在全局環境中,所以此時 this 指向全局對象Window,得到 this.x 為 1。

          我們再來看一個例子,如果函數被多個對象嵌套調用,this 會指向什么。

          var x = 1
          var obj = {
            x: 2,
            y: {
              x: 3,
              fn: function() {
                console.log(this);   // Object {x: 3, fn: function}
                console.log(this.x);   // 3
              }
            }
          }
          
          obj.y.fn(); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14

          為什么結果不是 2 呢,因為在這種情況下記住一句話:this 始終會指向直接調用函數的上一級對象,即 y,上面例子實際執行的是下面的代碼。

          var y = {
            x: 3,
            fn: function() {
              console.log(this);   // Object {x: 3, fn: function}
              console.log(this.x);   // 3
            }
          }
          
          var x = 1
          var obj = {
            x: 2,
            y: y
          }
          
          obj.y.fn(); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16

          對象可以嵌套,函數也可以,如果函數嵌套,this 會有變化嗎?我們通過下面代碼來探討一下。

          var obj = {
              y: function() {
                  console.log(this === obj);   // true
                  console.log(this);   // Object {y: function}
                  fn();
          
                  function fn() {
                      console.log(this === obj);   // false
                      console.log(this);   // Window 全局對象
                  }
              }
          }
          
          obj.y(); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15

          在函數 y 中,this 指向了調用它的上一級對象 obj,這是沒有問題的。但是在嵌套函數 fn 中,this 并不指向 obj。嵌套的函數不會從調用它的函數中繼承 this,當嵌套函數作為函數調用時,其 this 值在非嚴格模式下指向全局對象,在嚴格模式是 undefined,所以上面例子實際執行的是下面的代碼。

          function fn() {
              console.log(this === obj);   // false
              console.log(this);   // Window 全局對象
          }
          
          var obj = {
              y: function() {
                  console.log(this === obj);   // true
                  console.log(this);   // Object {y: function}
                  fn();
              }
          }
          
          obj.y(); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15

          情況三:作為構造函數調用

          我們可以使用 new 關鍵字,通過構造函數生成一個實例對象。此時,this 便指向這個新對象。

          var x = 1;
          
          function Fn() {
             this.x = 2;
              console.log(this);  // Fn {x: 2}
          }
          
          var obj = new Fn();   // obj和Fn(..)調用中的this進行綁定
          console.log(obj.x)   // 2 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          使用new來調用Fn(..)時,會構造一個新對象并把它(obj)綁定到Fn(..)調用中的this。還有值得一提的是,如果構造函數返回了非引用類型(string,number,boolean,null,undefined),this 仍然指向實例化的新對象。

          var x = 1
          
          function Fn() {
            this.x = 2
          
            return {
              x: 3
            }
          }
          
          var a = new Fn()
          
          console.log(a.x)      // 3 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14

          因為Fn()返回(return)的是一個對象(引用類型),this 會指向這個return的對象。如果return的是一個非引用類型的值呢?

          var x = 1
          
          function Fn() {
            this.x = 2
          
            return 3
          }
          
          var a = new Fn()
          
          console.log(a.x)      // 2 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          情況四:call 和 apply 方法調用

          如果你想改變 this 的指向,可以使用 call 或 apply 方法。它們的第一個參數都是指定函數運行時其中的this指向。如果第一個參數不傳(參數為空)或者傳 null 、undefined,默認 this 指向全局對象(非嚴格模式)或 undefined(嚴格模式)。

          var x = 1;
          
          var obj = {
            x: 2
          }
          
          function fn() {
              console.log(this);
              console.log(this.x);
          }
          
          fn.call(obj)
          // Object {x: 2}
          // 2
          
          fn.apply(obj)     
          // Object {x: 2}
          // 2
          
          fn.call()         
          // Window 全局對象
          // 1
          
          fn.apply(null)    
          // Window 全局對象
          // 1
          
          fn.call(undefined)    
          // Window 全局對象
          // 1 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31

          使用 call 和 apply 時,如果給 this 傳的不是對象,JavaScript 會使用相關構造函數將其轉化為對象,比如傳 number 類型,會進行new Number()操作,如傳 string 類型,會進行new String()操作,如傳 boolean 類型,會進行new Boolean()操作。

          function fn() {
            console.log(Object.prototype.toString.call(this))
          }
          
          fn.call('love')      // [object String]
          fn.apply(1)          // [object Number]
          fn.call(true)          // [object Boolean] 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          call 和 apply 的區別在于,call 的第二個及后續參數是一個參數列表,apply 的第二個參數是數組。參數列表和參數數組都將作為函數的參數進行執行。

          var x = 1
          
          var obj = {
            x: 2
          }
          
          function Sum(y, z) {
            console.log(this.x + y + z)
          }
          
          Sum.call(obj, 3, 4)       // 9
          Sum.apply(obj, [3, 4])    // 9 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13

          情況五:bind 方法調用

          調用 f.bind(someObject) 會創建一個與 f 具有相同函數體和作用域的函數,但是在這個新函數中,新函數的 this 會永久的指向 bind 傳入的第一個參數,無論這個函數是如何被調用的。

          var x = 1
          
          var obj1 = {
              x: 2
          };
          var obj2 = {
              x: 3
          };
          
          function fn() {
              console.log(this);
              console.log(this.x);
          };
          
          var a = fn.bind(obj1);
          var b = a.bind(obj2);
          
          fn();
          // Window 全局對象
          // 1
          
          a();
          // Object {x: 2}
          // 2
          
          b();
          // Object {x: 2}
          // 2
          
          a.call(obj2);
          // Object {x: 2}
          // 2 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33

          在上面的例子中,雖然我們嘗試給函數 a 重新指定 this 的指向,但是它依舊指向第一次 bind 傳入的對象,即使是使用 call 或 apply 方法也不能改變這一事實,即永久的指向 bind 傳入的第一次參數。

          情況六:箭頭函數中this指向

          值得一提的是,從ES6 開始新增了箭頭函數,先來看看MDN 上對箭頭函數的說明

          An arrow function expression has a shorter syntax than a function expression and does notbind its ownthis,arguments,super, ornew.target. Arrow functions are always anonymous. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

          這里已經清楚了說明了,箭頭函數沒有自己的this綁定。箭頭函數中使用的this,其實是直接包含它的那個函數或函數表達式中的this。在前面情況二中函數嵌套函數的例子中,被嵌套的函數不會繼承上層函數的 this,如果使用箭頭函數,會發生什么變化呢?

          var obj = {
            y: function() {
                  console.log(this === obj);   // true
                  console.log(this);           // Object {y: function}
          
                var fn = () => {
                    console.log(this === obj);   // true
                    console.log(this);           // Object {y: function}
                }
                fn();
            }
          }
          
          obj.y() 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15

          和普通函數不一樣,箭頭函數中的 this 指向了 obj,這是因為它從上一層的函數中繼承了 this,你可以理解為箭頭函數修正了 this 的指向。所以箭頭函數的this不是調用的時候決定的,而是在定義的時候處在的對象就是它的this。

          換句話說,箭頭函數的this看外層的是否有函數,如果有,外層函數的this就是內部箭頭函數的this,如果沒有,則this是window。

          var obj = {
            y: () => {
                  console.log(this === obj);   // false
                  console.log(this);           // Window 全局對象 
          
                var fn = () => {
                    console.log(this === obj);   // false
                    console.log(this);           // Window 全局對象 
                }
                fn();
            }
          }
          
          obj.y() 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15

          上例中,雖然存在兩個箭頭函數,其實this取決于最外層的箭頭函數,由于obj是個對象而非函數,所以this指向為Window全局對象。

          同 bind 一樣,箭頭函數也很“頑固”,我們無法通過 call 和 apply 來改變 this 的指向,即傳入的第一個參數被忽略

          var x = 1
          var obj = {
              x: 2
          }
          
          var a = () => {
              console.log(this.x)
              console.log(this)
          }
          
          a.call(obj)       
          // 1
          // Window 全局對象
          
          a.apply(obj)      
          // 1
          // Window 全局對象 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18

          上面的文字描述過多可能有點干澀,那么就看以下的這張流程圖吧,我覺得這個圖總結的很好,圖中的流程只針對于單個規則。

          小結

          本篇文章介紹了 this 指向的幾種情況,不同的運行環境和調用方式都會對 this 產生影響??偟膩碚f,函數 this 的指向取決于當前調用該函數的對象,也就是執行時的對象。在這一節中,你需要掌握:

          • this 指向全局對象的情況;
          • 嚴格模式和非嚴格模式下 this 的區別;
          • 函數作為對象的方法調用時 this 指向的幾種情況;
          • 作為構造函數時 this 的指向,以及是否 return 的區別;
          • 使用 call 和 apply 改變調用函數的對象;
          • bind 創建的函數中 this 的指向;

          • 箭頭函數中的 this 指向。
          • 轉自:csdn 論壇  作者:蛋黃酥要不要來一口阿

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服

          vue.js前端框架:Vue.js簡介

          前端達人

          1 Vue.js概述

          • Vue.js(讀音 /vju?/,類似于 view) 是一套用于構建用戶界面的漸進式框架。與其他重量級框架不同的是,它只關注視圖層(View層),采用自底向上增量開發的設計。Vue.js的目標是通過盡可能簡單的API實現相應的數據綁定和組合的視圖組件。它不僅容易上手,還非常容易與其他庫或已有項目進行整合。另一方面,當與現代化的工具鏈以及各種支持類庫結合使用時,Vue .js也完全能夠為復雜的單頁應用提供驅動。

          1.1 什么是Vue.js

          • Vue.js實際上是一個用于開發Web前端界面的庫,其本身具有響應式編程和組件化的特點。所謂響應式編程,即保持狀態和視圖的同步。響應式編程允許將相關模型的變化自動反映到視圖上,反之亦然。Vue.js采用的是MVVM(Model-View-ViewModel)的開發模式。與傳統的MVC開發模式不同,MVVM將MVC中的Controller改成了ViewModel。在這種模式下,View的變化會自動更新到ViewModel,而ViewModel的變化也會自動同步到View上進行顯示。
          • ViewModel模式的示意圖如下圖所示:
            在這里插入圖片描述
          • 與ReactJS一樣,Vue.js同樣擁有”一切都是組件“的理念。應用組件化的特點,可以將任意封裝好的代碼注冊成標簽,這樣就在很大程度上減少了重復開發,提高了開發效率和代碼復用性。如果配合Vue.js的周邊工具vue-loader,可以將一個組件的HTML、CSS、JavaScript代碼都寫在一個文件中,這樣可以實現模塊化的開發。

          1.2 Vue.js的特性

          • Vue.js的主要特性如下:
          •  輕量級
            相比較AngularJS和ReactJS而言,Vue.js是一個更輕量級的前端庫。不但容量非常小,而且沒有其它的依賴。
          •  數據綁定
            Vue.js最主要的特點就是雙向的數據綁定。在傳統的Web項目中,將數據在視圖層展示出來后,如果需要再次修改視圖,需要通過獲取DOM的方法進行修改,這樣才能維持數據和視圖的一致。而Vue.js是一個響應式的數據綁定系統,在建立綁定后,DOM將和Vue對象中的數據保持同步,這樣就無須手動獲取DOM的值再同步到 js中。
          •  應用指令
            同AngularJS一樣,Vue.js也提供了指令這一概念。指令用于在表達式的值發生變化時,將某些行為應用到綁定的DOM上,通過對應表達式值的變化就可以修改對應的DOM。
          •  插件化開發
            與AngularJS類似,Vue,js也可以用來開發一個完整的單頁應用。在Vue.js的核心庫中并不包含路由、Ajax等功能,但是可以非常方便地加載對應插件來實現這樣的功能。例如,vue-router插件提供了路由管理的功能,vue-resource插件提供了數據請求的功能。

          2 Vue.js的安裝

          2.1 直接下載并使用 script 標簽引入

          • Vue.js官網:https://cn.vuejs.org/v2/guide/
          • 在Vue.js的官方網站中可以直接下載vue.js文件并使用 script標簽引入。
            1、下載Vue.js
            Vue.js是一個開源的庫,可以從它的官方網站中下載。下面介紹具體的下載步驟:
            (1)打開Vue.js的官方網站,進入到Vue.js的下載頁面,找到如下圖1.2所示的內容。
            在這里插入圖片描述
            (2)根據開發者的實際情況選擇不同的版本進行下載。這里以下載開發版本為例,在”開發版本“按鈕上單擊鼠標右鍵,如下圖1.3所示。
            在這里插入圖片描述
            (3)在彈出的快捷菜單中單擊“連接另存為”選項,彈出下載對話框,如下圖1.4所示,單擊對話框中的“保存”按鈕,將Vue.js文件下載到本地計算機上。在這里插入圖片描述此時下載的文件為完整不壓縮的開發版本。如果在開發環境下,推薦使用該版本,因為該版本中包含所有常見錯誤相關的警告。如果在生產環境下,推薦使用壓縮后的生產版本,因為使用生產版本可以帶來比開發環境下更快的速度體驗。
          • 引入Vue.js
            將Vue.js下載到本地計算機后,還需要在項目中引用Vue.js。即將下載后的vue.js文件放置到項目的指定文件夾中。通常文件放置在JS文件夾中,然后在需要應用vue.js文件的頁面中使用下面的語句,將其引入到文件中。
          <script type="text/javascript" src="JS/vue.js"></script> 
          
          • 1

          注意:引入Vue.js的 script 標簽,必須放在所有的自定義腳本文件的script 之前,否則在自定義的腳本代碼中應用步到Vue.js。

          2.2 使用CDN方法

          • 在項目中使用Vue.js,還可以采用引用外部CDN文件的方式。在項目中直接通過 script 標簽加載CDN文件,通過CDN文件引用Vue.js的代碼如下:
          <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js"></script> 
          
          • 1

          注意:為了防止出現外部CDN文件不可用的情況,還是建議用戶將Vue.js下載到本地計算機中。

          2.3 使用NPM方法

          • 在使用Vue.js構建大型應用時,推薦使用NPM方法進行安裝,執行命令如下:
          npm install vue 
          
          • 1

          注意:使用NPM方法安裝Vue.js需要在計算機中安裝node.js。
          node;js官網:https://nodejs.org/en/,通過node.js官網下載之后,傻瓜式安裝即可。

          3 前端框架開發工具

          • 利用Vue.js進行前端框架開發的常用工具有如下幾個:WebStorm、IDEA、Vscode

          • 前端框架開發常用的工具下載:
            (1)WebStorm官網:https://www.jetbrains.com/webstorm/
            (2)IDEA官網:https://www.jetbrains.com/idea/
            (3)Vscode官網:https://vscode.en.softonic.com/

          • 轉自:csdn 作者:小白_xm

          JS筆記之快樂的數組

          前端達人


          數組(Array)

          概念:數組是一組數據的集合,其中每一個數據稱為數組元素,js的數組可以放任意數據類型的元素,數組是一種將一組數據元素存儲在單個變量名下的優雅方式。

          示例解釋:

          //普通變量名只能存一個數據 var num = 10 //數組存儲一組數據 var arr = [1,2,3,4,5,'a','b','c'] 
          
          • 1
          • 2
          • 3
          • 4

          一、數組的創建

          數組的創建方式:
          1、通過new關鍵字創建(不常用)

          //注意A要大寫 var 數組名 = new Array() var arr = new Array()//創建一個空數組 var arr = new Array(5)//創建一個長度為5的空數組 
          
          • 1
          • 2
          • 3
          • 4

          2、通過數組字面量創建(常用)

          //1、使用數組字面量創建空數組 var 數組名 = [] //2、使用數組字面量創建帶初始值的數組 var 數組名 = [元素1,元素2,元素3···] 如:var arr = [1,3,5,'a','c','e',7,9]//數組元素可以是任意數據類型的 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          小結:
          1.數組是一組數據的集合,是將一組數據存放在同一變量的優雅方式
          2.數組的字面量:[ ]
          3.聲明數組并賦值稱為數組的初始化
          4.數組中元素可以是任意類型的數據
          5.注意:數組元素為字符或字符串要加引號,不加的話解釋器會認為是變量從而報錯

          二、數組元素的訪問

          1.通過數組索引訪問

          索引(下標):用來存放數組元素的序號(數組下標從0開始)
                  我們可以通過索引來訪問、設置、修改相應的元素,注意數組的長度可以不指定,它可以隨元素的增加自動擴容,當訪問未賦值的元素時,返回undefined。獲取方式: 數組名[索引號]
          下面通過例子來演示一下:

          //定義數組 var arr = [1,3,5,'a','b','c']---->//注意字符串要加引號,不然解釋器會認為是變量而報錯 console.log(arr)---------------->Array(6)//輸出數組長度 console.log(arr[0])------------->1 console.log(arr[2])------------->5 console.log(arr[4])------------->b
           console.log(arr[6])---------- -->undefined//索引下標6沒有元素,所以返回undefined 注意:數組的下標從0開始喲?。?! 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          2.通過循環遍歷

          請看下面代碼:

          var arr = ["red","blue","green"] console.log(arr[0]) console.log(arr[1]) console.log(arr[2]) //輸出結果:red blue green 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          從上面代碼觀察發現,取出每一個元素只是數組下標改變而已,如果數組元素多的話,一行一行訪問速度很慢,為提高效率,我們可以用循環

          //1、使用for循環遍歷 var arr = ["red","blue","green"] for(var i = 0;i<arr.length;i++){//length是數組的屬性,即為數組長度,沒有() console.log(arr[i]) } //輸出結果:red blue green 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          //2、使用for in循環遍歷 var arr = ["red","blue","green"] for(var i in arr){ console.log(arr[i]) } //輸出結果:red blue green 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          以上訪問數組元素的結果是一樣的,使用循環可是很便利的
          下面做兩個小案例來演示下數組的魔力吧!
          案例1
          使用數組對一組數求和并求平均值,思路:定義2個變量,一個存總和,一個存平均數,通過遍歷數組求和,然后除以數組長度求平均數

          案例1實現代碼 //使用數組對一組數求和并求平均值 var arr = [1,3,5,7,9,2,4,6,8] //聲明一個數組并初始化 var sum = 0; //創建一個變量來存取各數字之和 var avg = 0; //創建一個變量來存取平均數 for(i=0;i<arr.length;i++){ sum += arr[i]; //相當于 sum = sum + arr[i] } console.log("sum:"+sum) //打印sum的值,即所有數的和 avg = sum/arr.length //求平均值并賦值給avg(arr.lengt是數組的個數) console.log("avg:"+avg) //打印avg的值,即所有數的平均數 //輸出結果:sum:45 avg:5(這里僅是演示,沒有對結果為多位小數進行處理) 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          案例2
          使用數組對一組數求最大值,圖例思路:把數組第一個元素賦值給max,依次遍歷比較,如果有元素大于max,則把此元素賦值給max,遍歷后比完max即為最大值
          在這里插入圖片描述

          案例2實現 //使用數組對一組數求最大值 var arr = [1,3,5,7,9,2,4,6,8] //聲明一個數組并初始化 var max=arr[0]; //創建一個變量來存取最大值,把arr[0],即1賦值給max for(i=01;i<arr.length;i++){ if(arr[i]>max){ max=arr[i]//判斷是否有數組元素大于max,有的話就把大于max的值賦值給max } } console.log("max:"+max) //打印max的值,即數組中最大值 //輸出結果:max:9 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          三、數組的操作

          1.數組長度及數組擴容

          1. 使用length屬性獲取數組長度
          // 注意!length是屬性,不是函數,所以不用加() var arr = [1,2,3,4,5]; console.log(arr.length) //輸出結果:5 通常數組的length屬性跟數組遍歷一起使用,使用循環遍歷數組要注意
          數組元素最后一位的索引下標是數組長度-1(arr.length-1) 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          1. 使用length屬性實現數組擴容
          // 創建數組,通過length屬性擴容數組 var arr = ['a','b','c','d']; console.log(arr.length)//輸出結果為4,數組長度為4 console.log(arr[3]) //輸出數組最后一位元素的值為d arr.length=6; //數組長度擴容為6 console.log(arr) //輸出結果為6,數組長度為6,第四第五個元素的值為empty console.log(arr[4]) //未賦值,默認為undefined console.log(arr[5]) //未賦值,默認為undefined 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          輸出結果入圖
          在這里插入圖片描述
          3. 通過索引號實現數組擴容

          // 創建數組,通過索引號擴容數組 var arr = ['a','b','c','d']; console.log(arr.length)//輸出結果為4,數組長度為4 console.log(arr[3]) //輸出數組最后一位元素的值為d arr[4]='A' //給第五個元素進行賦值,因為第5個元素沒被占用,所以會在數組后面追加元素 arr[5]='B' //給第六個元素進行賦值 arr[10] = 'C' //給第11個元素進行賦值 console.log(arr) console.log(arr[6]) 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          運行結果如圖,可以發現,JS數組可以直接通過索引號進行擴容,而未賦值的元素默認為empty,返回結果是undefined,如果元素不存在,則在數組后面追加元素,若已經存在,則替換數組元素–>arr[3]

          在這里插入圖片描述

          注意:不要直接給數組名賦值,否則會覆蓋原有數組!

          // 演示 var arr=[1,2,3,4,5] console.log(arr)//打印原數組 arr="有點意思!" console.log(arr) 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          在這里插入圖片描述


          2.數組的常用方法

           1. shift()刪除數組中的第一個元素并且返回被刪除的元素,
              同時其他元素的索引向前移動一位 
          
          • 1
          • 2
          var fruits = ['banana', 'orange', 'apple', 'mango'] console.log(fruits) //打印原數組 var x = fruits.shift(); //首元素刪除 console.log(fruits) //刪除后的數組,數組元素向前索引移動一位 console.log(x) //打印被刪除元素的值banana 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          結果如圖:
          在這里插入圖片描述

           2. pop()刪除數組中的最后一個元素并且返回被刪除的元素 
          
          • 1
          var fruits = ['banana', 'orange', 'apple', 'mango'] console.log(fruits) //打印原數組 var x = fruits.pop(); //末尾元素刪除 console.log(fruits) //刪除后的數組 console.log(x) //打印被刪除元素的值mango 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          結果如圖:
          在這里插入圖片描述

           3. unshift(參數1,參數2···)向數組的開頭添加一個或者更多的元素并且返回新的長度,
              同時數組元素的索引向后移動 
          
          • 1
          • 2
          var fruits = ['orange', 'apple', 'mango'] console.log(fruits) //打印原數組 var x = fruits.unshift('banana'); //添加后返回數組長度 console.log(fruits) //添加后的數組 console.log(x) //打印新數組的總長度 
          
          • 1
          • 2
          • 3
          • 4
          • 5

          結果如圖:
          在這里插入圖片描述

           4. push()向數組末尾增添一個或者多個數組元素,并且返回數組的長度 
          
          • 1
          var fruits = ['orange', 'apple', 'mango'] console.log(fruits) //打印原數組 var x = fruits.push('banana'); //添加一個數后返回數組長度 console.log(fruits) //添加一個元素后的數組 console.log(x) //打印添加一個數后新數組的總長度 var x = fruits.push('pineapple','strawberry');//添加多個元素后返回數組長度 console.log(fruits) //添加多個元素后的數組 console.log(x) //添加多個元素后的總長度 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          結果如圖
          在這里插入圖片描述

           5. reverse()翻轉數組 
          
          • 1
           var fruits = ['orange', 'apple', 'mango'] console.log(fruits); //打印原數組 console.log(fruits.reverse()) //打印翻轉后的數組 
          
          • 1
          • 2
          • 3

          結果如圖:
          在這里插入圖片描述
          6. sort()數組排序,對字符串排序,比較的是ASCII碼的順序

           1>如["a", "A", "1"], 按ASCII碼順序排列后為["1", "A", "a"](數字>大寫字母>小寫字母)
           2>如["ab", "ac", "abc"], 排序后為["ab", "abc", "ac"]
           3>字符串數字:如["10", "5", "18" , "50"], 排序后為["10", "18", "5", "50"]
           比較方法:
          (第一位相同比較第二位,b>c,因此ab和abc在ac之前;再比較ab與abc, ab沒有第三位,因此排在abc之前)!!!
          
          如需要對字符串形式的數字進行排序,可以采用以下兩種方式:
              1、統一格式,補足位數,如["10", "5", "18" , "50"]中, 將"5"寫為"05"
              2、轉換為數字,使用特殊方法對數字排序 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
           var arr1 = ["a", "A", "1"] var arr2 = ["ab", "ac", "abc"] var arr3 = ["10", "5", "18", "50"] var arr4 = ["10", "05", "18", "50"] console.log(arr1.sort()); console.log(arr2.sort()); //比較方法:按ASCII碼比較排序  console.log(arr3.sort()); console.log("數字排序(5改成'05'):" + arr4.sort()); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          結果如圖:
          在這里插入圖片描述

          3.拓展:數字排序

          1、匿名函數當做參數使用數組排序(一維數組)

          //語法格式:
          arr.sort ( function (a, b) {   //arr為需要排序的數組名
              return a - b               //升序
          })  
          根據比較函數返回值正負來決定排列順序,為正升序,為負降序
          使用如下: 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
           function sortby(a, b) { return a - b //升序 } //降序就改成return b-a var arr = [10, 5, 8, 50]; arr.sort(sortby)//把排序匿名函數當做參數進行調用 console.log(arr);//輸出排序后的結果 //匿名函數的函數名任意 結果如圖: 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          在這里插入圖片描述

          2、匿名函數當做參數使用數組排序(二維數組)

          //語法格式:
          arr.sort (function (a, b) {   //arr為需要排序的數組名
              return a[1] - b [1]      //升序,降序改為b[1]-a[1]
          })  
          使用如下: 
          
          • 1
          • 2
          • 3
          • 4
          • 5
           function sortby(a, b) { return a[1] - b[1] //升序 } //降序就改成return b-a var arr = [["Alice", "95"], ["Bob", "80"], ["Cart", "100"]] arr.sort(sortby)//把排序匿名函數當做參數進行調用 console.log(arr);//輸出排序后的結果 //匿名函數的函數名任意 結果如圖: 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          在這里插入圖片描述

          3.拓展:數組篩選(巧用length屬性)

          將數組[2,4,5,6,8,9,12,15,3,7,26,14]中大于10的元素存入新數組
          //分析:
              1、創建一個數組arr=[2,4,5,6,8,9,12,15,3,7,26,14]
              2、創建一個新數組newArr來存放大于10的元素
              3、遍歷舊數組,找出大于10的元素
              4、依次添加到新數組中 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          // 方法1,通過變量來增加數組長度 var arr = [2,4,5,6,8,9,12,15,3,7,26,14] //創建一個數組 var newArr=[] //創建空的新數組,用于存放大于10的元素 var j =0 ; //定義變量,用于增加新數組的長度 for(var i = 0 ;i<arr.length;i++){ //for循環遍歷數組 if(arr[i]>10){ //判斷數組元素是否大于10 newArr[j]=arr[i] //如果arr數組元素>10,則存入新數組 j++; //當存入元素,長度+1,即j++ } } console.log(newArr)//打印新數組 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          // 方法2,巧用length屬性 var arr = [2,4,5,6,8,9,12,15,3,7,26,14] //創建一個數組 var newArr=[] //創建空的新數組,用于存放大于10的元素 for(var i = 0 ;i<arr.length;i++){ //for循環遍歷數組 if(arr[i]>10){ //判斷數組元素是否大于10 newArr[newArr.length]=arr[i]//如果arr數組元素>10,則存入新數組 } //巧用length屬性,數組索引從0開始增加 } console.log(newArr)//打印新數組 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9

          結果如圖:
          在這里插入圖片描述

          四、二維數組

          概念:二維數組就是一維數組的每個元素是一個一維數組

          1.二維數組的創建與訪問

          // 創建與訪問 
          
          • 1
          var fruits = [["1", "Banana"], ["2", "Orange"]];//創建二維數組 var element = ["3", "Apple"]; fruits[2] = element; //給第三個元素賦值 console.log(fruits); //打印二維數組 console.log(fruits[0]); // ["1", "Banana"]//打印第一個元素(數組) console.log(fruits[0][1]); // "Banana"   //打印第一個元素的第2個值 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          結果如圖:
          在這里插入圖片描述

          2.遍歷二維數組

          通過for in循環遍歷二維數組

          var fruits = [["1", "Banana"], ["2", "Orange"], ["3", "Apple"]]; for (var i in fruits) {//控制外層數組元素 for (var j in fruits[i]) {//控制內層數組元素 console.log(fruits[i][j]);//輸出數值中每一個元素 } } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          結果如圖:
          在這里插入圖片描述
          小結:二維數組跟一維數組使用差不多,只是稍微麻煩一點,訪問二維數組的具體元素值:arr[外層數組索引][內層數組索引]
          遍歷二維數組使用雙重for循環

          3.使用二維數組進行排序(冒泡排序法)

          圖例解釋:
          在這里插入圖片描述

          //代碼實現如下 var arr = [5,4,3,2,1]; for (var i = 0; i < arr.length - 1; i++) {//外層循環控制交換趟數,趟數為數組長度-1 for (var j = 0; j < arr.length - 1 - i; j++) {//內層循環控制每一趟需要交換的次數 if (arr[j] > arr[j + 1]) {//判斷前面一個數是否大于后面一個數,如果是則交換兩數 var temp = arr[j];//臨時變量,用于交換兩數的臨時存取 arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } console.log(arr);//輸出排序后的結果 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          結果如圖:
          在這里插入圖片描述

          總結

          本次筆記主要記錄了數組的基本使用,但是數組的妙用遠不止這些,需要我們自己去探索。


          轉自:csdn論壇 作者:想要搞錢

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服


          JavaScript基礎:手寫實現JSONP

          前端達人

          在這里插入圖片描述

          一、原理

          jsonp的核心原理是利用script標簽沒有同源限制的方式,可以發送跨域的get請求(只能發送get請求)。script標簽中的src屬性將請求參數和當前請求的回調函數名拼接在鏈接上。最終由服務端接收到請求之后拼接成前端可執行的字符串的形式返回。這個結果字符串最終會在前端的script標簽中解析并執行。

          利用<script>標簽沒有跨域限制的漏洞,網頁可以得到從其他來源動態產生的JSON數據。JSONP請求一定需要對方的服務器做支持才可以。

          1. JSONPAJAX對比: JSONPAJAX相同,都是客戶端向服務端發送請求,從服務端獲取數據的方式。但AJAX屬于同源策略,JSONP屬于非同源策略(跨域請求)。
          2. JSONP優缺點: JSONP優點是簡單兼容性好,可用于解決主流瀏覽器的跨域數據訪問的問題。缺點是僅僅支持get方法具有局限性,不安全,可能會遭受XSS攻擊。

          二、實現

          1.正常實現

          1. jsonp回調函數的名稱callbackName拼接到src
          2. 構建一個script標簽,設置它的src屬性
          3. 在全局設置一個callbackName回調函數,等待script標簽請求結束,并調用
           /**
               * jsonp獲取請求數據
               * @param {object}options
               */ function jsonp(options) { // console.log(options); // 1. 產生不同的函數名(函數名隨機) let callBackName = 'itLike' + Math.random().toString().substr(2)+Math.random().toString().substr(2); // console.log(callBackName); // 2. 產生一個全局函數 window[callBackName] = function (params) { // console.log(params); // console.log(options.success); if(params !== null){ options.success(params); }else{ options.failure(params); } // 2.1 刪除當前腳本標簽 scriptE.remove(); // 2.2 將創建的全局函數設為null window[callBackName] = null; }; // 3. 取出url地址 let jsonpUrl; if(options.data !== undefined){ jsonpUrl = options.url + '?' + options.data + '&callBack=' + callBackName; }else { jsonpUrl = options.url + '?callBack=' + callBackName; } // console.log(jsonpUrl); // 4. 創建script標簽 let scriptE = document.createElement('script'); scriptE.src = jsonpUrl; document.body.appendChild(scriptE); }  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40

          服務端(express)

          router.get('/api/v1', function(req, res, next) { res.json({ 'name': '前端收割機', 'address': '廣東', 'intro': '前端技術交流公眾號' }); });  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

          調用jsonp

          btn.addEventListener('click', ()=>{ jsonp({ url: 'http://localhost:3000/api/v1', data: 'name=前端收割機&age=20', success: function (data) { console.log(data); }, failure:function(data){ console.log(數據請求失敗); } }); });  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          2.利用promise封裝

          1. 聲明一個回調函數,其函數名(如show)當做參數值,要傳遞給跨域請求數據的服務器,函數形參為要獲取目標數據(服務器返回的data)
          2. 創建一個 <script src=>標簽 ,把那個跨域的API數據接口地址,賦值給scriptsrc, 還要在這個地址中向服務器傳遞該函數名(可以通過問號傳參?callback=show)。
          3. 服務器接收到請求后,需要進行特殊的處理:把傳遞進來的函數名和它需要給你的數據拼接成一個字符串,例如:傳遞進去的函數名是show,它準備好的數據是 show('前端收割機')。
          4. 最后服務器把準備的數據通過HTTP協議返回給客戶端,客戶端再調用執行之前聲明的回調函數(show),對返回的數據進行操作。
          /**
          * jsonp獲取請求數據
          * @param {string}url
          * @param {object}params
          * @param {function}callback
          */ function jsonp({ url, params, callback }) { return new Promise((resolve, reject) => { let script = document.createElement('script'); params = JSON.parse(JSON.stringify(params)); let arrs = []; for (let key in params) { arrs.push(`${key}=${params[key]}`); } arrs.push(`callback=${callback}`); script.src = `${url}?${arrs.join('&')}`; document.body.appendChild(script); window[callback] = function (data) { resolve(data); document.body.removeChild(script); } }) }  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24

          服務器(express

          // 后端響應 // 這里用到了 express var express = require('express'); var router = express.Router(); var app = express(); router.get('/say',function(req,res,next) { //要響應回去的數據 let data = { username : 'zs', password : 123456 } let {wd , callback} = req.query; console.log(wd); console.log(callback); // 調用回調函數 , 并響應 res.end(`${callback}(${JSON.stringify(data)})`); }) app.use(router); app.listen(3000);  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21

          調用jsonp

          // 前端調用 btn.addEventListener('click', ()=>{ jsonp({ url: 'http://localhost:3000/say', params: { wd: '前端收割機' }, callback: 'show' }).then(data => { console.log(data) }); }); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          三、總結

          基本原理:利用 script 標簽的 src 沒有跨域限制來完成實現。

          優缺點:只能 GET;兼容性好。

          簡單實現:通過 url, params, callback 來定義 JSONP() 方法的參數。



          轉自:csdn論壇 作者:imagine_tion

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 平面設計服

          一文帶你了解如何排查內存泄漏導致的頁面卡頓現象

          前端達人

          不知道在座的各位有沒有被問到過這樣一個問題:如果頁面卡頓,你覺得可能是什么原因造成的?有什么辦法鎖定原因并解決嗎?

          這是一個非常寬泛而又有深度的問題,他涉及到很多的頁面性能優化問題,我依稀還記得當初面試被問到這個問題時我是這么回答的:

          1. 先會檢查是否是網絡請求太多,導致數據返回較慢,可以適當做一些緩存
          2. 也有可能是某塊資源的bundle太大,可以考慮拆分一下
          3. 然后排查一下js代碼,是不是某處有過多循環導致占用主線程時間過長
          4. 瀏覽器某幀渲染的東西太多,導致的卡頓
          5. 在頁面渲染過程中,可能有很多重復的重排重繪
          6. emmmmmm…不知道了

          后來了解到了,感官上的長時間運行頁面卡頓也有可能是因為內存泄漏引起的

          內存泄漏的定義

          那什么是內存泄漏呢?借助別的大佬給出的定義,內存泄漏就是指由于疏忽或者程序的某些錯誤造成未能釋放已經不再使用的內存的情況。簡單來講就是假設某個變量占用100M的內存,而你又用不到這個變量,但是這個變量沒有被手動的回收或自動回收,即仍然占用100M的內存空間,這就是一種內存的浪費,即內存泄漏

          JS的數據存儲

          JavaScript的內存空間分為棧內存堆內存,前者用來存放一些簡單變量,后者用來存放復雜對象

          • 簡單變量指的是JS的基本數據類型,例如:String、Number、Boolean、null、undefined、Symbol、BigInt
          • 復雜對象指的是JS的引用數據類型,例如:Object、Array、Function

          JS垃圾回收機制

          根據內存泄漏的定義,有些變量或數據不再被使用或不需要了,那么它就是垃圾變量或垃圾數據,如果其一直保存在內存中,最終可能會導致內存占用過多的情況。那么此時就需要對這些垃圾數據進行回收,這里引入了垃圾回收機制的概念

          垃圾回收的機制分為手動自動兩種

          例如C/C++采用的就是手動回收的機制,即先用代碼為某個變量分配一定的內存,然后在不需要了后,再用代碼手動釋放掉內存

          JavaScript采用的則是自動回收的機制,即我們不需要關心何時為變量分配多大的內存,也不需要關心何時去釋放內存,因為這一切都是自動的。但這不表示我們不需要關心內存的管理?。。?!否則也不會有本文討論的內存泄露了

          接下來就講一下JavaScript的垃圾回收機制

          通常全局狀態(window)下的變量是不會被自動回收的,所以我們來討論一下局部作用域下的內存回收情況

          function fn1 () { let a = { name: '零一' } let b = 3 function fn2() { let c = [1, 2, 3] } fn2() return a } let res = fn1() 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          以上代碼的調用棧如下圖所示:

          img

          圖中左側為??臻g,用于存放一些執行上下文和基本類型數據;右側為堆空間,用于存放一些復雜對象數據

          當代碼執行到fn2()時,??臻g內的執行上下文從上往下依次是 fn2函數執行上下文 => fn1函數執行上下文 => 全局執行上下文

          fn2函數內部執行完畢以后,就該退出fn2函數執行上下文了,即箭頭向下移動,此時fn2函數執行上下文會被清除并釋放棧內存空間,如圖所示:

          img

          fn1函數內部執行完畢以后,就該退出fn1函數執行上下文了,即箭頭再向下移動,此時fn1函數執行上下文會被清除并釋放相應的棧內存空間,如圖所示:

          img

          此時處于全局的執行上下文中。JavaScript的垃圾回收器會每隔一段時間遍歷調用棧,假設此時觸發了垃圾回收機制,當遍歷調用棧時發現變量b和變量c沒有被任何變量所引用,所以認定它們是垃圾數據并給它們打上標記。因為fn1函數執行完后將變量a返回了出去,并存儲在全局變量res中,所以認定其為活動數據并打上相應標記。待空閑時刻就會將標記上垃圾數據的變量給全部清除掉,釋放相應的內存,如圖所示:

          img

          從這我們得出幾點結論:

          1. JavaScript的垃圾回收機制是自動執行的,并且會通過標記來識別并清除垃圾數據
          2. 在離開局部作用域后,若該作用域內的變量沒有被外部作用域所引用,則在后續會被清除

          補充: JavaScript的垃圾回收機制有著很多的步驟,上述只講到了標記-清除,其實還有其它的過程,這里簡單介紹一下就不展開討論了。例如:標記-整理,在清空部分垃圾數據后釋放了一定的內存空間后會可能會留下大面積的不連續內存片段,導致后續可能無法為某些對象分配連續內存,此時需要整理一下內存空間;交替執行,因為JavaScript是運行在主線程上的,所以執行垃圾回收機制時會暫停js的運行,若垃圾回收執行時間過長,則會給用戶帶來明顯的卡頓現象,所以垃圾回收機制會被分成一個個的小任務,穿插在js任務之中,即交替執行,盡可能得保證不會帶來明顯的卡頓感

          Chrome devTools查看內存情況

          在了解一些常見的內存泄漏的場景之前,先簡單介紹一下如何使用Chrome的開發者工具來查看js內存情況

          首先打開Chrome的無痕模式,這樣做的目的是為了屏蔽掉Chrome插件對我們之后測試內存占用情況的影響

          img

          然后打開開發者工具,找到Performance這一欄,可以看到其內部帶著一些功能按鈕,例如:開始錄制按鈕;刷新頁面按鈕;清空記錄按鈕;記錄并可視化js內存、節點、事件監聽器按鈕;觸發垃圾回收機制按鈕等等

          img

          簡單錄制一下百度頁面,看看我們能獲得什么,如下動圖所示:

          img

          從上圖中我們可以看到,在頁面從零到加載完成這個過程中JS Heap(js堆內存)、documents(文檔)、Nodes(DOM節點)、Listeners(監聽器)、GPU memory(GPU內存)的最低值、最高值以及隨時間的走勢曲線,這也是我們主要關注的點

          再來看看開發者工具中的Memory一欄,其主要是用于記錄頁面堆內存的具體情況以及js堆內存隨加載時間線動態的分配情況

          img

          堆快照就像照相機一樣,能記錄你當前頁面的堆內存情況,每快照一次就會產生一條快照記錄,如圖所示:

          img

          如上圖所示,剛開始執行了一次快照,記錄了當時堆內存空間占用為13.9MB,然后我們點擊了頁面中某些按鈕,又執行一次快照,記錄了當時堆內存空間占用為13.4MB。并且點擊對應的快照記錄,能看到當時所有內存中的變量情況(結構、占總占用內存的百分比…)

          然后我們還可以看一下頁面動態的內存變化情況,如圖所示:

          img

          在開始記錄后,我們可以看到圖中右上角有起伏的藍色與灰色的柱形圖,其中藍色表示當前時間線下占用著的內存;灰色表示之前占用的內存空間已被清除釋放。

          從上圖過程來看,我們可以看到剛開始處于的tab所對應顯示的頁面中占用了一定的堆內存空間,成藍色柱形,在點擊別的tab后,原tab對應的內容消失,并且原來藍色的柱形變成灰色(表示原占用的內存空間得到了釋放),同時新tab所對應顯示的頁面也占用了一定的堆內存空間。因此后續我們就可以針對這個圖來查看內存的占用與清除情況

          內存泄漏的場景

          那么到底有哪些情況會出現內存泄漏的情況呢?這里列舉了常見的幾種:

          1. 閉包使用不當引起內存泄漏
          2. 全局變量
          3. 分離的DOM節點
          4. 控制臺的打印
          5. 遺忘的定時器

          接下來介紹一下各種情況,并嘗試用剛才講到的兩種方法來捕捉問題所在

          1.閉包使用不當

          文章開頭的例子中,在退出fn1函數執行上下文后,該上下文中的變量a本應被當作垃圾數據給回收掉,但因fn1函數最終將變量a返回并賦值給全局變量res,其產生了對變量a的引用,所以變量a被標記為活動變量并一直占用著相應的內存,假設變量res后續用不到,這就算是一種閉包使用不當的例子

          接下來嘗試使用PerformanceMemory來查看一下閉包導致的內存泄漏問題,為了使內存泄漏的結果更加明顯,我們稍微改動一下文章開頭的例子,代碼如下:

          <button onclick="myClick()">執行fn1函數</button> <script> function fn1 () { let a = new Array(10000) // 這里設置了一個很大的數組對象 let b = 3 function fn2() { let c = [1, 2, 3] } fn2() return a } let res = [] function myClick() { res.push(fn1()) } </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22

          設置了一個按鈕,每次執行就會將fn1函數的返回值添加到全局數組變量res中,是為了能在performacne的曲線圖中看出效果,如圖所示:

          img

          在每次錄制開始時手動觸發一次垃圾回收機制,這是為了確認一個初始的堆內存基準線,便于后面的對比,然后我們點擊了幾次按鈕,即往全局數組變量res中添加了幾個比較大的數組對象,最后再觸發一次垃圾回收,發現錄制結果的JS Heap曲線剛開始成階梯式上升的,最后的曲線的高度比基準線要高,說明可能是存在內存泄漏的問題

          在得知有內存泄漏的情況存在時,我們可以改用Memory來更明確得確認問題和定位問題

          首先可以用Allocation instrumentation on timeline來確認問題,如下圖所示:

          img

          在我們每次點擊按鈕后,動態內存分配情況圖上都會出現一個藍色的柱形,并且在我們觸發垃圾回收后,藍色柱形都沒變成灰色柱形,即之前分配的內存并未被清除

          所以此時我們就可以更明確得確認內存泄漏的問題是存在的了,接下來就精準定位問題,可以利用Heap snapshot來定位問題,如圖所示:

          img

          第一次先點擊快照記錄初始的內存情況,然后我們多次點擊按鈕后再次點擊快照,記錄此時的內存情況,發現從原來的1.1M內存空間變成了1.4M內存空間,然后我們選中第二條快照記錄,可以看到右上角有個All objects的字段,其表示展示的是當前選中的快照記錄所有對象的分配情況,而我們想要知道的是第二條快照與第一條快照的區別在哪,所以選擇Object allocated between Snapshot1 and Snapshot2,即展示第一條快照和第二條快照存在差異的內存對象分配情況,此時可以看到Array的百分比很高,初步可以判斷是該變量存在問題,點擊查看詳情后就能查看到該變量對應的具體數據了

          以上就是一個判斷閉包帶來內存泄漏問題并簡單定位的方法了

          2.全局變量

          全局的變量一般是不會被垃圾回收掉的,在文章開頭也提到過了。當然這并不是說變量都不能存在全局,只是有時候會因為疏忽而導致某些變量流失到全局,例如未聲明變量,卻直接對某變量進行賦值,就會導致該變量在全局創建,如下所示:

          function fn1() { // 此處變量name未被聲明 name = new Array(99999999) } fn1() 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          此時這種情況就會在全局自動創建一個變量name,并將一個很大的數組賦值給name,又因為是全局變量,所以該內存空間就一直不會被釋放

          解決辦法的話,自己平時要多加注意,不要在變量未聲明前賦值,或者也可以開啟嚴格模式,這樣就會在不知情犯錯時,收到報錯警告,例如:

          function fn1() { 'use strict'; name = new Array(99999999) } fn1() 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          3.分離的DOM節點

          什么叫DOM節點?假設你手動移除了某個dom節點,本應釋放該dom節點所占用的內存,但卻因為疏忽導致某處代碼仍對該被移除節點有引用,最終導致該節點所占內存無法被釋放,例如這種情況:

          
          

          改動很簡單,就是將對.child節點的引用移動到了click事件的回調函數中,那么當移除節點并退出回調函數的執行上文后就會自動清除對該節點的引用,那么自然就不會存在內存泄漏的情況了,我們來驗證一下,如下圖所示:

          img

          結果很明顯,這樣處理過后就不存在內存泄漏的情況了

          4.控制臺的打印

          控制臺的打印也會造成內存泄漏嗎????是的呀,如果瀏覽器不一直保存著我們打印對象的信息,我們為何能在每次打開控制的Console時看到具體的數據呢?先來看一段測試代碼:

          <button>按鈕</button> <script> document.querySelector('button').addEventListener('click', function() { let obj = new Array(1000000) console.log(obj); }) </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          我們在按鈕的點擊回調事件中創建了一個很大的數組對象并打印,用performance來驗證一下:

          img

          開始錄制,先觸發一次垃圾回收清除初始的內存,然后點擊三次按鈕,即執行了三次點擊事件,最后再觸發一次垃圾回收。查看錄制結果發現JS Heap曲線成階梯上升,并且最終保持的高度比初始基準線高很多,這說明每次執行點擊事件創建的很大的數組對象obj都因為console.log被瀏覽器保存了下來并且無法被回收

          接下來注釋掉console.log,再來看一下結果:

          <button>按鈕</button> <script> document.querySelector('button').addEventListener('click', function() { let obj = new Array(1000000) // console.log(obj); }) </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8

          performance如圖所示:

          img

          可以看到沒有打印以后,每次創建的obj都立馬被銷毀了,并且最終觸發垃圾回收機制后跟初始的基準線同樣高,說明已經不存在內存泄漏的現象了

          其實同理,console.log也可以用Memory來進一步驗證

          • 未注釋console.log

          img

          • 注釋掉了console.log

          img

          最后簡單總結一下:在開發環境下,可以使用控制臺打印便于調試,但是在生產環境下,盡可能得不要在控制臺打印數據。所以我們經常會在代碼中看到類似如下的操作:

          // 如果在開發環境下,打印變量obj if(isDev) { console.log(obj) } 
          
          • 1
          • 2
          • 3
          • 4

          這樣就避免了生產環境下無用的變量打印占用一定的內存空間,同樣的除了console.log之外,console.error、console.info、console.dir等等都不要在生產環境下使用

          5.遺忘的定時器

          其實定時器也是平時很多人會忽略的一個問題,比如定義了定時器后就再也不去考慮清除定時器了,這樣其實也會造成一定的內存泄漏。來看一個代碼示例:

          <button>開啟定時器</button> <script> function fn1() { let largeObj = new Array(100000) setInterval(() => { let myObj = largeObj }, 1000) } document.querySelector('button').addEventListener('click', function() { fn1() }) </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15

          這段代碼是在點擊按鈕后執行fn1函數,fn1函數內創建了一個很大的數組對象largeObj,同時創建了一個setInterval定時器,定時器的回調函數只是簡單的引用了一下變量largeObj,我們來看看其整體的內存分配情況吧:

          img

          按道理來說點擊按鈕執行fn1函數后會退出該函數的執行上下文,緊跟著函數體內的局部變量應該被清除,但圖中performance的錄制結果顯示似乎是存在內存泄漏問題的,即最終曲線高度比基準線高度要高,那么再用Memory來確認一次:

          img

          在我們點擊按鈕后,從動態內存分配的圖上看到出現一個藍色柱形,說明瀏覽器為變量largeObj分配了一段內存,但是之后這段內存并沒有被釋放掉,說明的確存在內存泄漏的問題,原因其實就是因為setInterval的回調函數內對變量largeObj有一個引用關系,而定時器一直未被清除,所以變量largeObj的內存也自然不會被釋放

          那么我們如何來解決這個問題呢,假設我們只需要讓定時器執行三次就可以了,那么我們可以改動一下代碼:

          <button>開啟定時器</button> <script> function fn1() { let largeObj = new Array(100000) let index = 0 let timer = setInterval(() => { if(index === 3) clearInterval(timer); let myObj = largeObj
                      index ++ }, 1000) } document.querySelector('button').addEventListener('click', function() { fn1() }) </script> 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          現在我們再通過performancememory來看看還不會存在內存泄漏的問題

          • performance

          img

          這次的錄制結果就能看出,最后的曲線高度和初始基準線的高度一樣,說明并沒有內存泄漏的情況

          • memory

          img

          這里做一個解釋,圖中剛開始出現的藍色柱形是因為我在錄制后刷新了頁面,可以忽略;然后我們點擊了按鈕,看到又出現了一個藍色柱形,此時就是為fn1函數中的變量largeObj分配了內存,3s后該內存又被釋放了,即變成了灰色柱形。所以我們可以得出結論,這段代碼不存在內存泄漏的問題

          簡單總結一下: 大家在平時用到了定時器,如果在用不到定時器后一定要清除掉,否則就會出現本例中的情況。除了setTimeoutsetInterval,其實瀏覽器還提供了一個API也可能就存在這樣的問題,那就是requestAnimationFrame

          總結

          在項目過程中,如果遇到了某些性能問題可能跟內存泄漏有關時,就可以參照本文列舉的5種情況去排查,一定能找到問題所在并給到解決辦法的。

          雖然JavaScript的垃圾回收是自動的,但我們有時也是需要考慮要不要手動清除某些變量的內存占用的,例如你明確某個變量在一定條件下再也不需要,但是還會被外部變量引用導致內存無法得到釋放時,你可以用null對該變量重新賦值就可以在后續垃圾回收階段釋放該變量的內存了。

          轉自:csdn博客 作者:「零一」

          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服

          JS入門數組處理實用方法總結

          前端達人

          join()方法:將數組中所有元素通過指定分隔符連接成一個字符串

          舉例:

          myArr.join('-') // 用'-'符號拼接  
          
          • 1

          concat()方法:將兩個數組或多個數組合并成一個數組

          舉例:

          myArr.concat(arr1, arr2, ..., arrN)  
          
          • 1

          注意:該方法不會改變現有的數組,所以可以和空數組合并實現復制舊數組,在操作新數組數據時不污染舊數組數據

          sort() 方法:用于對數組的元素進行排序

          如果調用該方法時沒有使用參數,將按字母順序對數組中的元素進行排序,說得更精確點,是按照字符編碼的順序進行排序。要實現這一點,首先應把數組的元素都轉換成字符串(如有必要),以便進行比較

          舉例:

          myArr.sort() // 按字母排序
          myArr.sort(function(a, b) {
              return a - b
          }) // 按數字升序,降序為b - a
          // 箭頭函數寫法
          myArr.sort((a, b) => a - b)  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          reverse() 方法:用于顛倒數組中元素的順序

          舉例:

          myArr.reverse()  
          
          • 1

          push() / unshift()方法:向數組的末尾 / 開頭添加一個或多個元素,并返回新的長度

          舉例:

          myArr.push(item1, item2, ..., itemN)
          myArr.unshift(item1, item2, ..., itemN)  
          
          • 1
          • 2

          shift()方法:刪除數組的第一個元素,并返回第一個元素的值

          舉例:

          myArr.shift()  
          
          • 1

          pop()方法:刪除數組的一個元素(默認最后一個元素),并且返回該元素的值

          舉例:

          myArr.pop() // 刪除數組最后一個元素
          myArr.pop(1) // 刪除數組中索引為1的元素  
          
          • 1
          • 2

          splice()方法:向/從數組中添加/刪除項目,然后返回被刪除的項目

          myArr.splice(index, count, item1, item2, ..., itemN)
          // index 必需。整數,規定添加/刪除項目的位置,使用負數可從數組結尾處規定位置
          // count 必需。要刪除的項目數量。如果設置為 0,則不會刪除項目
          // item1, item2, ..., itemN 可選。向數組添加的新項目  
          
          • 1
          • 2
          • 3
          • 4

          forEach()方法:方法用于調用數組的每個元素,并將元素傳遞給回調函數(相當于for循環)

          舉例:

          myArr.forEach(function (item, index, arr) {
              if (index === 3) {
                  item = 123
              } 
          }) // 循環數組,將索引為3的元素值更改為123
          // 箭頭函數寫法
          myArr.forEach((v, i, arr) => if (i === 3) { v = 123 })  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

          注意:以下方法均不會對空數組進行檢測,不會改變原始數組

          indexOf()方法:查找數組是否存在某個元素,返回下標,沒有則返回-1

          舉例:

          myArr.indexOf(item)  
          
          • 1

          注意:indexOf() 方法對大小寫敏感!

          slice()方法:可提取字符串的某個部分,并以新的字符串返回被提取的部分(淺拷貝數組的元素)

          舉例:

          const newArr = myArr.slice(0, 1)
          // 截取數組myArr索引從0到1的部分元素
          // 參數:
          // begin(可選): 索引數值,接受負值,從該索引處開始提取原數組中的元素,默認值為0。
          // end(可選):索引數值(不包括),接受負值,在該索引處前結束提取原數組元素,默認值為數組末尾(包括最后一個元素)  
          
          • 1
          • 2
          • 3
          • 4
          • 5

          every()方法:用于檢測數組中的元素是否滿足指定條件(函數提供)(如某個值是否都為true)

          如果有一個元素不滿足,則整個表達式返回 false,且停止檢測;如果所有元素都滿足條件,則返回 true

          舉例:

          const state = myArr.every(function (item, index, arr) {
              return item > 10
          }) // 檢測數組myArr的所有元素是否都大于10,返回一個布爾值state
          // 箭頭函數寫法
          const state = myArr.every((v, i, arr) => v > 10)  
          
          • 1
          • 2
          • 3
          • 4
          • 5

          some()方法:用于檢測數組中的元素是否滿足指定條件(函數提供)(如某個值是否都為true)

          如果有一個元素滿足,則整個表達式返回 true ,且停止檢測;如果沒有滿足條件的元素,則返回false

          舉例:

          const state = myArr.some(function (item, index, arr) {
              return item > 10
          }) // 檢測數組myArr中是否存在元素大于10,返回一個布爾值state
          // 箭頭函數寫法
          const state = myArr.some((v, i, arr) => v > 10)  
          
          • 1
          • 2
          • 3
          • 4
          • 5

          includes()方法:用于判斷數組是否包含指定的值,如果找到匹配的值則返回 true,否則返回 false

          注意:includes() 方法區分大小寫
          參數:
          searchvalue:必需,要查找的值
          start:可選,設置從那個位置開始查找,默認為 0

          舉例:

          const state = myArr.includes(3) // 檢測數組myArr中是否存在元素3,返回一個布爾值state
          const state = myArr.includes(3, 3) // 從索引3開始檢測數組myArr中是否存在元素3,返回一個布爾值state  
          
          • 1
          • 2

          filter()方法:創建一個新的數組,新數組中的元素是通過檢查指定數組中符合條件的所有元素

          舉例:

          const newArr = myArr.filter(function (item, index, arr) {
              return item > 10
          }) // 檢測數組myArr中所有元素都大于10的元素,返回一個新數組newArr
          // 箭頭函數寫法
          const newArr = myArr.filter((v, i, arr) => v > 10)  
          
          • 1
          • 2
          • 3
          • 4
          • 5

          map()方法:返回一個新數組,數組中的元素為原始數組元素調用函數處理后的值

          map()方法按照原始數組元素順序依次處理元素

          舉例:

          const newArr = myArr.map(function (item, index, arr) {
              return item * 10
          }) // 數組myArr中所有元素都乘于10,返回一個新數組newArr
          // 箭頭函數寫法
          const newArr = myArr.map((v, i, arr) => v * 10)  
          
          • 1
          • 2
          • 3
          • 4
          • 5

          舉例(用于數組嵌套對象的類型):

          const newArr = myArr.map(function (item, index, arr) {
              return {
                  id: item.id,
                  newItem: '123'
              }
          }) // 處理數組myArr中指定的對象元素里面的元素或新元素,返回一個新數組newArr
          // 箭頭函數寫法
          const newArr = myArr.map((v, i, arr) => {
              return {
                  id: v.id,
                  newItem: '123'
              }
          })  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13

          find() / findIndex()方法:返回通過測試(函數內判斷)的數組的第一個元素的 值 / 索引。如果沒有符合條件的元素返回 undefined / -1

          舉例:

          const val = myArr.find(function (item, index, arr) {
              return item > 10
          }) // 返回數組myArr中第一個大于10的元素的值val,沒有則返回undefined
          
          const val = myArr.findIndex(function (item, index, arr) {
              return item > 10
          }) // 返回數組myArr中第一個大于10的元素索引,沒有則返回-1  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7

          reduce()方法:返回一個新數組,數組中的元素為原始數組元素調用函數處理后的值

          這個方法接收兩個參數:要執行的函數,傳遞給函數的初始值
          要執行的函數(total, currentValue, currentValue, arr):
          total:必選,初始值, 或者計算結束后的返回值
          currentValue:必選,當前元素;
          currentValue:可選,當前元素索引;
          arr:可選,當前元素所屬的數組對象

          舉例1:

          const myArr = [1, 2, 3]
          const sum = myArr.reduce(function(pre, cur, index, arr) {
              console.log(pre, cur)
              return pre + cur
          })
          console.log(sum)
          // 輸出值分別為
          // 1, 2
          // 3, 3
          // 6  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          舉例2(設置初始迭代值):

          const myArr = [1, 2, 3]
          const sum = myArr.reduce(function(pre, cur, index, arr) {
              console.log(pre, cur)
              return prev + cur
          }, 2)
          console.log(sum)
          // 輸出值分別為
          // 2, 1
          // 3, 2
          // 5, 3
          // 8  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11

          應用:

          1.求和、求乘積
          const myArr = [1, 2, 3, 4]
          const result1 = myArr.reduce(function(pre, cur) {
            return pre + cur
          })
          const result2 = myArr.reduce(function(pre, cur) {
            return pre * cur
          })
          console.log(result1) // 6
          console.log(result2) // 24
          
          2.計算數組中每個元素出現的次數
          const myArr = ['liang','qi','qi','liang','ge','liang'] 
          const arrResult = myArr.reduce((pre,cur) =>{
              if(cur in pre){
                  pre[cur]++
              }else{
                  pre[cur] = 1
              }
              return pre
          },{})
          console.log(arrResult) // 結果:{liang: 3, qi: 2, ge: 1}
          
          3.對對象的屬性求和
          const myArr = [
              {
                  name: 'liangqi',
                  weigth: 55
              },{
                  name: 'mingming',
                  weigth: 66
              },{
                  name: 'lele',
                  weigth: 77
              }
          ]
          const result = myArr.reduce((a,b) =>{
              a = a + b.weigth
              return a
          },0)
          console.log(result) // 結果:198  
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41

          Array.of()方法:用于將一組值轉化為新數組

          舉例:

          Array.of() // []
          Array.of(undefined) // [undefined]
          Array.of(1) // [1]
          Array.of(1, 2) // [1, 2]  
          
          • 1
          • 2
          • 3
          • 4

          flat()方法:數組拍平方法也叫數組扁平化、數組拉平、數組降維,用于將嵌套的數組變成一維數組,返回一個新數組


          轉自:csdn論壇, 作者:Assam180


          藍藍設計www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服


          日歷

          鏈接

          個人資料

          藍藍設計的小編 http://www.syprn.cn

          存檔

          亚洲va欧美va天堂v国产综合