1

本文主要记录平时开发遇到的知识点和小技巧


相等判断(==)

类型相同: 判断其值是否相同

类型不同:

 1. 如果数字和字符串比较, 则字符串会被隐式转换为数字,在做判断。
 2. 如果有一方是布尔值, 则true 转换为 1, false 转换为 0 ,再进行判断。
 3. 如果其中有一个值为对象, 则对象会调取自身的valueOf 或者toString方法进行转换,再做判断。
 4.undefined 与 null 相等。

 

等同运算符(===)

类型不同: 返回false
类型相同: 

    1. 如果同为数字/字符串, 则比较值
    2. 如果同为布尔值, 相同则为true, 不同为false
    3.  如果两个操作数同为引用类型,且引用的为同一个对象(函数,数组),则相同。

所以使用 === 进行逻辑判断的时候,自己就要很清楚两边的数据类型。 比如调用函数得到的是字符串'1', 与 数字 1 比较的时候,得到的false, 不要犯类似的低级错误。

this指针

javascript中, this表示当前上下文, 即调用者的引用。

        var tom = {
            sex: 'M',
            age: 20
        }


        var jerry = {
            sex: 'F',
            age: 18
        }

        function getAge() {
            return this.age;
        }

        console.log(getAge.call(tom)); // 20
        console.log(getAge.call(jerry)); // 18

        // 通过call 方法, 改变了getAge函数中this的指向, this不会指向getAge函数本身。
        


        var person = {
            first: 'john',
            last: 'tom',
            getFull: function () {
                console.log(this.first + ' ' + this.last);
            }
        }

        person.getFull(); // john tom 
        //this 指向person


        var firstName = 'will';
        var lastName = 'smith';

        function getFull() {
            console.log(this.firstName + ' ' + this.lastName);
        }

        getFull(); // will smith
        //调用者是window,所以 this 指向window。

        function a() {
            console.log(this);
        }
        a.call(null);

call 调用一个对象的一个方法,以另一个对象替换当前对象。

格式如 call(thisObj, arg1,arg2...argN);

在函数体外部调用call()方法,如果传入null,则默认转成window,如果不传也是一样,即函数中的this指向window。
console.log(this) // window;

        function a() {
            console.log(this === window);
        }

        console.log(this === window); // true

        a.call(); // true
        a.call(null); // true
        a.call(this); // true
        a.call(window); // true
        a(); // true

this 的值并非取决于如何被定义, 而是取决于调用方式
更多有关函数调用的内容请翻阅javascript语言精粹第四章的函数部分。
更多有关this的内容可以翻阅《你不知道的javascript上卷》第二章。

变量声明与函数声明提升

JavaScript会将所有变量和函数声明移动到它的作用域的最前面,这就是所谓的变量提升(Hoisting)。

也就是说,无论你在什么地方声明变量和函数,解释器都会将它们移动到作用域的最前面。因此我们可以先使用变量和函数,而后声明它们.

但是,仅仅是变量声明被提升了,而变量赋值不会被提升。

如果你不明白这一点,有时则会出错:


console.log(a);  // 输出undefined
a = 2; // 初始化y

// 上面的代码等同于
var a;  // 声明y
console.log(a);  // 输出undefined
a = 2; // 初始化y

再看一个:

var a;

console.log(a);

a = 1;

function a() {
    // xxx
}

输出:
/*

function a() {
    // xxx
}
1
*/

javascript永远是先解析声明函数,再解析变量。

执行顺序如下:
(1) 解析函数a;
(2) 声明变量var a; 因为a此时并没有被赋值,所以它为 undefined, 还是指向原来的值,即函数 function a;
(3) console.log(a); // function a
(4) a = 1; // 重新赋值, 输出1

函数重载

javascript中是没有函数重载的,但是javascript的函数没有限制传入的参数个数必须与函数接收参数的个数相同,所以我们可以利用这一特性来模拟函数重载。

举个栗子:

        function add() {
            if (arguments.length < 2) {
                return arguments[0];
            } else {
                var _args = [].slice.call(arguments);
                return _args.reduce(function (a, b) {
                    return a + b;
                })
            }
        }

        add(1); // 1
        add(1, 2, 3, 4); // 10

