0%

一、作用域(Scope)

作用域表示变量或函数起作用的区域,作用域决定了代码区块中变量和其他资源的可见性(可访问性)。

作用域也指变量的生命周期(一个变量在哪些范围内保持一定值)。

作用域是可访问变量的集合。

作用域最大的用处就是隔离变量。

二、作用域分类

作用域分为全局作用域函数作用域和ES6中的块级作用域

全局作用域:

在代码中任何地方都能访问到的对象拥有全局作用域。

  • 最外层函数 和在最外层函数外面定义的变量拥有全局作用域
  • 所有末定义直接赋值的变量自动声明为拥有全局作用域
  • 所有window对象的属性拥有全局作用域,如window.name、window.location等

全局作用域的弊端:全局变量过多的情况下会污染全局命名空间, 容易引起命名冲突。

函数作用域:

函数作用域是指声明在函数内部的变量,和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的如函数内部。

作用域是分层的,内层作用域可以访问外层作用域的变量,反之则不行。

1
2
3
4
5
6
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 200);
};
// 5 5 5 5 5

块级作用域:

使用let和const声明的变量在所在的代码块内(使用花括号{}包裹的区域)有效,即是块级作用域。

块级作用域内的变量不会提升到代码块顶部。let或const 声明的变量不会被提升到当前代码块的顶部,因此需要手动将 let或const 声明放置到顶部,以便让变量在整个代码块内部可用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for(let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 200);
};
// 0 1 2 3 4

for(var i = 0; i < 5; i++) {
const a = i
setTimeout(function() {
console.log(a);
}, 200);
};
// 0 1 2 3 4

三、作用域和执行上下文

JavaScript属于解释型语言。

JavaScript的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:

解释阶段:词法分析,语法分析,确定作用域

执行阶段:创建执行上下文,执行函数代码,垃圾回收

JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定。

执行上下文是函数执行之前创建的。执行上下文最明显的就是this的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。

作用域和执行上下文之间最大的区别是: 执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变

一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值

四、作用域链

执行环境:每个函数都有自己的执行环境。当执行流进入一个函数时(即调用该函数),函数的环境就会被推入一个环境栈中。而在函数执行之后,将其环境弹出栈,把控制权返回给之前的执行环境。

作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的第一个变量对象,始终都是当前执行的代码所在环境的变量对象。作用域链中的下一个变量对象来自包含(外部)环境,再下一个变量对象则来自下一个包含环境。这样,一致延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

即代码不仅仅可以访问当前的作用域的变量,对于嵌套的父级作用域中的变量也可以访问。

作用域链的作用主要用于查找标识符,当作用域需要查询变量的时候会沿着作用域链依次查找,如果找到标识符就会停止搜索,否则将会沿着作用域链依次向后查找,直到作用域链的结尾。

原型链是用于查找引用类型的属性,查找属性会沿着原型链依次进行,如果找到该属性会停止搜索并做相应的操作,否则将会沿着原型链依次查找直到结尾。

每天敲的代码都要提交到GitLab上,其中遇到过各种问题,这里简单做一下记录,后续会补充。

Clone repository:
1
git clone 项目链接
Git global setup:
1
2
git config --global user.name "于禤"
git config --global user.email "1924442613@qq.com"
Push an existing folder:
1
2
3
4
5
6
cd existing_floder
git init
git remote add origin 项目链接
git add .
git commit -m "inital commit"
git push -u origin master
git checkout :
1
2
3
4
git checkout -b newBranchName //在当前分支的基础上创建一个新的分支
git checkout dev //切换到dev分支

git checkout . //撤销本地所有的修改,恢复到未修改状态。
Edit commit & git reset:

​ git commit –amend //此时会进入默认vim编辑器,修改注释完毕后保存就好了。

​ git reset –soft HEAD^ //不删除工作空间改动代码,撤销commit,不撤销git add .

​ git reset –hard HEAD^ //删除工作空间改动代码,撤销commit,撤销git add .

​ git reset –mixed HEAD^ //不删除工作空间改动代码,撤销commit,并且撤销git add

​ git reset –soft commitid 后面添加comiit_id指明回退到哪个版本

​ git reset –mixed HEAD^ 和 git reset HEAD^ 效果是一样的

​ HEAD^的意思是上一个版本,也可以写成HEAD~1

​ 如果你进行了2次commit,想都撤回,可以使用HEAD~2

项目A的develop分支合并到项目B中:
1
2
3
4
5
6
7
//在项目B下添加远程项目A
git remote add upstream 项目A的链接

