# fill 原理

# 使用 for 实现

Array.prototype._fill = function (value = [], start = 0, end = this.length) {
  //start 大于数组长度 或 end 大于数组长度返回自身 不然下面的 this [i] 会改变自身长度
  if (start > this.length || end > this.length) return this
  //start < 0 或 end < 0 则是按 length + start|end 来计算,MDN 有介绍 
  start = start < 0 ? this.length + start : start
  end = end < 0 ? this.length + end : end 
  
  for (let i = start; i < end; i++) {
    // 边界判断 什么参数都不传则都为 undefined
    if (!arguments.length) this[i] = undefined 
    else this[i] = value
  } 
  return this
}

# 测试

// fill
[1, 2, 3].fill()                // [undefined, undefined, undefined]
[1, 2, 3].fill(4)               // [4, 4, 4]
[1, 2, 3].fill(4, 1)            // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2)         // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1)         // [1, 2, 3]
[1, 2, 3].fill(4, 3, 3)         // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2)       // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN)     // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5)         // [1, 2, 3]
Array(3).fill(4)                // [4, 4, 4]
[].fill.call({ length: 3 }, 4)  // {0: 4, 1: 4, 2: 4, length: 3}
// _fill
[1, 2, 3]._fill()               // [undefined, undefined, undefined]
[1, 2, 3]._fill(4)              // [4, 4, 4]
[1, 2, 3]._fill(4, 1)           // [1, 4, 4]
[1, 2, 3]._fill(4, 1, 2)        // [1, 4, 3]
[1, 2, 3]._fill(4, 1, 1)        // [1, 2, 3]
[1, 2, 3]._fill(4, 3, 3)        // [1, 2, 3]
[1, 2, 3]._fill(4, -3, -2)      // [4, 2, 3]
[1, 2, 3]._fill(4, NaN, NaN)    // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5)         // [1, 2, 3]
Array(3)._fill(4)               // [4, 4, 4]
[]._fill.call({ length: 3 }, 4) // {0: 4, 1: 4, 2: 4, length: 3}

# 使用 for in 实现

Array.prototype._fill = function (value = [], start = 0, end = this.length) {
  //start 大于数组长度 或 end 大于数组长度返回自身 不然下面的 this [i] 会改变自身长度
  if (start > this.length || end > this.length) return this
  //start < 0 或 end < 0 则是按 length + start|end 来计算,MDN 有介绍 
  start = start < 0 ? this.length + start : start
  end = end < 0 ? this.length + end : end 
  // 用于判断类似 Array (4)._fill (4) this+'' 这种情况时 此时会是三个逗号
  const commaLength = this.length - 1
  let comma = ''
  for (let i = 0; i < commaLength; i++) comma += ','
  
  // 进行的边界判断 
  if (this + '' == comma || this + '' === '[object Object]') {
    for (let i = 0; i < end; i++) {
      this[i] = value
    }
  return this
}
  // 进行遍历
  for (const key in this) {
    if ('_fill' === key) return this
    // 从 start 开始 小于 start 就跳过
    if (key < start) continue
    // 小于 end 就停止
    if (key < end) this[key] = value
    // 判断上面什么参数也没传则都为 undefined
    if(!arguments.length) this[key] = undefined  
  }
}

# 测试

// fill
[1, 2, 3].fill(4);               // [4, 4, 4]
[1, 2, 3].fill(4, 1);            // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2);         // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1);         // [1, 2, 3]
[1, 2, 3].fill(4, 3, 3);         // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2);       // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN);     // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5);         // [1, 2, 3]
Array(3).fill(4);                // [4, 4, 4]
[].fill.call({ length: 3 }, 4);  // {0: 4, 1: 4, 2: 4, length: 3}
// _fill
[1, 2, 3]._fill(4)              // [4, 4, 4]
[1, 2, 3]._fill(4, 1)           // [1, 4, 4]
[1, 2, 3]._fill(4, 1, 2)        // [1, 4, 3]
[1, 2, 3]._fill(4, 1, 1)        // [1, 2, 3]
[1, 2, 3]._fill(4, 3, 3)        // [1, 2, 3]
[1, 2, 3]._fill(4, -3, -2)      // [4, 2, 3]
[1, 2, 3]._fill(4, NaN, NaN)    // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5)         // [1, 2, 3]
Array(3)._fill(4)               // [4, 4, 4]
[]._fill.call({ length: 3 }, 4) // {0: 4, 1: 4, 2: 4, length: 3}

# join 原理

# 使用 for 实现

Array.prototype._join = function (separator = ',') {
  let str = ''
  for (let i = 0; i < this.length; i++)
    str = i === 0 ? `${this[i]}` : `${str}${separator}${this[i]}`
  return str
}

# 测试

// join
const str = ['Wind', 'Rain', 'Fire']
str.join()      // Wind,Rain,Fire
str.join(', ')  // Wind, Rain, Fire
str.join(' + ') // Wind + Rain + Fire
str.join('')    // WindRainFire
// _join
const str = ['Wind', 'Rain', 'Fire']
str._join()      // Wind,Rain,Fire
str._join(', ')  // Wind, Rain, Fire
str._join(' + ') // Wind + Rain + Fire
str._join('')    // WindRainFire

