前端复习------js篇(中)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 前端复习------js篇

mutationobserver


Mutation Observer API 用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。


概念上,它很接近事件,可以理解为 DOM 发生变动就会触发 Mutation Observer 事件。但是,它与事件有一个本质不同:事件是同步触发,也就是说,DOM 的变动立刻会触发相应的事件;Mutation Observer 则是异步触发,DOM 的变动并不会马上触发,而是要等到当前所有 DOM 操作都结束才触发。


Mutation Observer 有以下特点。


  • 它等待所有脚本任务完成后,才会运行(即异步触发方式)。


  • 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动。


  • 它既可以观察 DOM 的所有类型变动,也可以指定只观察某一类变动。


MutationObserver构造函数


通过new创建一个观察者对象,改构造函数需要传递一个回调函数。


var observer = new MutationObserver(callback);


该回调函数接收两个参数,参一:变动dom的数组,参二:观察者实例。


let callback = function (mutations, self) {
}


实例方法:


1.observe(变动元素,观察选项)


let odiv = document.getElementById('#div');
let  options = {
  'childList': true,
  'attributes':true
} ;
observer.observe(odiv, options);


以下是 options对象的各属性及其描述:


属性 类型 描述
childList Boolean 是否观察子节点的变动
attributes Boolean 是否观察属性的变动
characterData Boolean 是否节点内容或节点文本的变动
subtree Boolean 是否观察所有后代节点的变动
attributeOldValue Boolean 观察 attributes 变动时,是否记录变动前的属性值
characterDataOldValue Boolean 观察 characterData 变动时,是否记录变动前的属性值
attributeFilter Array 表示需要观察的特定属性(比如['class','src']),不在此数组中的属性变化时将被忽略


注意:


  1. 对一个节点添加观察器,就像使用addEventListener方法一样,多次添加同一个观察器是无效的,回调函数依然只会触发一次。但是,如果指定不同的options对象,就会被当作两个不同的观察器。


  1. 监听选项中必须指定 childList、attributes 和 characterData 中的一种或多种。


Failed to execute 'observe' on 'MutationObserver': The options object must set at least one of 'attributes', 'characterData', or 'childList' to true.


2.disconnect()


该方法是停止监听dom变化的。


observer.disconnect();


3.takeRecords()


用来清除变动记录,即不再处理未处理的变动。该方法返回变动记录的数组。


// 保存所有没有被观察器处理的变动
observer.takeRecords();


MutationRecord 对象


即每次dom发生变化所产生的状态值。


属性 类型 描述
type String 根据变动类型,值为 attributes, characterData 或 childList
target Node 发生变动的DOM节点
addedNodes NodeList 被添加的节点,或者为 null
removedNodes NodeList 被删除的节点,或者为 null
previousSibling Node 被添加或被删除的节点的前一个兄弟节点,或者为 null
nextSibling Node 被添加或被删除的节点的后一个兄弟节点,或者为 null
attributeName String 发生变更的属性的本地名称,或者为 null
attributeNamespace String 发生变更的属性的命名空间,或者为 null
oldValue String 如果 type 为 attributes,则返回该属性变化之前的属性值;如果 type 为 characterData,则返回该节点变化之前的文本数据;如果 type为 childList,则返回 null


参考 JavaScript 标准参考教程(alpha))DOM模型Mutation Observer API


优化js中的逻辑判断


普通的写法:if-else if-else, switch-case


一元判断:Map, 对象


Map: [[条件, 逻辑参数],[条件, 逻辑参数],...]


对象: {条件:逻辑参数}


/**
 * 按钮点击事件
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
 */
const onButtonClick = (status)=>{
  if(status == 1){
    sendLog('processing')
    jumpTo('IndexPage')
  }else if(status == 2){
    sendLog('fail')
    jumpTo('FailPage')
  }else if(status == 3){
    sendLog('fail')
    jumpTo('FailPage')
  }else if(status == 4){
    sendLog('success')
    jumpTo('SuccessPage')
  }else if(status == 5){
    sendLog('cancel')
    jumpTo('CancelPage')
  }else {
    sendLog('other')
    jumpTo('Index')
  }
}