git fetch upstream

//将项目A的develop分支合并到项目B中
git merge --no-ff --log upstream/develop
项目A的某次提交合并到项目B中:
1
2
3
4
5
//在项目B下添加远程项目A
git remote add upstream 项目A的链接

git fetch upstream
git cherry-pick 09cc4ecb0c25a1968633b27d3ae60940768f117d(commit id)
合并分支:

(v0.18版本合并到develop开发分支):

首先切换到v0.18分支,拉下最新的代码
然后切换到develop分支,拉下最新的代码然后执行下面的步骤:

1
2
3
4
5
6
7
git merge --no-ff v0.18
(如果没有冲突则不用进行下面的操作)

解决冲突后执行以下命令:
git add .
git commit //进入另一个页面查看信息后退出按键组合是:Esc -> Shift -> : -> wq
git push origin develop
创建develop分支:
1
2
3
git fetch
git checkout -b develop
git push origin develop
解决提交代码冲突:

第一步:找到冲突的文件,合并冲突的内容。
第二步:重新提交一次 git add .
第三步:查看状态 git status
第四步:git rebase –continue
第五步:git push origin v0.2

解决pull代码时文件突然被删除的问题:

第一步:关闭正在运行的项目
第二步:终止pull命令, git rebase –abort
第三步:重新git pull –rebase origin develop
第四步:git push origin develop

git cherry-pick 的用法:

把0.12上的部分提交移到0.11上具体步骤:

  1. git checkout v0.11

  2. git pull –rebase origin v0.11

  3. git cherry-pick a6b90cab238fcd04590011f7385b40d1e2171f10 (提交的id)

  4. git status

    如果有冲突就解决冲突,解决冲突后执行以下步骤:

    1
    2
    3
    4
    git add . 
    git status
    git cherry-pick --continue
    git push origin v0.11

    如果没有冲突,则执行

    1
    git push origin v0.11 
    git revert 的用法:

    revert是撤回指定版本的内容并提交一个新的commit,不影响之前提交的内容

    git revert HEAD //撤销前一次 commit

    git revert HEAD^ //撤销前前一次 commit

    git revert commit-id //撤回指定commit-id

    git reset 和 git revert 的区别

    git reset 简单暴力的将版本置回到某个版本,现在有过a、b、c、d四次提交,提交顺序为a、b、c、d,现在为d。

    使用git reset恢复到a之后,看git log,就剩下a, 发现 b、c、d都不见了。

    使用git revert恢复到a之后,看git log,会发现a、b、c、d都在,多了e操作,e操作为“revert a”。

=+和+=的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
let a = 100;

a += 1;
console.log("a1=" + a); //a1 = 101

a -= 1;
console.log("a2=" + a); //a2 = 99

a = +1;
console.log("a3=" + a); //a3 = 1

a = -1;
console.log("a4=" + a); //a4 = -1
  • += 是简写,a += 1 就是 a = a+1
  • =+并不是简写,a =+ a 直接对 a 的赋值,± 符号代表的是正负(完全可以省略不写),即 a =+ b 其实就是 a = b

a++和++a的区别

1
2
3
let a= 1;
console.log(++a); //2,先执行a=a+1,再输出a
console.log(a++); //1,先输出a,在执行a=a+1

最近老大让我用Mobx编写供应商系统,写着代码看着文档很心累,现在写完了第一版本,mobx也算用的顺手了,今天整理了mobx的一些基础知识点,以供需要时瞄瞄!!!

MobX 是一个库,使得状态管理变得简单和可扩展。

MobX 支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图Action, State, View

状态改变时,所有衍生都会进行原子级的自动更新。因此永远不可能观察到中间值。

