30天前端打卡整理记录

 第一天:

写一个 execTime 函数,要求如下

参数:时间毫秒数
作用:什么都不做,但函数执行消耗的时间为参数传递的毫秒数

function execTime(t) {
  // 补全代码
}
console.log(1) // print 1
execTime(3000) // execute cost 3s
console.log(2) // print 2

  我的解答:

// while循环中判断条件,条件表达式,在每次循环前被求值。如果求值为真,statement就会被执行。如果求值为假,则跳出while循环执行后面的语句。
function execTime(t) {
  var start = (new Date()).getTime()
  while ((new Date()).getTime() - start < t) {
    continue
  }
}

第二天:

写一个 execTime 函数,要求

  • 参数 t:时间毫秒数
  • 参数 callback:回调函数
function execTime(t, callback) {
  // some code
}
console.log(1)
execTime(3000, function(){
  console.log(3)
})
console.log(2)

执行结果为:立即输出1和2,3秒后输出3

  我的解答:

// 1.任务进行到执行栈,
// 2.判断是异步或者同步任务
// 3.同步任务放置到主线程
// 4.异步任务放置到Event Table并注册函数。
// 5.当指定的事情完成时,Event Table会将这个函数移入Event Queue
// 6.当主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,
//   进入主线程执行。
// :上述过程不断重复,也就是常说的Event Loop(事件循环)。

// <1> 将console.log(1)与函数execTime 以及console.log(2)放置到执行栈,
// <2> 判断到execTime的执行为一个异步任务
// <3> 主线程以及 时间表分别放置
// <4> 执行主线程
// <5> 执行 Event Queue中的函数
// <6> 函数设置3秒后执行,所以,时隔三秒执行完成
// : (有一部分误差为setTimeout本事存在,有时是7有时是1,2,3)
function execTime(delay, callback) {
  setTimeout(callback, delay)
}

let time = +new Date()
console.log(1)
execTime(3000, function(){
  let nowTime = +new Date()
  console.log(3,`执行时间为:${nowTime - time}`) // 3,执行时间为:3007
})
console.log(2)

  我的拓展:

// 验证setTimeout跟随主线程执行时间判断
// 1.console.log(1)
// 2.task()进入Event Table并注册,计时开始。
// 3.执行while循环,很慢,非常慢,计时仍在继续。
// 4.3秒时间到了,计时事件timeout完成,task()进入Event Queue,
//   但是while循环还在花时间执行(...)
// 5.终于,while循环执行完了,task()终于从Event Queue进入了主线程执行。
function execTime2(delay, callback) {
  setTimeout(callback, delay)
}
function task(){
  let nowTime = +new Date()
  console.log(3,`执行时间为:${nowTime - time2}`) // 3,执行时间为:5005
}
let time2 = +new Date()
console.log(1)
execTime2(3000, function(){
  task()
})
let start = (new Date()).getTime()
while ((new Date()).getTime() - start < 5000) {}
console.log(2)

// 特别注意,代码建议在chrome浏览器控制台执行,jsbin执行失效,原理不明


第三天:

创建一个对象 obj,使得 obj.obj.obj.obj.obj === obj,即,不管出现多少次 .obj,都得到 obj

  我的解答:

var obj = {
  obj: this
}

第四天:

写出一个函数 fn,使得 fn 满足以下条件:

  • fn() === fn
  • fn.fn === fn

  我的解答:

function fn() {
  return fn
}
// 因为函数也是对象类型,所以函数也是可以赋属性的
fn.fn = fn

第五天:

写一个函数 fn,使得 fn 满足一下条件

  • fn() 打印出 'a'
  • fn()() 打印出 'b'
  • fn()()() 打印出 'c'

  我的解答:

// 按照题目要求,函数fn会return一个函数,
// 函数fn return的函数同样return一个函数

function fn(){
  console.log('a')
  return function(){
    console.log('b')
    return function(){
      console.log('c')
    }
  }
}
fn()
fn()()
fn()()()

// 这种方案看起来完成了题目要求,但是却每次都会把之前的a,b打印
// 所以有了下面的方法
function fn1(){
  let a = setTimeout(()=> console.log('a'),0)
  return function(){
  clearTimeout(a)
  let b = setTimeout(()=> console.log('b'),0)
    return function(){
      clearTimeout(b)
      setTimeout(()=> console.log('c'),0)
    }
  }
}

