JS -- 题

112 阅读3分钟
  1. call、apply、bind
    • fn.call(obj, 10, 20, 30)
    • fn.apply(obj, [10, 20, 30])
    • fn.bind(obj)
    • 三个函数都可以改变this的指向
    • call和apply会立即执行,但是bind返回的只是改变了this后的函数,需要调用才能执行
    • call和apply传参方式不同,call的性能比apply相对好一些
    console.time('A')  //可以测试出一段程序执行的时间
    ...
    console.timeEnd('A')
    
    console.profile() //在火狐中安装FireBug,可以精准的获取到程序每一个步骤所消耗的时间
    
  2. 箭头函数与普通函数的区别
    • 箭头函数语法比普通函数简单
    • 箭头函数没有自己的this,箭头函数的this从属于所属上下文(使用call、apply等任何方式都无法改变箭头函数的this)
    • 箭头函数没有arguments
    • 箭头函数不能被new执行(箭头函数没有this,也没有prototype)
  3. 实现string的replace方法
     (function () {
      /**
       * @strOrReg  查找的字符串或需要匹配的正则
       * @strOrFun  替换的字符串或返回字符串的函数
       */
      function myReplace(strOrReg, strOrFun) {
        //this为需要操作的字符串
        let newStr = this
        let reg = strOrReg
        if (typeof strOrReg === 'string') {
          reg = new RegExp(strOrReg)
        }
        let rs = this.match(reg)
        if (rs) { //有匹配结果
          if (typeof b === 'string') { //第二个参数为字符串
            if (rs.length === 1) {
              newStr = newStr.slice(0, rs['index']) + strOrFun + newStr.slice(rs['index'] + strOrReg.length)
            } else {
              for (let i = 0; i < rs.length; i++) {
                let index = newStr.indexOf(rs[i])
                newStr = newStr.slice(0, index) + strOrFun + newStr.slice(index + rs[i].length)
              }
            }
          } else { //第二个参数为function
            if (rs.length === 1) {
              newStr = newStr.slice(0, rs['index']) + strOrFun(rs[0], rs[0].length, this) + newStr.slice(rs['index'] +
                strOrReg.length)
            } else {
              for (let i = 0; i < rs.length; i++) {
                let index = newStr.indexOf(rs[i])
                newStr = newStr.slice(0, index) + strOrFun(rs[0], index, this) + newStr.slice(index + rs[i].length)
              }
            }
          }
        }
        return newStr
      }
      String.prototype.myReplace = myReplace
    })()
    
  4. 打印结果
    var a = {}, b = '123', c = 123
    a[b] = 'b'
    a[c] = 'c'
    console.log(a[b]) //c   a['123'] => a[123]
    
    var a = {}, b = Symbol('123'), c = Symbol('123')
    a[b] = 'b'
    a[c] = 'c'
    console.log(a[b]) //b   Symbol创建的数据都是唯一的
    
    var a = {},
      b = {
        key: '123'
      },
      c = {
        key: '456'
      }
    a[b] = 'b'   // => a["[object object]"] = 'b'
    a[c] = 'c'   // => a["[object object]"] = 'c'
    console.log(a[b]) //c  
    //对象的属性名不能是一个对象(遇到对象属性名,会默认转为字符串"[object, object]")
    //obj = {}, arr = [12, 23] obj[arr] = 'test'  => obj['12, 23'] = 'test'
    
    //1. 本应匿名的函数如果设置了函数名,在外面还是无法调用,但是再函数里面是可以使用的。 
    //2. 而且类似于创建常量一样,这个名字存储的值不能再被修改(非严格模式下不会报错,但是不会有任何效果,严格模式下直接报错)
    var b = 10;
    (function b() {
      b = 20
      console.log('1', b)  //function
    })()
    console.log('2', b)  //10
    
    let obj = {
      2: 3,
      3: 4,
      length: 2,
      push: Array.prototype.push   //原理是把obj.length = val, obj.length自动加1
    }
    obj.push(1)  //obj[obj.length] = 1 => obj[2] = 1  => obj.length =3
    obj.push(2)  //obj[obj.length] = 2 => obj[3] = 2  => obj.length =4
    console.log(obj) //{2: 1, 3: 2, length: 4, push: ƒ}
    
  5. $attr('xxx', 'yyy'),获得所有属性xxx值为yyy的标签
    function $attr(property, value) {
      let elements = document.getElementsByTagName('*'),
        arr = []
      elements = Array.from(elements) //把非数组转为数组
      elements.forEach(item => {
        let itemValue = item.getAttribute(property)
        if (property === 'class') { //class要特殊处理,因为class可能有多个
          new RegExp("\\b" + value + "\\b").test(itemValue) ? arr.push('item') : null
          return
        }
        if (itemValue === value) {
          arr.push(item)
        }
      })
      return arr
    }
    
    let arr = $attr('class', 'box')
    console.log(arr)
    
  6. 将数组扁平化
    //1. 用es6的Array.prototype.flat
    arr = arr.flat(Infinity)
    
    //2. toString()
    arr = arr.toString().split(',').map(item => +item)
    
    //3. new Set() +  Array.from
    arr = Array.from(new set(arr)).sort((a, b) => a-b)
    arr = [...new set(arr)].sort((a, b) => a-b)
    
    //4.JSON.stringify
    arr = JSON.stringify(arr).replace(/(\[|\])/g, '').split('').map(item => +item)
    
    //5. some() 遍历每一项,某一项返回true,则整个返回true,找不到返回false(find()返回找到的结果,只找一项就返回,找不到就返回undefined)
    while(arr.some(item => Array.isArray(item))){
        arr = [].concat(...arr)
    }
    
    //6. 递归
    (function () {
      function myFlat() {
        //this为调用方法的数组
        let res = [],
          _this = this
        let fn = arr => {
          for (let i = 0; i < arr.length; i++) {
            let item = arr[i]
            if (Array.isArray(item)) {
              fn(item)
              continue
            }
            res.push(item)
          }
        }
        fn(_this)
        return res
      }
      Array.prototype.myFlat = myFlat
    })()
    let arr = [1, [3, [4, [5, 6]]]]
    console.log(arr.myFlat())
    
  7. == 如果类型不一样,先转为相同的数据类型再比较
    • {} == {} false
    • null == undefined true
    • null === undefined false
    • NaN == NaN false NaN和谁都不相等
    • [12] == "12" true
    /* 重新定义对象的toString
      var a = {
      n: 0,
      toString: function () {
        return ++this.n
      }
    } */
    
    /* 重新定义数组的toString
    let a = [1, 2, 3]
    a.toString = a.shift
    */
    
    
    /* 
    Object.defineProperty(window, 'a', {
      get: function () {
        this.value ? this.value++ : this.value = 1
        return this.value
      }
    }) */
    if (a == 1 && a == 2 && a == 3) {
      console.log(1)   //条件成立打印1
    }
    
    //Array.from()   将类数组转为数组
    //Array.isArray()
    //Object.create([obj]) 创建一个新对象,并且对象的__proto__指向obj