一般需要掌握以下四点就可以熟练的使用Mobx

  1. @observer

    函数/装饰器可以用来将 React 组件转变成响应式组件。

    它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。 observer 是由单独的 mobx-react 包提供的。

  2. @observable

    装饰器使状态变成可观察的

    @observable classProperty = value

  3. @computed

    计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值。

    如果你想响应式的产生一个可以被其它 observer 使用的值,请使用 @computed

    computed 还可以直接当做函数来调用。 在返回的对象上使用 .get() 来获取计算的当前值,或者使用 .observe(callback) 来观察值的改变。 这种形式的 computed 不常使用,但在某些情况下,你需要传递一个“在box中”的计算值时,它可能是有用的。

  4. @action

    动作是用来修改状态的,应该永远只对修改状态的函数使用动作。

    建议对任何修改 observables 或具有副作用的函数使用 @action 。 结合开发者工具的话,动作还能提供非常有用的调试信息。

    runInAction 是个简单的工具函数,它接收代码块并在(异步的)动作中执行。这对于即时创建和执行动作非常有用,例如在异步过程中。runInAction(f)action(f)() 的语法糖。

  5. 例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import { observable, computed, action } from "mobx";
    import { observer } from 'mobx-react'

    @observer
    class testComponent extends React.Component {
    @observable num1 = 0;
    @observable num2 = 0;

    @computed get total() {
    return this.num1 + this.num2;
    }

    @action
    onChangeNum1 = (event) => {
    this.num1 = event.target.value
    }

    @action
    onChangeNum2 = (event) => {
    this.num2 = event.target.value
    }

    render() {
    return(
    <div>
    Num1: <input type="text" onChange={this.onChangeNum1}/>
    NUm2: <input type="text" onChange={this.onChangeNum2}/>
    Show count: <span>{this.total}<span>
    </div>
    )
    }
    }

刚进公司时用的是Redux,最近老大让我看Mobx,准备以后的开发用Mobx替换Redux。今天整理了一下Redux和Mobx的一些区别。

Redux 是单一数据源,而MobX 是多个store (MobX 可以根据应用的UI、数据或业务逻辑来组织store).

Redux的state 是只读的,只能通过将之前的state 与触发的action 结合,产生新的state,因此是纯净的(pure)。

Mobx的state 即可读又可写,action 是非必须的,可以直接赋值改变,因此是不纯净的(Impure)

React 和 MobX 是一对强力组合。React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。而MobX提供机制来存储和更新应用状态供 React 使用。

那么具体到这两种模型,又有一些特定的优缺点呈现出来,先谈谈 各自的优势:

Redux:

  1. 数据流流动很自然,因为任何 dispatch 都会导致广播,需要依据对象引用是否变化来控制更新粒度。
  2. 如果充分利用时间回溯的特征,可以增强业务的可预测性与错误定位能力。
  3. 时间回溯代价很高,因为每次都要更新引用,除非增加代码复杂度,或使用 immutable。
  4. 时间回溯的另一个代价是 action 与 reducer 完全脱节,数据流过程需要自行脑补。原因是可回溯必然不能保证引用关系。
  5. 引入中间件,其实主要为了解决异步带来的副作用,业务逻辑或多或少参杂着 magic。
  6. 但是灵活利用中间件,可以通过约定完成许多复杂的工作。
  7. 对 typescript 支持困难。

Mobx:

  1. 数据流流动不自然,只有用到的数据才会引发绑定,局部精确更新,但免去了粒度控制烦恼。
  2. 没有时间回溯能力,因为数据只有一份引用。
  3. 自始至终一份引用,不需要 immutable,也没有复制对象的额开销。
  4. 没有这样的烦恼,数据流动由函数调用一气呵成,便于调试。
  5. 业务开发不是脑力活,而是体力活,少一些 magic,多一些效率。
  6. 由于没有 magic,所以没有中间件机制,没法通过 magic 加快工作效率(这里 magic 是指 action 分发到 reducer 的过程)。
  7. 完美支持 typescript。

难得今天上班不忙!!!重新整理了一下我对JS理解的几个困惑点,之前一直处于模糊的状态,似懂非懂!!!

同步和异步

同步:

1
2
console.log('我要做第一件事情');
console.log('我要做第二件事情');

这段代码的实现就叫做同步,也就是说按照顺序去做,做完第一件事情之后,再去做第二件事情

异步:

1
2
3
4
5
console.log('我要做第一件事情');
setTimeout(function () {
console.log('我突然有事,晚点再做第二件事情');
},1000)
console.log('我要做第三件事情');

这段代码的实现就叫做异步,也就是说不完全按照顺序去做,
突发情况,第二件事情不能立刻完成,所以等待一段时间再去完成,
优先去做后面的第三件事情,这样就不耽搁时间。

为什么需要异步呢?

JavaScript是单线程的,那么单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。所以这就是异步过程的由来。

我们经常用到的dom事件也是属于一个异步行为

例子:

1
2
3
4
var button = document.getElement('#btn');
button.addEventListener('click', function(e) {
console.log('按钮');
});

从事件的角度来看,上述代码表示:在按钮上添加了一个鼠标单击事件的事件监听器;当用户点击按钮时,鼠标单击事件触发,事件监听器函数被调用。