fn1()
fn1()()
fn1()()()

第六天:

写一个函数 fn,使得 fn 满足以下条件

fn() == 'a'
fn()() == 'b'
fn()()() == 'c'

  我的解答:

// 1.运算符中非字符串的比较
// 2.如果运算子是对象,会转为原始类型的值,再进行比较。
// :对象转换成原始类型的值,算法是先调用valueOf方法;
//  如果返回的还是对象,再接着调用toString方法,
//  详细解释参见《数据类型的转换》一章。
// 参考资料:阮一峰 JavaScript 标准参考教程(alpha)
function fn(){
  return a
}

let a = function(){
  return b
}
let b = function(){
  return 'c'
}
a.valueOf = function(){
  return 'a'
}
b.valueOf = function(){
  return 'b'
}
fn.valueOf = function(){
  return 'fn'
}

console.log(fn == 'fn') //true
console.log(fn() == 'a') //true
console.log(fn()() == 'b') // true
console.log(fn()()() == 'c') // true

参考链接(阮一峰的JavaScript标准参考教程(alpha)中的比较运算符)

第七天:

小明成绩不好,每次考试都靠瞎。小明的老师对他说:“小明,如果考不到60分你继续考,直到考到60分,我实现你的愿望让你和凯丽坐到一起”

function exam() {
  var score = Math.floor(Math.random() * 101)
  if (score >= 60) {
    console.log('及格,和凯丽坐到一起')
  } else {
    console.log('不及格,继续考试')
    setTimeout(exam, 1000)
  }
}
exam()

要求:对上述代码使用 Promise 改写,能用以下方式调用

exam().then(score => {
  console.log('及格,和凯丽坐到一起', score)
})

   我的解答:

// 既然要使用promise.所以exam需要返回一个Promise 对象,
// 方法1
// 可以依靠第一天的while循环,帮助,等到判断好成绩及格再做处理
function exam() {
  return new Promise(function (resolve, reject) {
    let score = Math.floor(Math.random() * 101)
    while (score < 60) {
      score = Math.floor(Math.random() * 101)
      console.log('不及格,继续考试')
    }
    resolve(score)
  })
}

exam().then(score => {
  console.log('及格,和凯丽坐到一起', score)
})

// 方法2
// 递归处理,直至score>= 60
function exam2() {
  return new Promise(function (resolve, reject) {
    function _exam() {
      let score = Math.floor(Math.random() * 101)
      if (score < 60) {
        setTimeout(((score) => {
          return () => {
            console.log(`不及格,继续考试,只有${score}分`)
            _exam()
          }
        })(score), 1000)
      } else {
        resolve(score)
      }
    }

    _exam()

  })
}

exam2().then(score => {
  console.log('及格,和凯丽坐到一起', score)
})

 

第八天:

写一个 byField 函数,实现数组按姓名、年纪任意字段排序

var users = [
  { name: 'John', age: 20, company: 'Baidu' },
  { name: 'Pete', age: 18, company: 'Alibaba' },
  { name: 'Ann', age: 19, company: 'Tencent' }
];

users.sort(byField('company'))
// 输出
/*
[
  { name: 'Pete', age: 18, company: 'Alibaba' },
  { name: 'John', age: 20, company: 'Baidu' },
  { name: 'Ann', age: 19, company: 'Tencent' }
];
*/
users.sort(byField('name'))
// 输出
/*
[
  { name: 'Ann', age: 19, company: 'Tencent' },
  { name: 'John', age: 20, company: 'Baidu' },
  { name: 'Pete', age: 18, company: 'Alibaba' },
];
*/


   我的解答:

// (1)sort排序数字使用方案:

// JavaScript的sort方法默认对数组的内容的排序按照字符串来进行排序
// 所以加入不传入自定义的比较函数,我们会得到一个错误的结果
// 按照sort函数规则,自定义函数需要接受两个参数,
// 如果这两个参数相等则返回0,如果第一个参数应该排序在前,则返回负数
// 如果第二个参数需要排序在前则返回正数
// 所以排序数字类型的数组时,我们应该这样写
let arr = [4,2,8,45,23,11,16]
arr.sort(function(a,b){
  return a - b
})
console.log(arr)
// (2)sort排序字符串以及数字使用方法:
// 对于上面的方法我们仅仅只能排序数字,但对于字符串无法进行排序
// 所以如果我们如果要让排序方法适应数字以及字符串我们对于比较函数需要进一步修改
let arr1 = [1,5,3,23,56,'a','b']
arr1.sort(function(a,b){
    // 判断 a 与 b 是否全等于
    if(typeof a === b){
      return 0
    }
    if(typeof a === typeof b){
      return a < b ? -1 : 1
    }
    return typeof a < typeof b ? -1 : 1
  })
