前端知识总结系列第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篇的主要内容,如果未能解决你的问题,请参考以下文章