const actions = {
  '1': ['processing','IndexPage'],
  '2': ['fail','FailPage'],
  '3': ['fail','FailPage'],
  '4': ['success','SuccessPage'],
  '5': ['cancel','CancelPage'],
  'default': ['other','Index'],
}
/**
 * 按钮点击事件
 * @param {number} status 活动状态:1开团进行中 2开团失败 3 商品售罄 4 开团成功 5 系统取消
 */
const onButtonClick = (status)=>{
  let action = actions[status] || actions['default'],
      logName = action[0],
      pageName = action[1]
  sendLog(logName)
  jumpTo(pageName)
}


const actions = new Map([  [1, ['processing','IndexPage']],
  [2, ['fail','FailPage']],
  [3, ['fail','FailPage']],
  [4, ['success','SuccessPage']],
  [5, ['cancel','CancelPage']],
  ['default', ['other','Index']]
])
/**
 * 按钮点击事件
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
 */
const onButtonClick = (status)=>{
  let action = actions.get(status) || actions.get('default')
  sendLog(action[0])
  jumpTo(action[1])
}


多层判断:Map


Map: [[条件,() => {}], [条件,() => {}], ....]


  • 条件可以是多个条件合并的字符串


  • 条件可以是一个对象


  • 条件可以是正则,为了匹配更多相同逻辑不同条件的处理


  • .....


对象:{条件:() => {}, 条件:() => {}, ...}


  • 条件可以是多个条件合并的字符串


/**
 * 按钮点击事件
 * @param {number} status 活动状态:1开团进行中 2开团失败 3 开团成功 4 商品售罄 5 有库存未开团
 * @param {string} identity 身份标识:guest客态 master主态
 */
const onButtonClick = (status,identity)=>{
  if(identity == 'guest'){
    if(status == 1){
      //do sth
    }else if(status == 2){
      //do sth
    }else if(status == 3){
      //do sth
    }else if(status == 4){
      //do sth
    }else if(status == 5){
      //do sth
    }else {
      //do sth
    }
  }else if(identity == 'master') {
    if(status == 1){
      //do sth
    }else if(status == 2){
      //do sth
    }else if(status == 3){
      //do sth
    }else if(status == 4){
      //do sth
    }else if(status == 5){
      //do sth
    }else {
      //do sth
    }
  }
}


const actions = new Map([
  ['guest_1', ()=>{/*do sth*/}],
  ['guest_2', ()=>{/*do sth*/}],
  ['guest_3', ()=>{/*do sth*/}],
  ['guest_4', ()=>{/*do sth*/}],
  ['guest_5', ()=>{/*do sth*/}],
  ['master_1', ()=>{/*do sth*/}],
  ['master_2', ()=>{/*do sth*/}],
  ['master_3', ()=>{/*do sth*/}],
  ['master_4', ()=>{/*do sth*/}],
  ['master_5', ()=>{/*do sth*/}],
  ['default', ()=>{/*do sth*/}],
])
/**
 * 按钮点击事件
 * @param {string} identity 身份标识:guest客态 master主态
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 开团成功 4 商品售罄 5 有库存未开团
 */
const onButtonClick = (identity,status)=>{
  let action = actions.get(`${identity}_${status}`) || actions.get('default')
  action.call(this)
}


const actions = {
  'guest_1':()=>{/*do sth*/},
  'guest_2':()=>{/*do sth*/},
  //....
}
const onButtonClick = (identity,status)=>{
  let action = actions[`${identity}_${status}`] || actions['default']
  action.call(this)
}


const actions = new Map([
  [{identity:'guest',status:1},()=>{/*do sth*/}],
  [{identity:'guest',status:2},()=>{/*do sth*/}],
  //...
])
const onButtonClick = (identity,status)=>{
  // ...actions表示将[[],[],[]]内部的数组取出[],[],[]
  let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status))
  action.forEach(([key,value])=>value.call(this))
}