console.log(arr1)

// (3)sort排序在复杂情况下使用
// :题目答案
// 对于上面的方法我们仅仅只能排序数字,以及字符串的组合数组
// 无法进行排序题目要求的对象集合的数组
// 对于题目要求,我们对对象进行处理,每次返回的值,应该是属性值的比较
let users = [
  { name: 'John', age: 20, company: 'Baidu' },
  { name: 'Pete', age: 18, company: 'Alibaba' },
  { name: 'Ann', age: 19, company: 'Tencent' }
]
let user = [
  444,
  { name: 'Pete', age: 18, company: 'Alibaba' },
  { name: 'Ann', age: 19, company: 'Tencent' }
]
function byField(name){
  return function(x,y){
    if(typeof x === 'object' && typeof y === 'object' && x && y){
      
      let a = x[name],b=y[name]
      // 判断 a 与 b 是否全等于
      if(typeof a === b){
        return 0
      }
      if(typeof a === typeof b){
        return a < b ? -1 : 1
      }
      return typeof a < typeof b ? -1 : 1
    }else{
      console.log('错误')
      throw{
        name:'Error',
      }
    }
  }
}
users.sort(byField('age'))
console.log(users)        
user.sort(byField('age'))
console.log(user)         // 数组有部分元素不是对象,所以出差错

第九天:

const obj = { a: 1, b: 2, c: 3 };
function select(obj, arr) {
  // your code
}
select(obj, ["a", 'c'])
// 输出 { a: 1, c: 3 }

   我的解答:

// 1 
function select(obj, arr){
    var newArr = {};
    for(var i in obj){
        if(arr.indexOf(i) > -1){
            newArr[i] = obj[i]
        }
    }
    return newArr
}

// 2
function select(obj,arr){
  return JSON.parse(JSON.stringify(obj, arr))
}

// 3
function select(obj,arr){
   return arr.reduce((o, v) => {
    o[v] = obj[v]
    return o
  }, {})
}

第十天:

某个应用模块由文本框input,以及按钮A,按钮B组成。点击按钮A,会向地址urlA发出一个ajax请求,并将返回的字符串填充到input中(覆盖掉input中原有的数据),点击按钮B,会向地址urlB发出一个ajax请求,并将返回的字符串填充到input中(覆盖input中原有数据)。
当用户依次点击按钮A、B时,预期的效果是input依次被urlA、urlB返回的数据填充,但是由于urlA的请求返回比较慢,导致urlB返回的数据被urlA的数据覆盖了,与用户预期的顺序不一致。

问:应该如何设计代码,解决这个问题

  我的解答:

$('.btn1').click(function(){
  next('按钮1')
  
})
$('.btn2').click(function(){
   next('按钮2')
  
})
$('.btn3').click(function(){
   next('按钮3')
  
})

let num = 0
let next = (function(){
  // 使用闭包是为了让代码都沿用同一个promise
  let promise = new Promise(function(resolve){resolve()})
  // or
  // let promise = Promise.resolve()
  return function(url){
    promise = promise.then(()=>{
      return new Promise((resolve)=>{
        setTimeout(()=>{
          num ++
          console.log(`第${num}次打印的是${url}`)
          resolve()
        },3000 * Math.random())
      })
    })
  }
})() 

答案链接(包括html)

 

第十一天:

写一个函数 execTimes,用于测试一个函数执行一定次数的时长

如:execTimes(sort, 1000)('hello') 表示执行sort函数1000次,sort的参数是'hello'

function execTimes() {
  // your code
}
function sort(str) {
  return str.split('').sort().join('')
}
// 执行sort1000次,sort的参数是'hello'
execTimes(sort, 1000)('hello')
// 输出:‘执行1000次,耗时43ms’

  我的解答:

function execTimes(fn,num) {
  let startTime = new Date().getTime()
  return function(str){
    for(let i = 0 ; i < num ; i ++){
      fn(str)
    }
    let nowTime = new Date().getTime()
    console.log(`执行${num}次,耗时${nowTime - startTime}ms`)
    return nowTime - startTime
  }
}
function sort(str) {
  return str.split('').sort().join('')
}
// 执行sort1000次,sort的参数是'hello'
execTimes(sort, 1000)('hello')
// 输出:‘执行1000次,耗时43ms’

