阅读 2464

DD每周前端七题详解-第一期

DD每周七题-第一期

系列介绍

你盼世界,我盼望你无bug。Hello 大家好!我是霖呆呆!

从这周起呆呆每周都会分享七道前端题给大家,系列名称就是「DD每周七题」。

系列的形式主要是:3道JavaScript + 2道HTML + 2道CSS,帮助我们大家一起巩固前端基础。

所有题目也都会整合至 LinDaiDai/niubility-coding-jsissues中,欢迎大家提供更好的解题思路,谢谢大家😁。

正题

一、Array(3)和Array(3, 4)的区别?

console.log(Array(3))
console.log(Array(3, 4))

console.log(new Array(3))
console.log(new Array(3, 4))

console.log(Array.of(3))
console.log(Array.of(3, 4))
复制代码

考察知识点:

  • Array()new Array()
  • Array()参数个数不同时的不同表现
  • Array.of()的作用

结果:

console.log(Array(3)) // [empty x 3]
console.log(Array(3, 4)) // [3, 4]

console.log(new Array(3)) // [empty x 3]
console.log(new Array(3, 4)) // [3, 4]

console.log(Array.of(3)) // [3]
console.log(Array.of(3, 4)) // [3, 4]
复制代码

总结:

  • Array使不使用new效果都是一样的
  • Array方法,如果参数是一位的话,这个参数表示的是数组的长度,并创建此长度的空数组
  • Array方法,如果参数是多位的话则每一个参数都是数组的一项,会按顺序返回数组
  • Array.of()接收任意个参数,将按顺序成为返回数组中的元素,并返回这个新数组。

github.com/LinDaiDai/n…

二、请创建一个长度为100,值为对应下标的数组

// cool点的写法:
[...Array(100).keys()]

// 其他方法:
Array(100).join(",").split(",").map((v, i) => i)
Array(100).fill().map((v, i) => i)
复制代码

后面两种方法相信大家都能看到懂,也没啥好说的了😄,让我们仔细看一下这个比较cool的写法:

  • 使用Array(100)创建一个内容全为empty的数组:[empty x 100]

  • 使用keys()方法从数组创建一个包含数组键的可迭代对象:

    可迭代对象是不是让你想到了generator,没错的,这里的keys()Array.prototype上的方法,大家可能会把它和我们之前用的比较多的Object.keys()搞混。

    说一下它的作用吧,它其实就像刚刚介绍的一样,会创建一个可迭代对象,那么小伙伴们应该知道,一个可迭代对象返回的数据会是这样的:

    { value: 0, done: false }
    复制代码

    value为这次的返回值,done为当前可迭代对象的逻辑块是否执行完成。

    所以你会看到以下这段代码是会这样执行的:

    let it = Array(100).keys()
    console.log(it.next) // {value: 0, done: false}
    console.log(it.next) // {value: 1, done: false}
    console.log(it.next) // {value: 2, done: false}
    console.log(it.next) // {value: 3, done: false}
    复制代码
  • 至于[...arr]这个就是ES6的写法了,转化为数组,当然你也可以直接用Array.from()。这两种方式都支持将可迭代对象转换为数组。

github.com/LinDaiDai/n…

三、实现 arr[-1] = arr[arr.length - 1]

这道题的意思是:提供一个createArr()方法,用此方法创建的数组满足arr[-1] = arr[arr.length - 1]

function createArr (...elements) {
  // ...代码
  return arr
}
var arr1 = createArr(1, 2, 3)
console.log(arr1[-1]) // 3
console.log(arr1[-2]) // 2
复制代码

解题思路:

其实对于这类题目,我首先想到的会是Object.defineProperty()或者Proxy。因为这里涉及到了对数组值的获取,显然用Proxy是比较合适的。什么?你问我为什么不用Object.defineProperty()?因为这个方法是针对于对象的某一个属性的呀,对数组来说不合适。

所以对于这道题,我们也许可以使用Proxy代理每次传入进来的下标,也就是重写一下数组的get方法,在这个方法中我们去处理这方面的逻辑,一起来看看代码吧😊:

function createArr (...elements) {
  let handler = {
    get (target, key, receiver) { // 第三个参数传不传都可以
      let index = Number(key) // 或者 let index = ~~key
      if (index < 0) {
        index = String(target.length + index)
      }
      return Reflect.get(target, index, receiver)
    }
  }
  let target = [...elements] // 创建一个新数组
  return new Proxy(target, handler)
}
var arr1 = createArr(1, 2, 3)
console.log(arr1[-1]) // 3
console.log(arr1[-2]) // 2
复制代码