从异步过程的角度看,addEventListener函数就是异步过程的发起函数,事件监听函数就是异步过程的回调函数。事件触发时,表示异步任务完成,会将事件监听器函数封装成一条消息放到消息队列中,等待主线程执行。

回调函数

回调函数的理解:在JavaScript中function是内置的类对象。它可以存储在变量中,通过参数传递给另一个函数,在函数内部创建,从函数中返回结果值。

回调函数被认为是一种高级函数,一种被作为参数传递给另一个函数(在这称作”otherFunction”)的高级函数,回调函数会在otherFunction内被调用(或执行)。回调函数的本质是一种模式(一种解决常见问题的模式),因此回调函数也被称为回调模式。

回调函数是如何实现的?

我们可以像使用变量一样使用函数,作为另一个函数的参数,在另一个函数中作为返回结果,在另一个函数中调用它。当我们作为参数传递一个回调函数给另一个函数时,我们只传递了这个函数的定义,并没有在参数中执行它。

当包含(调用)函数拥有了在参数中定义的回调函数后,它可以在任何时候调用(也就是回调)它。

这说明回调函数并不是立即执行,而是在包含函数的函数体内指定的位置“回调”它(形如其名)。

1
2
3
4
5
6
7
8
function f1(callback){
    setTimeout(function () {
      // f1的任务代码
      callback();
    }, 1000);
  }

f1(f2);

promise 对象

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大 。 ES6 规定,**Promise对象**是一个构造函数,用来生成Promise实例。

1
2
3
4
5
6
7
8
9
const promise = new Promise(function(resolve, reject) {
// ... some code

if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。 它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

1
2
3
4
5
promise.then(function(value) {
// success
}, function(error) {
// failure
});

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise规范如下:

  • 一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)

  • 一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换

  • promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致

  • then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。

promise 的缺点:

  • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

进程和线程

JavaScript语言的执行环境是“单线程”。

“进程”, 指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。

”线程“,1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)。线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。

“单线程”,是一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,在执行后面一个任务。

“多线程”, 指1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务

(Library)和框架(Framework)的区别:

库是将代码集合成的一个产品,供程序员调用。面向对象的代码组织形式而成的库也叫类库。面向过程的代码组织形式而成的库也叫函数库。在函数库中的可直接使用的函数叫库函数。开发者在使用库的时候,只需要使用库的一部分类或函数,然后继续实现自己的功能。

框架则是为解决一类问题而开发的产品,框架用户一般只需要使用框架提供的类或函数,即可实现全部功能。可以说,框架是库的升级版。开发者在使用框架的时候,必须使用这个框架的全部代码。

第一次接触ref是看到同事写的代码中用了ref,感觉好神奇,竟然可以获取到input的输入值,后来因为有次需要获取子组件中的数据,找来老大帮忙,他也用了ref,这次是用在组件上。今天整理了一些关于ref的内容,写了这篇博客。

​ 在react典型的数据流(自顶向下传递)中props传递是父子组件交互的唯一方式;通过传递一个新的props值来使子组件重新re-render,从而达到父子组件通信。在react典型的数据量之外,某些情况下(和第三方的dom库整合,或者某个dom元素focus等)为了修改子组件我们可能需要另一种方式,这就是ref方式。

​ 在React中,数据是自顶向下流动的(称为单项数据流),从父组件传递到子组件。因此组件是简单且易于把握的,它们只需从父节点获取props渲染即可。如果顶层组件的某个prop改变了,React会递归向下遍历整个组件树,从新渲染所有使用这个属性的组件。如果想要修改组件内部的状态使用state

通过ref可以拿到DOM元素的值,并且能够对DOM元素进行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class InputComponent extends React.Component {
componentDidMount() {
this.textInput.focus() //加载页面时对input输入框focus
}

getInputValue = () => {
console.log(this.textInput.value) //点击按钮输出input的value值
}

render() {
return (
<div>
<input type="text" ref={input => (this.textInput = input)} />
<button onClick={this.getInputValue}>Get Input Value</button>
</div>
)
}
}

父组件通过ref可以拿到子组件的state值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class InputBoxComponent extends React.Component {
onClick = () => {
const inputValue = this.textInputBox.state.inputValue
console.log(inputValue) //输出iput输入框的值
}

render() {
return (
<div>
<InputComponent ref={input => (this.textInputBox = input)} />
<button onClick={this.onClick}>Get Input Value</button>
</div>
)
}
}

class InputComponent extends React.Component {
state = {
inputValue: ''
}

onChangeInputValue = event => {
this.setState({ ...this.state, inputValue: event.target.value })
}

render() {
return <input type="text" onChange={this.onChangeInputValue} />
}
}