举个计算日期的栗子:

            // ..
        getFutureDate: function(startDate, afterYear, afterMonth, afterDay) {
            var futureDate, year, month, day;

            if (arguments.length === 3) {
                afterDay = arguments[2];
                afterMonth = arguments[1];
                afterYear = arguments[0];
                startDate = new Date(startDate);
            }

            if (arguments.length === 4 && Object.prototype.toString.call(startDate) !== "[object Date]") {
                startDate = new Date(startDate);
        getFutureDate: function (startDate, afterYear, afterMonth, afterDay) {
            var futureDate, year, month, day;

            if (arguments.length === 3) {
                afterDay = arguments[2];
                afterMonth = arguments[1];
                afterYear = arguments[0];
                startDate = new Date(startDate);
            }

            if (arguments.length === 4 && Object.prototype.toString.call(startDate) !== "[object Date]") {
                startDate = new Date(startDate);
            }

            //计算年
            futureDate = startDate.setFullYear(startDate.getFullYear() + parseInt(afterYear));
            futureDate = new Date(futureDate);
            // 计算月
            futureDate = futureDate.setMonth(futureDate.getMonth() + parseInt(afterMonth));
            futureDate = new Date(futureDate);
            // 计算日
            futureDate = futureDate.setDate(futureDate.getDate() + parseInt(afterDay));
            futureDate = (new Date(futureDate));

            year = futureDate.getFullYear();

            month = futureDate.getMonth() + 1;
            month = month < 10 ? '0' + month : month;

            day = futureDate.getDate();
            day = day < 10 ? '0' + day : day;

            futureDate = [year, month, day].join('-');

            return futureDate
        },
         initDateTime: function () {
            // ...
            var endTime = _that.getFutureDate(new Date(today.replace(/-/g, "/")).getTime(), 0, maxInsuranceMonth, maxInsuranceDay);   
            // ...
        }
        // ...

Map 函数

    var ary = [1, 2, 3, 4, 5];

    var res = ary.map(function (item, index, input) {
        return item * 10;
    });

    console.log(res); // [10, 20, 30, 40, 50]
    console.log(ary); // [1, 2, 3, 4, 5]

map 函数的实现:

Array.prototype.map = function (func /*, obj */) {
    var len = this.length;
    //check the argument
    if (typeof func != "function") {
        throw new Error("argument should be a function!");
    }
    var res = [];
    var obj = arguments[1];
    for (var i = 0; i < len; i++) {
        //func.call(), apply the func to this[i]
        res[i] = func.call(obj, this[i], i, this);
    }
    return res;
}

map:和forEach非常相似,都是用来遍历数组中的每一项值的,用来遍历数组中的每一项;
区别:map的回调函数中支持return返回值;return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆一份,把克隆的这一份的数组中的对应项改变了);

前面已经说过,this会指向调用者,所以this是指向需要用到map函数的数组的。
需要注意的是,map函数是接收2个参数的,第二个参数是第一个参数的函数this指向。

柯里化

柯里化就是预先将函数的某些参数传入,得到一个简单的函数,但是预先传入的参数被保存在闭包中,因此会有一些奇特的特性。


var adder = function(num) {
    return function(y) {
        return num + y;
    }
}
console.log(adder(1)(100)); // 101
console.log(adder(2)(100)); // 102

更多内容请翻阅上一篇介绍《邂逅函数柯里化》

递归

递归在编程中会经常使用,在某些时候,递归可以给我们减少很多代码冗余。
比如我们的求阶乘函数:

function factorial(n) {
    if (n == 1) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

函数不停的调用自身,来达到不停的向下求值相乘,从而实现阶乘求值。代码逻辑也一目了然。


客户端判断

var UA = (function (userAgent) {
    var ISOldIOS     = /OS (\d)_.* like Mac OS X/g.exec(userAgent),
        isOldAndroid = /Android (\d.*?);/g.exec(userAgent) || /Android\/(\d.*?) /g.exec(userAgent);

    // 判断设备是否是IOS7以下
    // 判断设备是否是android4.5以下
    // 判断是否iOS
    // 判断是否android
    // 判断是否QQ浏览器
    return {
        oldIOS    : ISOldIOS ? +ISOldIOS.pop() < 8 : false,
        oldAndroid: isOldAndroid ? +isOldAndroid.pop().substr(0, 3) < 4.5 : false,
        iOS       : /\(i[^;]+;( U;)? CPU.+Mac OS X/.test(userAgent),
        android   : /Android/g.test(userAgent),
        mQQBrowser: /MQQBrowser/g.test(userAgent)
    }
})(navigator.userAgent);

皮小蛋
8k 声望12.8k 粉丝

积跬步,至千里。