注意点:

  • get接收到的第二个参数key表示的是数组下标,它是字符串形式,所以需要转为Number,当然方法有很多种了,使用Number()也可以,使用~~双非按位取反运算符也可以。对比于Number()的好处就是Number(undefined)会转换为NaN;但是使用~~能保证一直是数字,~~undefined === 0。(什么?你还不知道区别?那你得看霖呆呆的这篇文章了:JS中按位取反运算符~及其它运算符)

  • 接下来只需要判断一下传入进来的下标是不是小于0的,小于0的话加上数组的长度就可以了

  • 然后返回index这一项使用的是Reflect.get(target, index)。什么?为什么不直接用target[index]?当然这样也可以,对比target[index]的区别就是Reflect.get(target, index)如果传入的target不是一个Object的话(数组也属于对象),就会报一个类型错误TypeError,而target[index]返回的会是一个undefined。比如这样:

    var obj = 5
    console.log(obj['b']) // undefined
    console.log(Reflect.get(obj, 'b')) // Uncaught TypeError: Reflect.get called on non-object
    复制代码

扩展点:

呆呆这边主要是想扩展一下get的第三个参数receiver(接受者),在MDN上的解释是:

如果target对象中指定了getterreceiver则为getter调用时的this值。

来看个例子理解一下😊。

案例一

例如我们开始有这么一个对象:

var obj = {
  fn: function () {
    console.log('lindaidai')
  }
}
复制代码

现在使用Proxy来赋值到obj1中:

var obj = {
  fn: function () {
    console.log('lindaidai')
  }
}
var obj1 = new Proxy(obj, {
  get (target, key, receiver) {
    console.log(receiver === obj1) // true
    console.log(receiver === target) // false
    return target[key]
  }
})
obj1.fn()
复制代码

可以看到,receiver表示的是obj1这个新的代理对象,target表示的是被代理的对象obj

所以,receiver可以表示使用代理对象本身

案例二

另一种情况,receiver也可以表示是从其继承的对象

var proxy = new Proxy({}, {
  get (target, key, receiver) {
    return receiver;
  }
})
console.log(proxy.getReceiver === proxy) // true
var inherits = Object.create(proxy)
console.log(inherits.getReceiver === inherits) // true
复制代码

这个案例中,我新建了一个空对象的代理对象proxy,使用proxy.getReceiver获取它的receiver,发现它就是代理对象本身。

而如果我将这个代理对象作为一个原型对象,创建出一个新的对象inherits,也就是实现了原型式继承,那么这时候receiver的值就是这个被继承的对象inherits

总结

  • 可以使用Proxy代理,来改变每次获取数组的值。
  • Proxyget中,第二个参数是字符串,即使传入的是数组下标。
  • 对比于Number()的好处就是Number(undefined)会转换为NaN;但是使用~~能保证一直是数字,~~undefined === 0
  • 对比target[index]的区别就是Reflect.get(target, index)如果传入的target不是一个Object的话(数组也属于对象),就会报一个类型错误TypeError,而target[index]返回的会是一个undefined
  • Proxyget中,第三个参数receiver通常为使用代理对象本身或从其继承的对象。

github.com/LinDaiDai/n…

四、addEventListener和attachEvent的区别?

  • 前者是标准浏览器中的用法,后者IE8以下
  • addEventListener可有冒泡,可有捕获;attachEvent只有冒泡,没有捕获。
  • 前者事件名不带on,后者带on
  • 前者回调函数中的this指向当前元素,后者指向window

github.com/LinDaiDai/n…

五、addEventListener函数的第三个参数

第三个参数涉及到冒泡和捕获,是true时为捕获,是false则为冒泡。

或者是一个对象{passive: true},针对的是Safari浏览器,禁止/开启使用滚动的时候要用到。

github.com/LinDaiDai/n…

六、文字单超出显示省略号

div {
  width: 200px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
复制代码

github.com/LinDaiDai/n…

七、文字多行超出显示省略号

div {
  width: 200px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}
复制代码

该方法适用于WebKit浏览器及移动端。

跨浏览器兼容方案:

p {
  position:relative;
  line-height:1.4em;
  /* 3 times the line-height to show 3 lines */
  height:4.2em;
  overflow:hidden;
}
p::after {
  content:"...";
  font-weight:bold;
  position:absolute;
  bottom:0;
  right:0;
  padding:0 20px 1px 45px;
}
复制代码

github.com/LinDaiDai/n…

后语

你盼世界,我盼望你无bug。这篇文章就介绍到这里。

您每周也许会花48小时的时间在工作💻上,会花49小时的时间在睡觉😴上,也许还可以再花20分钟的时间在呆呆的7道题上,日积月累,我相信我们都能见证彼此的成长😊。

什么?你问我为什么系列的名字叫DD?因为呆呆呀,哈哈😄。

喜欢霖呆呆的小伙还希望可以关注霖呆呆的公众号 LinDaiDai 或者扫一扫下面的二维码👇👇👇。

我会不定时的更新一些前端方面的知识内容以及自己的原创文章🎉

你的鼓励就是我持续创作的主要动力 😊。

本文使用 mdnice 排版