写作意图
这篇文章用于总结一些 javascript 语言中常见的易混淆点
call | apply | bind
在 js 中, 最诡异莫测的莫过于 this 了, 理解的不够深入或是应用场景略微复杂, 使用时就会出现各种意想不到的错误所以, 在很多时候, 我们需要手动指定上下文环境, 来修正 this 的指向
最简单判断 this 所在环境的方法是, 寻找 this 的实际调用者
一个典型的错误
- var a = {
- name: 'ein',
- sayName: function () {console.log(this.name);
- }
- }
- var b = a.sayName;
- b(); //undefined
我们本想将对象 a 的 sayName 赋值给变量 b, 通过调用来查看 a 的 name 是什么? 结果却输出了 undefined 发生这个异常的原因就是因为在调用函数 b 时, sayName 中的 this 已经不再指向对象 a 而是指向了全局对象 window, 由于 window 下并没有 name 属性, 所以输出 undefined
下面我们将通过以上三种方法来修正这个问题
使用 call
- var b = a.sayName;
- b.call(a); //ein
使用 call 方法, 第一个参数为函数调用时的上下文环境, 将其设定为对象 a, 这样 this 就会指向对象 a, 如此便可以取到对象 a 中的 name 属性, 输出想要的值
使用 apply
- var b = a.sayName;
- b.apply(a); //ein
使用 apply 方法, 传入上下文环境, 可以实现同样的效果
call 和 apply 的区别
那么 call 和 apply 的区别是什么呢? 他们的区别在于后续的参数让我们改造一下上面的代码, 来观察效果
- var a = {
- name: 'ein',
- sayName: function (fistname, lastname) {
- console.log(`${fistname} ${this.name} ${lastname}`);
- }
- }
- var b = a.sayName;
- b.call(a,'nick','snow'); //nick ein snow
- b.apply(a,['nick','snow']); //nick ein snow
call 和 apply 的区别在于后续参数, call 依次传入后续参数, 将被函数所使用 apply 需要将后续参数以数组的形式传入
使用 bind
bind 同样可以用来修正 this 的指向, 它与以上二者的区别在于, bind 绑定之后并不会立即执行函数
- var b = a.sayName;
- b.bind(a);
在为 b 绑定 a 的上下文环境之后, b 并不会立即执行
- b.bind(a)(); //ein
- var c = b.bind(a);
- c(); //ein
如此, 便可以如愿得到你想要的效果了使用 bind 方法可以延缓函数的执行时间, 在你想调用时再执行函数
bind 方法同样可以传入其它参数, 和 call 方法的传入方式相同
splice | slice | split
看到这三个方法有没有头晕目眩的感觉? 因为它们三个实在是太像三胞胎了, 真的很难区分首先, 我们来学习或是回顾一下这三个单词
splice 拼接
slice 片, 切片
split 分裂, 裂开
大多数 api 其实其名称都与其用途有所关联, 这三个 api 便是很经典的案例
splice 意为拼接, 它的用途, 便是将一个数组分割开, 并且可以再以指定的方式重新拼接在一起
slice 意为切片, 我们可以使用它来在一个数组或是字符串中切取我们想要的一段
split 意为分裂, 它可以将一个字符串分裂成一个数组
使用 splice
- var a = [1,2,3,4,5];
- a.splice(1,2,4,4);
- console.log(a);
- //[1,4,4,4,5]
splice(starts, count, item1, ..., itemx) 方法接受多个参数, 第一个参数为删除的起始元素, 第二个参数为删除数量, 后续为插入的内容
注意: splice 方法会修改原始数组, 返回被删除的内容数组
使用 slice
- var a = [1,2,3,4,5];
- var b = '12345';
- a.slice(1,3); //[2,3]
- a.slice(1); //[2,3,4,5]
- a.slice(2,-1) //[3,4]
- b.slice(1,2); //'2'
- console.log(a); //[1,2,3,4,5]
- console.log(b); //'12345'
slice 方法既可应用于数组也可应用于字符串
slice(stats, beforeEnds) 方法接受最多两个参数, 第一个参数代表的序列号必须小于第二个参数, 第二个参数为切片的终止位置, 第二个参数省略时默认截取到数据末尾, 参数为负数时, 将反向查找匹配项
注意: slice 方法不会修改原始数组, 返回的是被切片节选的片段
使用 split
- var a = '123456';
- a.split(''); //['1','2','3','4','5','6']
- a.split('',3); //['1','2','3']
- console.log(a); //'123456'
split(separator, count) 方法可接受两个参数, 第一个参数为分割符, 用于指定字符串的分割规则, 第二个参数为返回数组的最大长度, 返回的输出长度不会大于这个参数
注意: split 不会修改原始字符串, 返回值为新数组
map | forEach | reduce | filter | every | some
这六个方法时常用的操作数组的 api, 均为 Array.prototype 的本地方法所以一切数组均可使用这些方法遍历操作数组的每一项, 下面将逐一介绍这些方法
- map
- var arr = [
- {'name': 'ein'},
- {'name': 'zwei'},
- {'name': 'drei'}
- ];
- let newarr = arr.map((item, index) => {
- return (
- {
- 'name': item.name,
- 'order': index
- }
- )
- });
- console.log(arr);
- // [{'name': 'ein'},{'name': 'zwei'},{'name': 'drei'}]
- console.log(newarr);
- // [{'name': 'ein','order': 0},{'name': 'zwei','order': 1},{'name': 'drei','order': 2}]
map(fn(item, index)) 方法接收一个函数作为参数, 这个函数接收两个参数, 第一个参数为每一项的数组内容, 第二个参数为数组下标
注意: map 方法返回一个新的数组, 如果在操作中没有 return 返回值, 默认返回一个值为 undefined 的数组
默认返回:[undefined, undefined, undefined]
- forEach
- arr.forEach((item, index) => {
- item.old = true;
- delete item.name;
- })
- console.log(arr);
- // [{'old': true},{'old': true},{'old': true}]
forEach(fn(item, index)) 方法接收一个函数作为参数, 这个函数接收两个参数, 第一个参数为每一项的数组内容, 第二个参数为数组下标
注意: forEach 方法直接操作原始数组, 并且不返回任何内容
reduce
简单用例
- var arr1 = [1,2,3,4,5];
- var res = arr1.reduce((curr, next) => {
- return curr + next
- });
- console.log(res); //15
- console.log(arr1); //[1, 2, 3, 4, 5]
复杂用例
- var res1 = arr1.reduce((curr, next,index,arr) => {
- console.log('content',curr,next,index,arr);
- return curr + next
- },10);
- console.log(res1);
content | 初始值(curr) | 当前元素(next) | 当前元素索引(index) | 当前元素所属数组(arr) | 函数初始值 |
---|---|---|---|---|---|
content | 10 | 1 | 0 | [1, 2, 3, 4, 5] | 10 |
content | 11 | 2 | 1 | [1, 2, 3, 4, 5] | 10 |
content | 13 | 3 | 2 | [1, 2, 3, 4, 5] | 10 |
content | 16 | 4 | 3 | [1, 2, 3, 4, 5] | 10 |
content | 20 | 5 | 4 | [1, 2, 3, 4, 5] | 10 |
reduce 方法用于对数组进行累积化操作, 常用于数组求和接收两个参数, 第一个参数为操作函数, 第二个参数为函数初始值对于数组的操作不会修改原始值
- filter
- var res = arr1.filter((item, index) => {
- console.log('data:',index,item);
- return item> 3
- });
- console.log(res);
- // [4, 5]
filter 方法用于过滤数组的每一项, 删选出符合条件的项, 并组成一个新的数组
- every
- var res = arr1.every((item, index) => {
- return item> 3
- });
- console.log(res);
- // false
every 方法用于检查数组的每一项是否符合条件, 全部符合条件时返回 true, 否则返回 false
- some
- var res = arr1.some((item, index) => {
- return item> 3
- });
- console.log(res);
- // true
some 方法用于检查数组中的是否存在符合条件的项, 存在则返回 true, 否则返回 false
另外
一副生动有趣的图解, everyday will be better!
来源: https://juejin.im/post/5abde3c05188255c56688bb6