资讯专栏INFORMATION COLUMN

Javascript中的魔鬼

daydream / 1758人阅读

摘要:发生这个异常的原因就是因为在调用函数时,中的已经不再指向对象而是指向了全局对象,由于下并没有属性,所以输出。在为绑定的上下文环境之后,并不会立即执行。方法用于检查数组中的是否存在符合条件的项,存在则返回否则返回。

写作意图

这篇文章用于总结一些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,"nick","snow")();  //nick ein snow
    var c = b.bind(a);
    c("nick","snow");  //nick ein snow

如此,便可以如愿得到你想要的效果了。使用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://www.ucloud.cn/yun/93944.html

相关文章

  • eval()不是魔鬼,只是被误解了(翻译)

    摘要:因为道格拉斯的大多数作品并没有注明日期,所以,我不确定他是否是在年创造了这个术语。但这并不能说明是魔鬼,这只是开发工作流程中的一点问题。中间人攻击被认为是的永远存在的危险,会受到蠕虫的的攻击。 原文来自:https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/ 作者:Nicholas C.Z...

    elarity 评论0 收藏0
  • JS学习笔记 - eval() 是魔鬼

    摘要:要牢记使用这些构造函数来传递参数,在大部分情况下,会导致类似的隐患,因此应该也尽量避免使用这些函数。下面一个栗子使用构造函数和是比较类似的,因此该函数的使用也需要十分小心。 本文章记录本人在学习 JavaScript 中看书理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。 小白使用 eval() 如果在代码中使用了eval(),请记住一句话:eval()是一个...

    mengera88 评论0 收藏0
  • 前端性能优化(JavaScript篇)

    摘要:像也是类似的也不建议使用,会降低性能,通过包裹的代码块,作用域链将会额外增加一层,降低索引效率对象的优化缓存需要被使用的对象获取数据的性能有如下顺序从快到慢变量获取数组下标获取对象的整数索引获取对象属性获取对象非整数索引获取。 正巧看到在送书,于是乎找了找自己博客上记录过的一些东西来及其无耻的蹭书了~~~ 小广告:更多内容可以看我的博客 优化循环 如果现在有个一个data[]数组...

    KunMinX 评论0 收藏0
  • 前端性能优化(JavaScript篇)

    摘要:像也是类似的也不建议使用,会降低性能,通过包裹的代码块,作用域链将会额外增加一层,降低索引效率对象的优化缓存需要被使用的对象获取数据的性能有如下顺序从快到慢变量获取数组下标获取对象的整数索引获取对象属性获取对象非整数索引获取。 正巧看到在送书,于是乎找了找自己博客上记录过的一些东西来及其无耻的蹭书了~~~ 小广告:更多内容可以看我的博客 优化循环 如果现在有个一个data[]数组...

    kun_jian 评论0 收藏0
  • 重塑你的CSS世界观——浮动魔鬼float

    摘要:而在文档流中,如果浮动元素和跟随元素都是元素,它们两在默认情况下都将占据一行。而由于浮动元素脱离了文档流,如果父元素没有指定高度或者其他元素撑起,也就出现了所谓的浮动元素的父元素高度塌陷。 为什么要写《重塑你的CSS世界观》系列文章 由于从工作到现在,我的主要工作都是写JavaScript,几乎没怎么碰CSS,通常都是别人写好界面,然后我来开发JavaScript逻辑代码,这导致了严重...

    joyqi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<