第十二天:

请用 JavaScript 实现一个方法,该方法能够判断两个字符串是否 匹配,如:

function isMatch(str1, str2){
  // your code
}
isMatch('something', 'gnihtemos') // true
isMatch('aaa', 'aa') // false
isMatch('abb', 'baa') // false
isMatch('hello', 'olleh') // true

使用上一章节的 execTimes 方法测试你的方案执行10000次的时长,该方案是否是最优方案?如果不是,请给出最优方法,并说明时间复杂度

  我的解答:

// 在for循环中,i 会执行n次,时间复杂度为O(n)


function execTimes(fn,num) {
  let startTime = new Date().getTime()
  return function(str1,str2){
    for(let i = 0 ; i < num ; i ++){
      isMatch(str1,str2)
    }
    let nowTime = new Date().getTime()
    console.log(`执行${num}次,耗时${nowTime - startTime}ms`)
    return nowTime - startTime
  }
}
// 按题目意思先对字符排序再判断字符是否一致
function sort(str) {
  return str.split('').sort().join('')
}
function isMatch(str1, str2){
  let arr1 = str1.split('').sort().join('')
  let arr2 = str2.split('').sort().join('')
  if(arr1 === arr2){
    return true
  }else{
    return false
  }
}
execTimes(isMatch, 1000)('something','gnihtemos')

第十三天:

以下代码输出什么?为什么?

  我的解答:

var app = {
    fn1: function() {
      console.log(this)  
    },
    fn2: function(){
        return function() {
            console.log(this)
        }
    },
    fn3: function() {
        function fn() {
            console.log(this)
        }
        fn()
    },
    fn4: function() {
        return {
            fn: function() {
                console.log(this)
            }
        }
    },
    fn5() {
        setTimeout(function() {
            console.log(this)
        }, 10)
    },
    fn6() {
        setTimeout(function() {
            console.log(this)
        }, 20)
    },
    fn7() {
        setTimeout((function(){
            console.log(this)
        }).bind(this), 30)
    },
    fn8: () => {
        setTimeout(() => {
            console.log(this)
        }, 40)
    }
}
// app,最简单的this指向
app.fn1()
// 此时运行的是闭包return出来的匿名函数,this指向window
// fn2 会返回一个匿名函数,如果要执行匿名函数内部的代码,需要执行该匿名函数,所以要 app.fn2()()
// 我们用 xxx 指代匿名函数,1.xxx = app.fn2();  2.xxx() 即为 app.fn2()() 
// 当js解释器执行xxx内部的代码时,因为函数作用域,内部的this不可能再指向app,此时,this会指向全局对象window(node下为 global)
app.fn2()()
// 同理类始于app.fn2()(),this指向window
//  fn3 内定义了一个函数 fn,当执行fn()时,同样因为函数作用域,fn内部的this不指向app,而是全局对象window
app.fn3()
// 指向fn4 return出来的对象相当于 var obj = app.fn4(); obj.fn()
app.fn4().fn()
// 执行内部代码,都会设置一个定时器,定时器内的函数都会进入事件队列等待执行
// 由 MDN 解释:由 setTimeout() 调用的代码运行在与所在函数完全分离的执行环境上。会导致,这些代码中包含的 this 关键字会指向 window(全局、node 下为 global)对象
app.fn5()
// window(同上)
app.fn6()
// app(bind 后this指向bind的this)
app.fn7()
// window(理由与 fn5 一样)
app.fn8()

第十四天:

以下代码中出现的所有的 this 分别指代什么

  我的解答:

<!DOCTYPE html>
<html>
<head>
  <script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
  <meta charset="utf-8">
  <title>This</title>
</head>
<body>
<div class="father" style='100px;height:100px;background:#f00;'>
  <div class="child" style='100px;height:50px;background:#ff0;'></div>
