前言:好记性不如烂笔头,知识来自于点点滴滴的记录

1.取出对象中一部分放到另一个对象中

今天下午在掘金摸鱼的时候,看见有一个小同学发了一个沸点

嗯嗯 ,日常要我写的话,以前可能我也会这么写,但是经过我最近一段时间的学习,我可能会用解构赋值来写

1
2
3
4
5
6
7
8
        const objOne = {
            name: 'xiaoMing',
            age: 18,
            id: 1234,
            gender: 1
        }
        const { name, id } = objOne;
        const objTwo = { name, id };

我可能会像上面这样写,这些些真的很优雅吗,我觉得跟上面那位小同学写的也没什么区别,还多一行代码,并不是一个好的方案,像我这么机灵的搜索引擎工程师,那么我就开始Google一下,于是找到一个讨论贴,针不戳,里面果然有几种比较秒的写法,显得有点高端。

(1)第一种

1
2
3
4
5
6
7
        const objOne = {
            name: 'xiaoMing',
            age: 18,
            id: 1234,
            gender: 1
        }
        const { age, gender, ...objTwo } = objOne;

这种也是利用的对象解构,把剩余的值赋给了objTwo,但是如果我原对象中有30个属性,我只需要取两个呢,那我岂不是得写很多。

(2)第二种

1
2
3
4
5
6
7
        const objOne = {
            name: 'xiaoMing',
            age: 18,
            id: 1234,
            gender: 1
        }
        const objTwo = (({ name, id }) => ({ name, id }))(objOne)

上面这种也是解构,不过这里用了一个立即执行函数,还是比较秒的,但是本质上还是解构的思想,跟上面的有同样的问题,假如我要取28个属性呢,那岂不是要写很多。

(3)第三种

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
        const objOne = {
            name: 'xiaoMing',
            age: 18,
            id: 1234,
            gender: 1
        }
        const pick = (obj, arr) => arr.reduce((iter, val) =>
            (val in obj && (iter[val] = obj[val]), iter), {});

        const objTwo = pick(objOne, ['name', 'id']);

这种刚看是不是还有点不明白,首先箭头函数箭头后面不写大括号{}的时候,会自动return,这是箭头函数带的特性,再就是 (val in obj && (iter[val] = obj[val]), iter) 这一句代码是不是看着有点不是很懂,前面倒是知道,就是判断,那这个逗号是什么意思呢,其实我刚看也没看懂,就直接翻了一下 逗号操作符–MDN

MDN 前端滴神 yyds

逗号操作符 对它的每个操作数求值(从左到右),并返回最后一个操作数的值。

也就是说上面这一行代码会返回每次的操作结果,然后用这个操作结果再进行下一次操作,上面的代码也就相当于:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
        const objOne = {
            name: 'xiaoMing',
            age: 18,
            id: 1234,
            gender: 1
        }
        const pick = (obj, arr) => arr.reduce((iter, val) => {
            if (val in obj && (iter[val] = obj[val])) {
                return iter
            }
        }, {});
        const objTwo = pick(objOne, ['name', 'id']);

但是上面用逗号写的确实会更简洁,对不懂逗号操作符的人来说可能不是很明了。

上面这三种写法相对而言比之前看着高大上一点,只是CodeReview的时候看着没那么呆🤗🤗,显得拉风一点,照日常来说,写出解构就行了,但是你要是会什么奇技淫巧也可以写出来,CodeReview同事看着这代码如果他不懂的话直呼大神,要是看懂了可能会发出一声惊叹,秒啊,也有可能是,就这?,花里胡哨,不如最开始的直接取过来赋值,简洁明了,立马看懂😏😏

2.判断一个变量同时等于多个数值

这是今天看B站上的一个UP的视频里面,UP提出来的,当然之前我也看到过,只是没有仔细的研究,只是今天看视频又看见了,想着连着上面那一题一起记录一下吧,毕竟好记性不如烂笔头嘛🤔🤔

1
2
3
        if (a == 1 && a == 2 && a == 3) {
            console.log('星空海绵');
        }