const actions = ()=>{
  const functionA = ()=>{/*do sth*/}
  const functionB = ()=>{/*do sth*/}
  return new Map([
    [{identity:'guest',status:1},functionA],
    [{identity:'guest',status:2},functionA],
    [{identity:'guest',status:3},functionA],
    [{identity:'guest',status:4},functionA],
    [{identity:'guest',status:5},functionB],
    //...
  ])
}
const onButtonClick = (identity,status)=>{
  let action = [...actions()].filter(([key,value])=>(key.identity == identity && key.status == status))
  action.forEach(([key,value])=>value.call(this))
}


const actions = ()=>{
  const functionA = ()=>{/*do sth*/}
  const functionB = ()=>{/*do sth*/}
  const functionC = ()=>{/*send log*/}
  return new Map([
    [/^guest_[1-4]$/,functionA],
    [/^guest_5$/,functionB],
    [/^guest_.*$/,functionC],
    //...
  ])
}
const onButtonClick = (identity,status)=>{
  let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
  action.forEach(([key,value])=>value.call(this))
}


学习自 掘金Think.大佬


promise


常用的静态方法:all,race,resolve, reject。


常用的实例方法:then, catch。


对于all方法来说,参数是一个若干个promise组成的数组,如果这些promise定义了自己的then,catch方法,并且有返回值,不管是成功还是失败,那么执行all方法的then就会执行,catch就不会执行。


const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(res => res)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result)
.catch();//这里没有返回值,才会执行all的catch方法。
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
//执行结果
Error: 报错了


axios对其还做了一步简化。将接收到的请求参数封装成一个函数接收spread()


function getUserAccount() {
  return axios.get('/user/12345');
}
function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  }));


promise只执行一次,但是同一个promise的then,catch可以被执行多次。


const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('once')
    resolve('success')
  }, 1000)
})
const start = Date.now()
promise.then((res) => {
  console.log(res, Date.now() - start)
})
promise.then((res) => {
  console.log(res, Date.now() - start)
})
//只会打印出一次once


.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。


Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)


.then抛出的错误不会被第二个参数函数捕获,只能被后面的catch捕获。


Promise.resolve()
  .then(function success (res) {
  //注意这里是抛出错误,而非返回错误。
    throw new Error('error')
  }, function fail1 (e) {
    console.error('fail1: ', e)
  })
  .catch(function fail2 (e) {
    console.error('fail2: ', e)
  })


手写promise


class Promise {
  constructor(executor) {//执行器函数
    let _this = this
    _this.status = 'pending'  //保存当前状态
    _this.value = undefined //执行成功时,传递的值
    _this.error = undefined//执行失败时,传递的值
    function resolve(value) {
      if (_this.status === 'pending') {
        _this.status = 'resolved'
        _this.value = value
      }
    }
    function reject(error) {
      if (_this.status === 'pending') {
        _this.status = 'rejected'
        _this.error = error
      }
    }
    executor(resolve, reject)//调用执行器函数
  }
  then(onfulfilled, onRejected) {
    let _this = this
    if (_this.status === 'resolved') {
      onfulfilled(_this.value)
    } else if (_this.status === 'rejected') {
      onRejected(_this.error)
    }
  }
}
//测试,此代码的问题是不能实现异步方法
let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('我是张昊')
    reject('我是李龙淼')
  }, 1000)
}).then(res => {
  console.log(res)
}, res => {
  console.log(res)
})
//这里什么也不输出


原因是我们在then函数中只对成功态和失败态进行了判断,而实例被new时,执行器中的代码会立即执行,但setTimeout中的代码将稍后执行,也就是说,then方法执行时,Promise的状态没有被改变依然是pending态,所以我们要对pending态也做判断,而由于代码可能是异步的,那么我们就要想办法把回调函数进行缓存,并且,then方法是可以多次使用的,所以要能存多个回调,那么这里我们用一个数组。


创建两个数组,为了存储成功的回调(resolve)和失败的回调(reject)


_this.onResolvedCallbacks = []; // 存放then成功的回调,这里存的都是resolve()的函数
    _this.onRejectedCallbacks = []; // 存放then失败的回调,这里存的都是reject()的函数


在then中判断pending的判断,将then中的回调分别存入onResolvedCallbacksonRejectedCallbacks数组中