</div>
<script>
  // .on() 方法会向事件监听的处理函数传递一个参数 event.target,处理函数内部的 this 指向  
  // event.target
  // 所以打印出的 this 便是 event.target 即 <div class="child"></div>
  $('.child').on('click', function(){
    // 此时绑定的是 .child , event.target 所以打印child dom元素
    // <div class="child" style='100px;height:50px;background:#ff0;'></div>
    console.log('.child',this)
  })
  $('.father').on('click', '.child', function(){
     // 此处用了事件代理,向处理函数传递的仍然是触发 点击 事件的真正元素,即 .child
      // 所以打印的 this 还是 event.target -> <div class="child"></div>
    console.log('.father',this)
  })
  $('.child')[0].onclick = function() {
    // 此时绑定的是 .child ,所以打印child dom元素
    console.log('.child[0]',this)
  }
  var app = {
    init: function() {
      this.$father = $('.father')
      this.$child = $('.child')
      this.bind()
    },
    bind: function() {
      var _this = this
      // 此时相当于,所以this指向father dom元素
      // this.$father.on('click', function(){
      //   console.log(this)
      // })
      this.$father.on('click', this.sayHi)
      this.$child.on('click', function() {
        // 此时因为执行的是_this.sayHello()
        // 相当于 _this.sayHello.call(_this)
        // 所以打印app
        _this.sayHello()
      })
      // 同理绑定的是this 即app
      this.$child.on('click', this.sayBye.bind(this))
    },
    sayHi: function() {
      console.log('sayHi',this)
    },
    sayHello: function() {
      console.log('sayHello',this)
    },
    sayBye: function() {
      console.log('sayBye',this)
    }
  }
  app.init()
</script>
</body>
</html>

第十五天:

封装一个 jsonp 方法,可以使用如下方式调用

jsonp(url[, data][, callbackName])
  • url
  • 类型:String,请求 url
  • data
  • 类型:PlainObject,参数对象
  • callbackName
  • 类型:String,传递给服务端的回调函数的 key
  • 默认是“callback”
  • 返回值
  • Promise 对象
// ?jsoncallback=fn&page=1&cate=recommend
jsonp('http://photo.sina.cn/aj/index',
  {
    page: 1, cate: 'recommend'
  }, 'jsoncallback')
  .then(data => {
    console.log(data)
  })

  我的解答:

  // 转换成链接参数形式
  let parseParam = function (param, key) {
    var paramStr = ""
    if (param instanceof String || param instanceof Number || param instanceof Boolean) {
      paramStr += "&" + key + "=" + encodeURIComponent(param)
    } else {
      $.each(param, function (i) {
        var k = key == null ? i : key + (param instanceof Array ? "[" + i + "]" : "." + i)
        paramStr += '&' + parseParam(this, k)
      })
    }
    return paramStr.substr(1)
  }

  function jsonp(url, data = {}, callback) {
    return new Promise(function (resolve, reject) {
      let reqUrl;
      data[callback] = callback
      reqUrl = url + '?' + parseParam(data)
      let script = document.createElement("script")
      script.src = reqUrl
      $('body').append(script)
      if (window[callback]) {
      } else {
        window[callback] = function (data) {
          resolve(data)
        }
      }
    })

  }

  // ?jsoncallback=fn&page=1&cate=recommend
  jsonp('http://photo.sina.cn/aj/index',
    {
      page: 1, cate: 'recommend'
    }, 'jsoncallback')
    .then(data => {
      console.log(data)
    }) 
  jsonp('http://photo.sina.cn/aj/index',
    {
      page: 2, cate: 'recommend'
    }, 'jsoncallback')
    .then(data => {
      console.log(data)
    })

(完整解答链接)

第十六天:

以下代码输出什么?为什么?

setTimeout(() => {
  console.log(4)
}, 0)
new Promise(resolve => {
  console.log(1)
  for (var i = 0; i < 10000; i++) {
    i == 9999 && resolve()
  }
  console.log(2)
}).then(() => {
  console.log(5)
  Promise.resolve(7)
  .then(v => console.log(v))
}).then(() => {
  console.log(6)
})
console.log(3)

  我的解答:

setTimeout(() => {
  console.log(4)
}, 0)
new Promise(resolve => {
  console.log(1)
  for (var i = 0; i < 10000; i++) {
    i == 9999 && resolve()
  }
  console.log(2)
}).then(() => {
  console.log(5)
  Promise.resolve(7)
  .then(v => console.log(v))
}).then(() => {
  console.log(6)
})
console.log(3)

// 结果:1,2,3,5,7,6,4

