前端知识总结系列第02篇

Posted 狼丶宇先森

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端知识总结系列第02篇相关的知识,希望对你有一定的参考价值。

一、写在前面

本期系列是javascript的一些知识点总结,这是笔者对自己多年以来学习和使用的一些技术的巩固与复习。

已经很久没有这样从头到尾的学习过了,这系列文章也是作为一个复习的过程的一些记录,若作者本人对此有理解错误的地方也请各位朋友帮忙指出,让我们一起来复习和巩固一些曾经的一些知识吧!

学而不思则罔,思而不学则殆。

下面正式开始,每篇文章10个知识点。

二、内容部分


1、typeof NaN的结果

NaN意指“不是一个数字”(not a number),NaN是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。

typeof NaN; //"number" 

NaN是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即 x === x 不成立)的值。

而NaN != NaN为true。

2、isNaN 和 Number.isNaN 函数的区别

函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,这种方法对于 NaN 的判断更为准确。

3、Array 构造函数只有一个参数值时的表现

Array 构造函数只带一个数字参数的时候,该参数会被作为数组的预设长度(length),而非只充当数组中的一个元素。

这样创建出来的只是一个空数组,只不过它的 length 属性被设置成了指定的值。构造函数 Array(…) 不要求必须带 new 关键字。不带时,它会被自动补上。

4、函数柯里化

柯里化(currying)又称部分求值。一个柯里化的函数首先会接收一些参数,接收了这些参数后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

/*
 * 1.可以接收多个参数,反复被调用
 * 2.第一次执行及后续执行返回函数
 * 3.参数可以被缓存
 */
function currying () 
    const args = Array.prototype.slice.call(arguments);
    //闭包缓存
    const inner = function inner () 
        args.push(...arguments);
        return inner;
    
    //当对象转为一个初始值的时候会被自动调用
    inner.__proto__[Symbol.toPrimitive] = inner.toString = function () 
        return args.reduce((a, b) => 
            return a + b;
        , 0);
    
    return inner;


const sum = currying(1, 2, 3, 4)(6, 7)(8, 9)(10, 11);

console.log(+sum);

5、斐波那契数列

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。

表示的公式为:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。

在js中表示的方法一般有:

①普通递归,优点就是简单明了,缺点是会重复计算,导致时间复杂度直线上升。


/**
 * 普通递归
 * @param * n 
 * @returns 
 */
function fibonacii (n) 
    if (n == 1 || n == 2) return 1;
    return fibonacii(n - 1) + fibonacii(n - 2);


console.time('普通递归');
console.log(fibonacii(total));
console.timeEnd('普通递归');

②参数缓存模式,时间复杂度相比普通递归有了质的提示。


/**
 * 参数模式
 * @param * n 
 */
function fibonacii2 (n) 
    function fib (n, v1, v2) 
        if (n == 1) return v1;
        if (n == 2) return v2;
        return fib(n - 1, v2, v1 + v2);
    
    return fib(n, 1, 1)


console.time('参数模式');
console.log(fibonacii2(total));
console.timeEnd('参数模式');

③for循环模式,相对于上面两种时间是最快的


/**
 * for循环模式
 * @param * n 
 * @returns 
 */
function fibonaccifor (n) 
    var n1 = 1, n2 = 1, sum = 0;
    for (let i = 2; i < n; i++) 
        sum = n1 + n2;
        n1 = n2;
        n2 = sum;
    
    return sum;


console.time('for循环模式');
console.log(fibonaccifor(total));
console.timeEnd('for循环模式');

④for循环+ES6数组解构模式,相对for循环要慢一点


/**
 * for循环+ES6数组解构模式
 * @param * n 
 * @returns 
 */
function fibonacciforarr (n) 
    let n1 = 1; n2 = 1;
    for (let i = 2; i < n; i++) 
        [n1, n2] = [n2, n1 + n2]
    
    return n2


console.time('for循环Array模式');
console.log(fibonacciforarr(total));
console.timeEnd('for循环Array模式');

6、递归快排

/**
 * 递归快排
 * @param * list 
 */