# 使用 for in 实现

Array.prototype._join = function (value = ',') {
  let str = ''
  for (const key in this) {
    if('_join' === key) return str
    str = key == 0 ? `${str}${this[key]}` : `${str}${value}${this[key]}`
  }
}

# 测试

// join
const str = ['Wind', 'Rain', 'Fire']
str.join()      // Wind,Rain,Fire
str.join(', ')  // Wind, Rain, Fire
str.join(' + ') // Wind + Rain + Fire
str.join('')    // WindRainFire
// _join
const str = ['Wind', 'Rain', 'Fire']
str._join()      // Wind,Rain,Fire
str._join(', ')  // Wind, Rain, Fire
str._join(' + ') // Wind + Rain + Fire
str._join('')    // WindRainFire

# String includes 原理

# 使用 for 实现

String.prototype._includes = function (searchString, position = 0) {
  // 1. 如果值一样直接 return true 节约性能
  if (searchString == this) return true
  // 2. 当 position 索引大于自身长度时则直接返回 false
  if (position > this.length) return false
  
  // 3. 字符串的长度
  const searchStringLength = searchString.toString().length
  // 4. 如果为负数从 0 开始查找
  position = position < 0 ? 0 : position
  // 5. 定义用来接收每次遍历完的字符串 来和 searchString 比较
  let resultStr = ''
  
  // 6. 需要遍历的次数 例如:abcde 查找 de -> ab bc cd de 需要遍历 4 次
  let cycleIndex = this.length - searchStringLength + 1
  
  // 7.getStr 函数用于获取每次递进的字符串
  // 8. 举栗:'abcde' -> 会以这种形式依次进行遍历 ab -> bc -> cd -> de
  const getStr = (index) => {
    for (let i = 0; i < searchStringLength; i++)
      resultStr += this[index++]
    return resultStr
  }
  for (let i = position; i < cycleIndex; i++){
    // 如果没查到就置空
    if (searchString != getStr(i)) resultStr = ''
    else return true
  }
  return false
}

# 测试

const str = 'To be, or not to be, that is the question.'
// includes
str.includes('To be')       // true
str.includes('question')    // true
str.includes('nonexistent') // false
str.includes('To be', 1)    // false
str.includes('TO BE')       // false
// _includes
str._includes('To be')       // true
str._includes('question')    // true
str._includes('nonexistent') // false
str._includes('To be', 1)    // false
str._includes('TO BE')       // false

# 使用 while 实现

String.prototype._includes = function (value, startIndex = 0) {
  if (this == value) return true
  // 字符串长度
  const length = this.length
  // 参数字符串长度
  const valueLength = value.length
  //startIndex < 0 就从 0 开始搜索
  startIndex = startIndex < 0 ? 0 : startIndex
  let i = startIndex
  // 循环次数
  while (i + valueLength <= length) {
    let num = 0, str = ''
    
    // 判断需要遍历次数 例如 abcde 查找 de -> ab bc cd de 4 次
    while (num < valueLength) str += this[i + num++]
    i++
    // 如果查找到返回 true
    if (str === value) return true
  }
  return false
}

# 测试

const str = 'To be, or not to be, that is the question.'
// includes
str.includes('To be')       // true
str.includes('question')    // true
str.includes('nonexistent') // false
str.includes('To be', 1)    // false
str.includes('TO BE')       // false
// _includes
str._includes('To be')       // true
str._includes('question')    // true
str._includes('nonexistent') // false
str._includes('To be', 1)    // false
str._includes('TO BE')       // false

# Array includex 原理

# 使用 for 实现

Array.prototype._includes = function (value, start = 0) {
  if (start > this.length) return false
  start = start < 0 ? this.length + start : start
  for (let i = start; i < this.length; i++){
  if(this[i] === value || Number.isNaN(this[i])) return true
  }
  return false
}

# 测试

// includes
[1, 2, 3].includes(3, -1) // true
[1, 2, 3].includes(2)     // false 
[1, 2, 3].includes(4)     // false
[1, 2, 3].includes(3, 3)  // true
[1, 2, NaN].includes(NaN) // true
// _includes
[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false 
[1, 2, 3].includes(3, 3)  // false
[1, 2, 3].includes(3, -1) // true
[1, 2, NaN].includes(NaN) // true

# 使用 for in 实现

Array.prototype._includes = function (value, start = 0) {
  if (start > this.length) return false
  start = start < 0 ? this.length + start : start
  for (const key in this) {
    if(key < start) continue
    if(value === this[key] || Number.isNaN(this[key])  === Number.isNaN(NaN)) return true
  }
  return false
}

# 测试

// includes
[1, 2, 3].includes(3, -1) // true
[1, 2, 3].includes(2)     // false 
[1, 2, 3].includes(4)     // false
[1, 2, 3].includes(3, 3)  // true
[1, 2, NaN].includes(NaN) // true
// _includes
[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false 
[1, 2, 3].includes(3, 3)  // false
[1, 2, 3].includes(3, -1) // true
[1, 2, NaN].includes(NaN) // true