// 此处分析代码可以承接第二天的解析
// import : 除了广义的同步任务和异步任务,我们对任务有更精细的定义:
// macro-task(宏任务):包括整体代码script,setTimeout,setInterval
// micro-task(微任务):Promise,process.nextTick
// 事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
// 当遇到微任务和宏任务时优先执行微任务

参考链接: 这一次,彻底弄懂 JavaScript 执行机制

第十七天:

以下代码输出的顺序是什么?为什么

setTimeout(() => {
  console.log(4)
  Promise.resolve(8).then(v => {
    console.log(v)
  }).then(() => {
    console.log(9)
  })
}, 0)
setTimeout(() => {
  console.log(10)
  Promise.resolve(11).then(v => {
    console.log(v)
  }).then(() => {
    console.log(12)
  })
}, 0)
new Promise(resolve => {
  console.log(1)
  for (var i = 0; i < 10000; i++) {
    i == 9999 && resolve()
  }
  console.log(2)
}).then(() => {
  console.log(5)
  Promise.resolve(7)
  .then(v => console.log(v))
}).then(() => {
  console.log(6)
})
console.log(3)

  我的解答:

// 1,2,3,5,7,6,4,8,9,10,11,12
// 答案参考前一天即可

第十八天:

写一个深拷贝函数 deepCopyArray,实现数组的深拷贝

要求:
  • 数组可嵌套
  • 数组元素只能是 Number, Boolean, String, Array 类型之一

思考

  • 当存在循环引用时该如何解决
let arr = [1, 2, 3];
arr.push(arr)
let arr2 = deepCopyArray(arr)

  我的解答:

function deepCopyArray(arr) {
    if(!Array.isArray(arr)){
        console.log(`${arr}不是数组`)
        return
    }
    let result = []
    for (let i = 0; i < arr.length; i++){
        if(typeof arr[i] === "number" || typeof arr[i] === "boolean" || typeof arr[i] === "string"){
            result[i] = arr[i]
        } else if(typeof arr[i] === "object" && Array.isArrat(arr[i])){
            // 遇到引用变量的解决
            result[i] = deepCopyArray(arr[i])
        }
    }
    return result
}

// 思考:当存在循环引用时该如何解决

// 可以参考 cycle.js
// 原理是将循环的那部分“路径”通过变量缓存起来,如果使用上方的 深拷贝函数,可能会导致栈堆溢出,程序卡死

// 使用方式:引用cycle(必须)
// var arr = [1,2,3,4,5]
// arr.push(arr)
// var a = JSON.decycle(arr)
// console.log(a)
// var b = JSON.retrocycle(a)
// console.log(a)
// b[5][1] = 3
// console.log(b)
// console.log(a,arr)

第十九天:

给定整数 n 和 m,写一个函数 dispatch(n, m),把 1~n 尽量平均地分成 m 个组

dispatch(6, 3)
// [[1, 2], [3, 4], [5, 6]]
dispatch(9, 2)
// [[1, 2, 3, 4, 5], [6, 7, 8, 9]]

  我的解答:

function dispatch(n,m){
  let result = []
  let num = Math.floor(n/m)
  // count 记录,从1到n
  let count = 0
  // 相除时的余数
  let remainNum = n % m
  for(let i = 0; i < m;++i){
    // 循环生成数组
    let arr = []
    let interimNum = num
    // 还有余数时,前面数据优先+1
    if(remainNum > 0){
      interimNum ++
      remainNum --
    }
    for(let j = 0; j < interimNum; ++j){
      count ++
      arr.push(count)
    }
    result.push(arr)
  }
  console.log(result)
  return result
} 
dispatch(100,40)

第二十天:

写出一个可以再页面上随意拖动的圆形 div

  我的解答:  链接

第二十一天:

写出一个 once 函数,满足以下条件

  • once 函数只接受一个参数 fnParam,这个参数必须是一个函数
  • once 函数返回一个函数 fnResult
  • 如果调用一次 fnResult,则执行一次 fnParam
  • 如果调用多次 fnResult,也只会执行一次 fnParam

示例如下

function once(fnParam) {
  // your code
}
let log = once(function() {
  console.log(1)
})

log() // print 1
log() // nothing or undefined
log() // nothing or undefined
log() // nothing or undefined

  我的解答:

function once(fnParam) {
  let check = true
  return function(){
    if(check){
      check = false
      fnParam()
    }else{
      return
    }
  }
}
let log = once(function() {
  console.log(1)
})