if (_this.status === 'pending') {
   // 每一次then时,如果是等待态,就把回调函数push进数组中,什么时候改变状态什么时候再执行
      //缓存成功时的回调函数,等到状态变为resolved的时候,在执行
      _this.onResolvedCallbacks.push(function () {
        onfulfilled(_this.value)
      })
      //缓存失败时的回调函数,等到状态变为rejected的时候,在执行
      _this.onRejectedCallbacks.push(function () {
        onRejectedCallbacks(_this.error)
      })
 } 


在resolve方法中缓存异步执行的成功回调,在reject方法中缓存异步执行的失败的回调


function resolve(value) {
      if (_this.status === 'pending') {
        _this.status = 'resolved'
        _this.value = value
        // 当成功的函数 (resolve()) 被调用时,之前缓存的回调函数会被一一调用
        _this.onResolvedCallbacks.map(callback => callback())
      }
    }
    function reject(error) {
      if (_this.status === 'pending') {
        _this.status = 'rejected'
        _this.error = error
        // 当失败的函数 (reject()) 被调用时,之前缓存的回调函数会被一一调用
        _this.onRejectedCallbacks.map(callback => callback())
      }
    }


为了防止promise在执行出现错误,我们需要做错误处理


try {
      executor(resolve, reject)
    } catch (e) {
      console.log(e)
    }


promise的链式调用。由于promise的状态会发生改变,所以then返回的promise是一个全新的。


有点复杂!!! promise的其他原型方法


// 捕获错误的方法,在原型上有catch方法,返回一个没有resolve的then结果即可
    Promise.prototype.catch = function (callback) {
        return this.then(null, callback)
    }
    // 解析全部方法,接收一个Promise数组promises,返回新的Promise,遍历数组,都完成再resolve
    Promise.all = function (promises) {
        //promises是一个promise的数组
        return new Promise(function (resolve, reject) {
            let arr = []; //arr是最终返回值的结果
            let i = 0; // 表示成功了多少次
            function processData(index, y) {
                arr[index] = y;
                if (++i === promises.length) {
                    resolve(arr);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(function (y) {
                    processData(i, y)
                }, reject)
            }
        })
    }
    // 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
    Promise.race = function (promises) {
        return new Promise(function (resolve, reject) {
            for (var i = 0; i < promises.length; i++) {
                promises[i].then(resolve,reject)
            }
        })
    }
    // 生成一个成功的promise
    Promise.resolve = function(value){
        return new Promise(function(resolve,reject){
            resolve(value);
        })
    }
    // 生成一个失败的promise
    Promise.reject = function(error){
        return new Promise(function(resolve,reject){
            reject(error);
        })
    }


学习自


promise必知必会


rocYoung对promise的分析


前端的路由跳转


Hash 方法是在路由中带有一个 #,主要原理是通过监听 # 后的 URL 路径标识符的更改而触发的浏览器 hashchange 事件,然后通过获取 location.hash 得到当前的路径标识符,再进行一些路由跳转的操作。


class RouterClass {
  constructor() {
    this.isBack = false
    this.routes = {}        // 记录路径标识符对应的cb
    this.currentUrl = ''    // 记录hash只为方便执行cb
    this.historyStack = []  // hash栈
    window.addEventListener('load', () => this.render())
    window.addEventListener('hashchange', () => this.render())
  }
  /* 初始化 */
  static init() {
    window.Router = new RouterClass()
  }
  /* 记录path对应cb */
  route(path, cb) {
    this.routes[path] = cb || function() {}
  }
  /* 入栈当前hash,执行cb */
  render() {
    if (this.isBack) {      // 如果是由backoff进入,则置false之后return
      this.isBack = false   // 其他操作在backoff方法中已经做了
      return
    }
    this.currentUrl = location.hash.slice(1) || '/'
    //将每一个路径都加入到栈中,为了back的判断
    this.historyStack.push(this.currentUrl)
    this.routes[this.currentUrl]()
  }
  /* 路由后退 */
  back() {
    this.isBack = true
    this.historyStack.pop()                   // 移除当前hash,回退到上一个
    const { length } = this.historyStack
    if (!length) return //如果栈中没有路径了,将直接结束
    let prev = this.historyStack[length - 1]  // 拿到要回退到的目标hash
    location.hash = `#${ prev }`   //为了使手动跳转正常进行,需要将当前路径加上一个#,来满足slice的分割
    this.currentUrl = prev
    this.routes[prev]()                       // 执行对应cb
  }
}


history


  1. history.go(n):路由跳转,比如n为 2 是往前移动2个页面,n为 -2 是向后移动2个页面,n为0是刷新页面


  1. history.back():路由后退,相当于 history.go(-1)


  1. history.forward():路由前进,相当于 history.go(1)


  1. history.pushState():添加一条路由历史记录,如果设置跨域网址则报错,浏览器有记录。


  1. history.replaceState():替换当前页在路由历史记录的信息,浏览器无记录。


  1. popstate 事件:当活动的历史记录发生变化,就会触发 popstate 事件,在点击浏览器的前进后退按钮或者调用上面前三个方法的时候也会触发,参见 MDN


何为闭包?


闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境里是若干对符号和值的对应关系,它既要包括约束变量该函数内部绑定的符号),也要包括自由变量在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。捕捉时对于值的处理可以是值拷贝,也可以是名称引用。