上面写一段是什么代码可以让这个判断成立,执行打印的代码,首先一看到这个题目,大家应该想到,想要这个判断成立,a肯定得是一个变化得值,每使用一次就加1,那么这个判断才有可能成立,好了,怎样让这个判断成立的思路已经有了,现在的问题就是怎么让a每使用一次就加1呢。 首先我们来看一下 == 操作符成立的条件:

  • 1.如果两个操作数都是对象,则仅当两个操作数都引用同一个对象时才返回true。
  • 2.如果一个操作数是null,另一个操作数是undefined,则返回true。
  • 3.如果两个操作数是不同类型的,就会尝试在比较之前将它们转换为相同类型:
    • 1.当数字与字符串进行比较时,会尝试将字符串转换为数字值。
    • 2.如果操作数之一是Boolean,则将布尔操作数转换为1或0。true转成1,false转成0。
    • 3.如果操作数之一是对象,另一个是数字或字符串,会尝试使用对象的valueOf()和toString()方法将对象转换为原始值。
  • 4.如果操作数具有相同的类型,则将它们进行如下比较:
    • 1.String:true仅当两个操作数具有相同顺序的相同字符时才返回。
    • 2.Number:true仅当两个操作数具有相同的值时才返回。+0并被-0视为相同的值。如果任一操作数为NaN,则返回false。
    • 3.Boolean:true仅当操作数为两个true或两个false时才返回true。

== 运算符与 === 运算符之间的区别: === 运算符不尝试类型转换。相反, === 运算符始终将不同类型的操作数视为不同。

(1)第一种解法

看到上面 == 运算符的相关成立条件后,我们再来分析上面这一题,当 a 是 number 类型时,只有每次访问后都加1上面的判断才会成立。那么我们看下上面的代码,这个a没用var或者let及const定义,那就是隐式变量,当然我们也可在上面定义a,就单纯这个式子而言,a是隐式变量,隐式变量再浏览器中是挂在全局对象window下面的,也就是说a是window对象下的一个属性,然后对象有一个 Object.defineProperty() 方法,这个方法能定义和修改一个对象上的属性,并且返回此对象。

1
2
3
4
5
6
7
8
9
        let _default = 0
        Object.defineProperty(window, 'a', {
            get() {
                return ++_default;
            }
        })
        if (a == 1 && a == 2 && a == 3) {
            console.log('星空海绵');
        }

此时通过上面这段代码我们发现这个判断成立,此时如果我们把 == 改成 === 成立吗,照样是成立的,因为此时a每次增加1并保持自己的类型为number,也就是说上面这段代码不仅适用于 == ,同时也适用于 === 。

(2)第二种解法

看到上面 == 运算符的介绍,我们发现 如果操作数之一是对象,另一个是数字或字符串,会尝试使用对象的valueOf()和toString()方法将对象转换为原始值。 有这么一个规则,也就是说假如现在上面的a是对象的话,会尝试使用valueOf方法和toSting方法,那么我们去修改这两个方法中的其中一个行吗

1
2
3
4
5
6
7
8
9
        let a = {
            _default: 0,
            valueOf: function () {
                return ++this._default;
            }
        }
        if (a == 1 && a == 2 && a == 3) {
            console.log('星空海绵');
        }
1
2
3
4
5
6
7
8
9
        let a = {
            _default: 0,
            toString: function () {
                return ++this._default;
            }
        }
        if (a == 1 && a == 2 && a == 3) {
            console.log('星空海绵');
        }

以上两种方法都可以,一个是重写a对象的valueOf方法,一个是重写对象的toString()方法。

3.深入类型转换

首先上题目

1
2
3
4
        console.log(({} + {}).length);
        console.log(([] + []).length);
        console.log(([] + {}).length);
        console.log((function () { }).length);

上面这4个console分别打印什么,首先第一个其实是两个空对象相加,那么空对象怎么相加呢,它们会自动转成字符串相加,也就是调用toString方法,也就是说{}.toSting(),那么这个结果是什么呢,这个结果是 [object object],此时你是不是想到了判断类型的 Object.prototype.toString.call() ,既然知道原理了,那第一个console的答案就是 “[object object]"+"[object object]",也就是30, 第二个可能不用说大家都知道是0,因为[]空数组转换成数值类型是0,根据上面两题我们知道这个答案是 0+"[object object]",也就是15,那么最后一题呢,我们不妨打印 console.dir(function () { }); 看一下,发现这个里面带了一个length属性,那么这个length属性是什么呢,其实是参数列表的个数,相当于 arguments.length,所以最后一个的结果也是0。

1
2
3
4
        console.log(({} + {}).length); // 30
        console.log(([] + []).length); // 0
        console.log(([] + {}).length); // 15
        console.log((function () { }).length); // 0

只是日常看到的,试着自己理解一下,我并不在乎这些题目是不是面试官为了装逼出出来难为面试者的,我只是看见了觉得好奇记录一下,请大佬们别打我!

完!