log() // print 1
log() // nothing or undefined
log() // nothing or undefined
log() // nothing or undefined

第二十二天:

写一个排序函数 sort,满足以下条件:

  • sort 接收一个数组(数组里面只会含有正整数,最大数字约为200)
  • sort 将该数组安从小到大的顺序排列
  • sort 返回排序后的数组
  • 对时间复杂度、空间复杂度均无要求,只要排序不报错


  我的解答:

function sort(arr) {
  return arr.sort((a, b) => a-b)
}

参考链接(更好的答案): 数组排序

第二十三天:

写一个方法,给定一个排序数组,在原数组上删除重复出现的元素,使得每个元素只出现一次,返回删除重复元素后的数组的长度

要求:

  • 不要使用额外的数组空间,在原地修改输入数组并在使用 O(1) 额外空间的条件下完成

示例:
给定数组 nums = [1, 1, 2, 3, 2, 5],函数应该返回新的长度 4,并且原数组 nums 去除了重复出现的数字

var removeDuplicates = function(nums) {
  // your code
}

  我的解答:

var removeDuplicates = function(nums) {
  let obj = {}
  let arr = []
  for(let i = 0; i < nums.length; ++i){
    if(obj[nums[i]]){
      
    }else{ 
      obj[nums[i]] = true
      arr.push(nums[i])
    }
  }
  console.log(arr)
  return arr.length
}
removeDuplicates([1,2,3,44,5,2,3,44])

第二十四天:

写一个对象 Bus,提供 onemit 方法,on 实现监听,emit 实现触发,使用方式如下:

Bus.on('event', function(data) {
  console.log(data)
})

Bus.on('hi', function(data) {
  console.log(data)
})

Bus.emit('event', {name: 'hello'})
Bus.emit('hi', {to: 'hunger'})
Bus.emit('hi', {to: 'valley'})

  我的解答:

let Bus = {
  // 使用event存储 Bus.on 的函数
  event:{},
  on:function(name,fn){
    this.event[name] = fn
  },
  emit:function(name,param){
    this.event[name](param)
  }
}

Bus.on('event', function(data) {
  console.log(data)
})

Bus.on('hi', function(data) {
  console.log(data)
})

Bus.emit('event', {name: 'hello'})
Bus.emit('hi', {to: 'hunger'})
Bus.emit('hi', {to: 'valley'})

第二十五天:

简单模拟 Vue 的数据代理功能

function Vue(options) {
  // your code
}
let app = new Vue({
  data: {
    message: 'hi'
  }
})
console.log(app.message) // 'hi'

  我的解答:

// 按照题目要求,我们需要将data中的数据依次绑定到实例化的对象上,
// defineproperty(给实例化的对象的属性添加监听) 
function Vue(options) {
  if(options.data){
    this.data = options.data
    Object.keys(this.data).map((key)=>{
      Object.defineProperty(this,key,{
        get(){
          return this.data[key]
        },
        set(value){
          console.log(`给${key}赋值${value}`)
          this.data[key] = value
        }
      })
    })
  }
  
}
let app = new Vue({
  data: {
    message: 'hi'
  }
})
console.log(app.message) // 'hi'
app.message = '333'      // '给message赋值333'
console.log(app.message) // 333


第二十六天:

写一个曝光组件 Expourse,实现当 dom 元素出现在浏览器窗口视野中时,执行回调函数,只执行一次。其中回调函数中的 this 代表 dom 元素

var expourse = new Expourse(node)
expourse.once(function() {
  console.log(this) // this 代表 node
  console.log('world') // node 曝光时执行,且只执行一次
})

  我的解答:

function Exposure(node) {
  this.element = node
}

Exposure.prototype.once = function (callback) {
  // 定义一个callback数组,每次当执行once方法时,
  // push回调
  // 添加一个是否以滚动的数组
  if (!this._callback) {
    this._callback = []
    this._checkScroll = []
    this._checkScroll.push(false)
    this._callback.push(callback)
  } else {
    this._checkScroll.push(false)
    this._callback.push(callback)
  }
  this.addEventListener()
//     callback.call(this.element)
}
Exposure.prototype.addEventListener = function () {
  window.onscroll = () => {
    for (let i = 0; i < this._callback.length; ++i) {
      if ((this.element.offsetTop - document.documentElement.clientHeight <= window.scrollY) && !this._checkScroll[i]) {
        this._checkScroll[i] = true
        this._callback[i].call(this.element)
      }
    }
  }
}
let exposure = new Exposure(document.getElementById('one'))
exposure.once(function () {
  console.log(this)
  console.log('world')
})
exposure.once(function () {
  console.log(this)
  console.log('hi')
})