而闭包则意味着同时包括函数指针环境两个关键元素。在编译优化当中,没有捕捉自由变量的闭包可以被优化成普通函数。


闭包中引入的变量何时被销毁?


闭包中访问的外部变量是存放在堆内存中的。


变量的生命周期取决于闭包的生命周期。被闭包引用的外部作用域中的变量将一直存活直到闭包函数被销毁。如果一个变量被多个闭包所引用,那么直到所有的闭包被垃圾回收后,该变量才会被销毁。


维基百科


数组方法(存在高阶函数)的返回值


map:该方法的返回值就是回调函数的返回值组成的数组。


filter: 该方法的返回值就是回调函数符合条件的返回值组成的数组。


let arr = [1, 2, 3, 4]
let resArr = arr.filter(item => {
  if (item > 2) {
    return item
  }
})
//等价于
arr.filter(item => item > 2)
console.log(resArr)


forEach:该方法没有返回值。


reduce:该方法返回的值是累计器(回调函数的第一个参数)累计后的值。


let arr = [
  { id: 1, name: 'zh', age: 20 },
  { id: 2, name: 'hy', age: 19 },
  { id: 3, name: 'llm', age: 19 }
]
let reArr = arr.reduce((pre, next) => {
  if (next.age === 19) {
    return pre.concat(Object.assign({}, next, { sex: 'female' }))
  }
  return []//这里必须有返回值,不然会报错,因为每遍历一项都需要有返回值。如果上面用到了pre,下面没有返回就会报错,pre的值是每次回调函数返回的值。
}, [])
console.log(reArr)
// {id: 2, name: "hy", age: 19, sex: "female"}
// {id: 3, name: "llm", age: 19, sex: "female"}


every: 该方法的返回值是布尔值,只有数组中的元素都满足条件,才回返回true,否则返回false。


let arr = [1, 2, 3, 4]
let resArr = arr.every(item => item >= 1)
console.log(resArr)


some:该方法返回的是布尔值,表示只要数组中有一个元素满足条件就返回true,否则返回false。


find:该方法返回的是第一个满足条件的值。


findIndex:该方法返回的是第一个满足条件的值的下标。


URLsearchParams


URLsearchParams(url)用来解析url参数的。


//URLSearchParams用来解析参数的
let url = '?name=zh&age=20';
let searchParams = new URLSearchParams(url)
// console.log(searchParams)//map对象
let arr = [...searchParams]
console.log(arr) //[ [ 'name', 'zh' ], [ 'age', '20' ] ]


获取单个参数。


searchParams.get('name')


校验参数是否存在。


searchParams.has('sex') // false
searchParams.has('age') // true


添加参数。


searchParams.append('sex', 'male')
console.log(searchParams)//URLSearchParams { 'name' => 'zh', 'age' => '20', 'sex' => 'male' }
console.log(url)//?name=zh&age=20,注意url并不会添加上该参数,但是解析后是有该参数的