ref提供了一种对于react标准的数据流不太适用的情况下组件间交互的方式,例如管理dom元素focus、text selection以及与第三方的dom库整合等等。 但是在大多数情况下应该使用react响应数据流那种方式,不要过度使用ref。

​ 在使用ref时,不用担心会导致内存泄露的问题,react会自动帮你管理好,在组件卸载时ref值也会被销毁。

当react原生动态添加多个className时就会报错,这时我们就可以利用classnames库添加多个className

安装: npm install classnames

功能: 简单来说就是将true的class 显示出来,false 的class 隐藏

例:

import classnames from 'classnames'

<div className=classnames({
    'class1': true,
    'class2': true
    )>
</div>    

用法:

classNames('class1', 'class2'); // => 'class1 class2'
classNames('class1', { class2: true }); // => 'class1 class2'
classNames({ 'class1-class2': true }); // => 'class1-class2'
classNames({ 'class1-class2': false }); // => ' '
classNames({ class1: true }, { class2: true }); // => 'class1 class2'
classNames({ class1: true, class2: true }); // => 'class1 class2

传入数组对象

var arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'

传入动态class

let buttonType = 'primary';
classNames({ [`btn-${buttonType}`]: true })

1. Array.prototype.find()

find()是用于查找数组中值的方法,该方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined

1
2
var findElement =  [12, 5, 22, 13, 52].find(element => element > 12); 
//findElement: 22

其中findIndex() 方法,它返回数组中找到的元素的索引,而不是其值。如果需要找到一个元素的位置或者一个元素是否存在于数组中,使用Array.prototype.indexOf()Array.prototype.includes()

2. 语法

1
arr.find(callback[, thisArg])

参数

callback

在数组每一项上执行的函数,接收 3 个参数:

element

当前遍历到的元素。

index

当前遍历到的索引。

array

数组本身。

thisArg 可选

可选,指定 callbackthis 参数。

返回值

当某个元素通过 callback 的测试时,返回数组中的一个值,否则返回 undefined

3. 描述

find 方法对数组中的每一项元素执行一次 callback 函数,直至有一个 callback 返回 true。当找到了这样一个元素后,该方法会立即返回这个元素的值,否则返回 undefined。注意 callback 函数会为数组中的每个索引调用即从 0 到 lengh - 1,而不仅仅是那些被赋值的索引,这意味着对于稀疏数组来说,该方法的效率要低于那些只遍历有值的索引的方法。

callback 函数带有3个参数:当前元素的值、当前元素的索引,以及数组本身。

如果提供了 thisArg 参数,那么它将作为每次 callback 函数执行时的上下文对象,否则上下文对象为 undefined

find 方法不会改变数组。

在第一次调用 callback 函数时会确定元素的索引范围,因此在 find 方法开始执行之后添加到数组的新元素将不会被callback 函数访问到。如果数组中一个尚未被callback函数访问到的元素的值被callback函数所改变,那么当callback函数访问到它时,它的值是将是根据它在数组中的索引所访问到的当前值。被删除的元素仍旧会被访问到。

1. Array.prototype.filter()

filter() 是用于筛选的方法,该方法用于创建一个符合条件的新数组,新数组中包含通过函数测试的所有元素。

1
2
var filteredArr = [1, 22, 6, 11, 34, 2, 9].filter(element => element > 9);
//filteredArr: [22, 11, 34]

2. 语法

1
var new_array = arr.filter(callback[, thisArg]);
callback

用来测试数组的每个元素的函数。调用时使用参数 (element, index, array)。返回true表示保留该元素(通过测试),false则不保留。

thisArg

可选。执行 callback 时的用于 this 的值。

返回值

一个新的通过测试的元素的集合的数组

3. 描述

filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或 等价于 true 的值 的元素创建一个新数组。callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中。

callback 被调用时传入三个参数:

  • 元素的值
  • 元素的索引
  • 被遍历的数组

如果为filter 提供一个 thisArg 参数,则它会被作为 callback 被调用时的 this 值。否则,callback 的 this 值在非严格模式下将是全局对象,严格模式下为 undefined。
The thisvalue ultimately observable by callback is determined according to the usual rules for determining thethis seen by a function.

filter 不会改变原数组。

filter 遍历的元素范围在第一次调用 callback 之前就已经确定了。在调用 filter 之后被添加到数组中的元素不会被 filter 遍历到。如果已经存在的元素被改变了,则他们传入 callback 的值是 filter 遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历到。