完整链接


第二十七天:

简单模拟 Vue 的 computed 功能:

function Vue(options) {
  // your code
}
let app = new Vue({
  data: {
    firstName: 'Frank',
    lastName: 'Fang'
  },
  computed: {
    name() {
      return this.firstName + ' ' + this.lastName
    }
  }
})

console.log(app.name) // 'Frank Fang'
app.firstName = 'Jack'
console.log(app.name) // 'Jack Fang'
// 不要求实现 app.name 的赋值操作

  我的解答:

// 按照题目要求,我们需要将data中的数据依次绑定到实例化的对象上,
// defineproperty(给实例化的对象的属性添加监听) 
function Vue(options) {
  
  this.data = options.data
  this.computed = options.computed
  let _this = this
  this.Observe = function(data){
     Object.keys(data).map((key)=>{
      Object.defineProperty(this,key,{
        get(){
          if(typeof data[key] === 'function'){
             console.log(this)
            // call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
            // 或者 this.computed[key].call(this)
            // bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
            return this.computed[key].bind(this)()
          }
          return this.data[key]
        },
        set(value){
          console.log(`给${key}赋值${value}`)
          this.data[key] = value
        }
      })
    })
  }
  
  this.Observe(this.data)
  this.Observe(this.computed)
}


let app = new Vue({
  data: {
    firstName: 'Frank',
    lastName: 'Fang'
  },
  computed: {
    name() {
      return this.firstName + ' ' + this.lastName
    }
  }
})

console.log(app.name) // 'Frank Fang'
app.firstName = 'Jack'
console.log(app.name) // 'Jack Fang'
// 不要求实现 app.name 的赋值操作

第二十八天:

写一个 debounce 函数,可以按照如下方式调用,实现的效果是:当连续滚动窗口时,滚动停下300ms后才执行 print

function debounce(fn, time) {
  // your code
}
function print() {
  console.log('print')
}
window.addEventListener('scroll', debounce(print, 300))


  我的解答:

function debounce(fn, time) {
  let t
  let count = 0
  return function(){
    if(count > 0){
      // 只有大于0 时才能清除
      clearTimeout(t)
      t = setTimeout(fn,time)
    }else{
      t = setTimeout(fn,time)
    }
    count ++
  }
}
function print() {
  console.log('print')
}
window.addEventListener('scroll', debounce(print, 300))

第二十九天:

写一个 throttle 函数,可以按照如下方式调用,实现的效果是:当连续滚动窗口的过程中,每100ms至多执行一次 print

function throttle(fn, time) {
  // your code
}
function print() {
  console.log('print')
}
window.addEventListener('scroll', throttle(print, 100))


  我的解答:

function throttle(fn, time) {
  let t = new Date()
  // 用来第一次滚动到现在的时间
  let timeCount = 0
  // 每次打印的计算
  let count = 1
  return function(){
    let nowT = new Date()
    // 判断此时距离上一次滚动时间已经大于time
    if((nowT - t) >= time){
      timeCount += (nowT - t)
      fn(count,timeCount)
      t = new Date()
      count ++
    }
  }
}
function print(count,time) {
  console.log(`隔${time}后,第${count}次打印print`)
}
window.addEventListener('scroll', throttle(print, 3000))


第三十天:

以下代码输出的结果是什么?

var a = 1
function fn1 () {
  function fn3 () {
    function fn2 () {
      console.log(a)
    }
    var a
    fn2()
    a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() // 输出什么


  我的解答:

var a = 1
function fn1 () {
  function fn3 () {
    function fn2 () {
      console.log(a)
    }
    var a
    fn2()
    a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() // 输出什么

// fn === fn3
// fn 执行相当于 fn3 内代码执行
// 1.定义fn2,声明变量 a = undefined
// 2.执行 fn2
// 3.fn2 内代码执行 console.log(a)
// 4. 寻求a 的值,zai fn2 内a无定义,沿着作用域链查找,找到 a ,
// 5. a此时还未赋值,打印 undefined
// 6. fn2 执行结束,此时定义 a 为 4
// 7. 结束

over

原文地址:https://www.cnblogs.com/h246802/p/9077275.html