删除参数。


searchParams.delete('sex');
searchParams.has('sex'); // false


修改参数。


searchParams.set('age', 22)
console.log(searchParams)//URLSearchParams { 'name' => 'zh', 'age' => '22', 'sex' => 'male' }


将解析后的参数,再转为查询字符串。


searchParams.toString() //name=zh&age=22&sex=male


重写数组方法


重写map方法,利用for循环


const selfMap = function (fn, context = this) {
  //这里的context会被当做fn函数中的this,如果不传入context,那么this就是调用的数组。
  let arr = Array.prototype.slice.call(context)
  let mappedArr = Array()
  for (let i = 0; i < arr.length; i++) {
    //判断稀疏数组的情况
    if (!arr.hasOwnProperty(i)) continue;
    mappedArr[i] = fn.call(context, arr[i], i, this)
    // console.log(context)
  }
  return mappedArr
}
Array.prototype.selfMap = selfMap
let resArr = [0, 0, 0, 1].selfMap(number => number * 2, [2, 3, 4])
console.log(resArr)//[4,6,8]


我对map方法的错误理解,context只是改变了this指向,然而并不是改变函数执行的数组。


作者大大,第二个方法,即map方法,我感觉应该把context初始为this,然后slice方法中传入context,这样传入第二个参数的时候,this的值才回改变。不然没有效果 let resArr = [0, 0, 0, 1].selfMap(number => number * 2, [2, 3, 4]) console.log(resArr)//这里仍然是[0,0,0,2],而不是[4,6,8] //下面这样就是对的 const selfMap = function (fn, context = this) { //这里的context会被当做fn函数中的this let arr = Array.prototype.slice.call(context) let mappedArr = Array() for (let i = 0; i number * 2, [2, 3, 4]) console.log(resArr)//[4,6,8] 如果不对,望告知。


这里是js原生的map测试


let resArr = [4, 5, 6].map(function (item, index, arr1) {
  console.log(arr1)//[4,5,6]
  console.log('this', this)//[1,2,3]
  return item * 2//作用的是[4,5,6]
}, [1, 2, 3])
console.log(resArr)


重写map方法。利用reduce方法。


const selfMap = function (fn, context) {
  let arr = Array.prototype.slice.call(this)
  return arr.reduce((pre, cur, index) => {
    //这个返回值就是pre,然后将完全处理后的元素,都返回给pre,然后展开pre即可。
    //也就是这里我有一个疑问,他每次都返回pre,每次都展开pre,每次展开都会重复上一次返回的pre中的元素呀,所以应该会重复很多呀。
    return [...pre, fn.call(context, cur, index, this)]
  }, [])
}
Array.prototype.selfMap = selfMap
let resArr = [1, 2, 3].selfMap(item => item * 2)
console.log(resArr)


解决疑惑:因为数组的内存地址都是一样的,pre的内存地址一样的,只是展开了最后一次赋值的值。


let a = new Array(10)
let resArr = []
for (let i = 0; i <= a.length; i++) {
  let arr = [i]
  resArr = [...arr, 2, 3, 4]
}
console.log(resArr)//[ 10, 2, 3, 4 ]


重写filter方法,利用for循环。


const selfFilter = function (fn, context) {
  //要处理的数组
  let arr = Array.prototype.slice.call(this)
  let resArr = Array()
  for (let i = 0; i < arr.length; i++) {
    //判断返回的条件是否正确,并且加入到数组中。
    fn.call(context, arr[i], i, this) && resArr.push(arr[i])
  }
  return resArr
}
Array.prototype.selfFilter = selfFilter
let arr = [1, 2, 3]
let arr1 = arr.selfFilter(item => item > 2)
console.log(arr1)


重写filter,利用reduce。


const selfFilter = function (fn, context) {
  let arr = Array.prototype.slice.call(this)
  let resArr = []
  return arr.reduce((pre, cur, index) => {
    // if (fn.call(context, cur, index, this)) {
    //   resArr.push(cur)
    //   return resArr
    // }
    return fn.call(context, cur, index, this) ? [...pre, cur] : [...pre]
  }, [])
}
Array.prototype.selfFilter = selfFilter
let arr = [0, 2, 3]
let arr1 = arr.selfFilter(item => item > 2)
console.log(arr1)