function quickSort (list) 
    if (!Array.isArray(list)) throw new Error('list 必须是一个数组');
    //找到一个标杆
    //大于标杆的放右边,小于标杆的放左边
    //重复上面两步

    //克隆一份数组
    let array = list.slice();
    let len = array.length;
    if (len <= 1) return array;
    let pivotIndex = Math.floor(len / 2);
    let pivot = array.splice(pivotIndex, 1)[0];
    let left = [], right = [];
    for (let i = 0; i < array.length; i++) 
        if (array[i] < pivot) 
            left.push(array[i]);
         else 
            right.push(array[i]);
        
    
    return quickSort(left).concat([pivot], quickSort(right));


console.time('quickSort');
console.log('原数组', list);
console.log('排序数组', quickSort(list));
console.timeEnd('quickSort');
7、手写实现bind、apply和call


Function.prototype._bind = function (context) 
    if (typeof this !== 'function') throw new Error(`$this 必须是一个函数`);
    //获取this 及参数
    const _this = this;
    const ctx = context || window;
    //获取传入参数
    const args = Array.prototype.slice.call(arguments, 1);
    //创建返回方法
    const fn = function () 
        //获取绑定参数
        const bindArgs = Array.prototype.slice.call(arguments);
        _this.apply(this instanceof fn ? this : ctx, args.concat(bindArgs));
    
    //考虑new操作符
    fn.prototype = Object.create(this.prototype);
    fn.prototype.custructor = _this;
    //返回函数
    return fn;


Function.prototype._apply = function (context) 
    if (typeof this !== 'function') throw new Error(`$this 必须是一个函数`);
    if (!Array.isArray(arguments[1])) throw new Error(`第二个参数必须是一个数组`);
    const ctx = context || window;
    const args = Array.prototype.slice.call(arguments[1], 1);
    const fn = Symbol();
    ctx[fn] = this;
    ctx[fn](...args);
    //移除属性
    delete ctx[fn];



Function.prototype._call = function (context) 
    if (typeof this !== 'function') throw new Error(`$this 必须是一个函数`);
    const ctx = context || window;
    const args = Array.prototype.slice.call(arguments, 1);
    const fn = Symbol();
    ctx[fn] = this;
    ctx[fn](...args);
    //移除属性
    delete ctx[fn];


8、手写实现new的过程

const _new = function _new (Fn, ...args) 
    if (!(Fn instanceof Function)) throw new Error('Fn 必须是函数');
    //从Object创建对象原型
    const obj = Object.create(Fn.prototype);
    //绑定this指向
    const result = Fn.apply(obj, args);
    //返回新对象
    return result instanceof Object ? result : obj;



function Persion (name, age) 
    this.name = name;
    this.age = age;



const persion = _new(Persion, '张二', 21);
console.log(persion);


9、手写实现防抖和节流

const debounce = (fn, delay) => 
    let timer = null;
    return function () 
        clearTimeout(timer);
        timer = setTimeout(() => 
            fn.call(this, arguments);
        , delay);
    


const throttle = (fn, delay) => 
    let last = 0;
    return function () 
        let now = Date.now();
        if (now - last >= delay) 
            last = now;
            fn.call(this, arguments);
        
    


10、什么情况下会发生布尔值的隐式强制类型转换?

(1) if (…) 语句中的条件判断表达式。

(2) for ( … ; … ; … ) 语句中的条件判断表达式(第二个)。

(3) while (…) 和 do…while(…) 循环中的条件判断表达式。

(4) ? : 中的条件判断表达式。

(5) 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)。


三、写在后面

许多知识久了没看还是可能会忘记的,抽一点时间出来,偶尔复习一下。让我们一起进步,加油~

上面就是本期的全部内容了,你学废了吗?

有问题请留言或者@博主,谢谢支持o( ̄︶ ̄)o~

感谢您的阅读,如果此文章或项目对您有帮助,请扫个二维码点个关注吧,若可以的话再给个一键三连吧!

公众号阅读的朋友可以点一下右下角的在看分享哦。

更多信息请关注公众号: “笔优站长”

扫码关注“笔优站长”,支持站长

以上是关于前端知识总结系列第02篇的主要内容,如果未能解决你的问题,请参考以下文章

前端知识总结系列第02篇

前端知识总结系列第02篇

前端知识总结系列第03篇

前端知识总结系列第03篇

前端知识总结系列第03篇

前端知识总结系列第03篇