重写some,利用for循环。


const someFilter = function (fn, context) {
  let arr = Array.prototype.slice.call(this)
  for (let i = 0; i < arr.length; i++) {
    if (fn.call(context, arr[i], i, this))
      return true
  }
  return false
}
Array.prototype.someFilter = someFilter
let arr = [1, 2, 3]
let arr1 = arr.someFilter(item => item > 2)
console.log(arr1)


重写flat方法,利用reduce。


const selfFlat = function (depth = 1) {
  let arr = Array.prototype.slice.call(this)
  // let resArr = []
  if (depth === 0) return arr
  return reduce((pre, cur) => {
    if (Array.isArray(cur)) {
      //反正就是递归
      return [...pre, ...selfFlat.call(cur, depth - 1)]
    } else {
      return [...pre, cur]
    }
  }, [])
}


es6的面向对象class语法


ES6 的 class 内部是基于寄生组合式继承


function inherit(subType, superType) {
  subType.prototype = Object.create(superType.prototype, {
    constructor: {
      value: subType,
      enumerable: false,
      configurable: true,
      writable: true
    }
  })
  // 让类的静态方法也可以被继承。
  Object.setPrototypeOf(subType, superType)
}


相关文章
|
7天前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
8天前
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
21 4
|
18天前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
|
18天前
|
移动开发 前端开发 JavaScript
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
于辰在大学期间带领团队参考网易游戏官网的部分游戏页面,开发了一系列前端实训作品。项目包括首页、2021校园招聘页面和明日之后游戏页面,涉及多种特效实现,如动态图片切换和人物聚合效果。作品源码已上传至CSDN,视频效果可在CSDN预览。
29 0
前端实训,刚入门,我用原生技术(H5、C3、JS、JQ)手写【网易游戏】页面特效
|
23天前
|
JavaScript 前端开发 开发者
前端框架对比:Vue.js与Angular的优劣分析与选择建议
【10月更文挑战第27天】在前端开发领域,Vue.js和Angular是两个备受瞩目的框架。本文对比了两者的优劣,Vue.js以轻量级和易上手著称,适合快速开发小型到中型项目;Angular则由Google支持,功能全面,适合大型企业级应用。选择时需考虑项目需求、团队熟悉度和长期维护等因素。
33 1
|
24天前
|
JavaScript 前端开发 API
前端框架对比:Vue.js与Angular的优劣分析与选择建议
【10月更文挑战第26天】前端技术的飞速发展让开发者在构建用户界面时有了更多选择。本文对比了Vue.js和Angular两大框架,介绍了它们的特点和优劣,并给出了在实际项目中如何选择的建议。Vue.js轻量级、易上手,适合小型项目;Angular结构化、功能强大,适合大型项目。
22 1
|
27天前
|
前端开发 JavaScript UED
"前端小技巧大揭秘:JS如何将后台时间戳秒变亲切小时前、分钟前,让用户秒懂,提升互动体验!"
【10月更文挑战第23天】在Web开发中,将后台返回的时间戳转换为“小时前”、“分钟前”、“刚刚”等友好的时间描述是常见需求。本文介绍如何用JavaScript实现这一功能,通过计算当前时间和时间戳的差值,返回相应的描述,提升用户体验。
29 1
|
1月前
|
前端开发 JavaScript 安全
JavaScript前端开发技术
JavaScript(简称JS)是一种广泛使用的脚本语言,特别在前端开发领域,它几乎成为了网页开发的标配。从简单的表单验证到复杂的单页应用(SPA),JavaScript都扮演着不可或缺的角色。
22 3
|
6月前
|
前端开发 JavaScript 网络协议
前端最常见的JS面试题大全
【4月更文挑战第3天】前端最常见的JS面试题大全
103 5
|
6月前
|
JavaScript 前端开发
web前端JS高阶面试题(1),高级开发工程师面试
web前端JS高阶面试题(1),高级开发工程师面试
下一篇
无影云桌面