-
Notifications
You must be signed in to change notification settings - Fork 456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【面试篇】寒冬求职季之你必须要懂的原生JS(上) #7
Comments
mark 第五点arguments拼错哒 |
哈哈哈 |
迷弟报道~ |
原始类型里又新加了 BigInt |
感谢,O(∩_∩)O哈哈~ |
赞赞赞,学习了~ |
深拷贝那里不能用 |
上面实现Promise.all的函数中在onRejected回调函数里写的return真的有用吗?并不能阻止其他promise继续执行吧?这个return相当于return undefined给一下一个.then的onFullfilled回调了 |
我觉得拷贝上也没什么问题?如果使用Object.keys会增加算法使用的空间. |
Promise.all的return意思是返回一个promise对象,return不能阻止其他的promise执行~~~ |
可能需要看下深拷贝是否应该拷贝原型链上的方法~ |
是否拷贝原型链的属性可以作为配置项吧。不过从直觉上来说更倾向于不拷贝原型链,如果需要则再去做一次原型链的深拷贝。 |
嗯嗯,不知道深拷贝的定义中有没有涉及到这个~ |
// ES5
var curry = function curry (fn, arr) {
arr = arr || []
return function () {
var args = [].slice.call(arguments)
var arg = arr.concat(args)
return arg.length >= fn.length
? fn.apply(null, arg)
: curry(fn, arg)
}
}
// ES6
const curry = (fn, arr = []) => (...args) => (
arg => arg.length >= fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args]) |
非常感谢~ |
@YvetteLau 我看错了...没啥问题 😄 |
O(∩_∩)O哈哈~ |
作者,我还想问一个问题。关于Promise的缺点,我们应该如何捕捉异常呢,如果在最后一个.catch中也发生异常我们怎么办,没有东西可以捕获这个异常了?直接等待浏览器自动进行垃圾回收吗? |
关于这个Promise.prototype.finally, 我想问问为什么需要对callback的结果进行resolve呢,反正最后返回给下一个promise的结果是finally之前promise决议的结果,所以可不可以直接这样,如下
|
需要用Promise.resolve,因为callback返回的可能是一个Promise对象~ let p2 = new Promise((resolve, reject) => { |
通常情况下,我们不会在最后一个catch中做一些操作,一般只会打印错误日志。 |
对对对对,我也是刚刚反映过来了,传给finally的回调函数也可以是异步的。谢谢 |
感谢作者的回答 |
题目里面new的实现貌似不能给fn传参数,如果调用fn返回的结果是null也会被忽略的 function _new(fn){
return function(){
let target = Object.create(fn.prototype);
let ans = fn.apply(target, arguments);
let ansType = typeof ans;
if((ansType === 'object' && ans !== null) || ansType === 'function' ){
return ans;
}
return target;
}
} |
对象深拷贝里面下面两行代码是不是可以不需要呢,因为后面var obj = new obj.constructor()以及后面的拷贝操作可以涵盖 if(obj instanceof RegExp) return new RegExp(obj);
if(obj instanceof Date) return new Date(obj); |
关于用es5实现继承下面这句代码 是不是应该换成SubType.prototype = Object.create(SuperType.prototype)呢,如果采用下面的方式会在原型对象上加上colorts和name两个属性,而这两个属性不应该出现在原型对象上(当然根据业务需求来决定),虽然在在SubType这个构造器里面使用了SuperType.call(this, arguments)来给实例对象创建这两个属性会覆盖原型对象上的这两个属性。但是始终觉得原型对象上的这两个属性是杂物,应该去掉。另外,SuperType构造器里面忘记声明name这个形参了。😝 SubType.prototype = new SuperType(); |
Promise.all的实现里面那个setTimeout貌似没什么用?好像并不需要这个setTimeout. |
不错 |
forEach/map可以用hack方法中断的, 中途手动抛出错误. |
[...arrayLike]; 不能把类数组对象转换成数组哟,会报错! var a = {0: 2, 1: 3, length: 2}; [...a] //object is not iterable |
instanceof 是通过原型链判断的,A instanceof B, 在A的原型链中层层查找,是否有原型等于B.prototype,如果一直找到A的原型链的顶端(null;即Object.prototype.proto),仍然不等于B.prototype,那么返回false,否则返回true. 这块描述是不正确的,A instanceof B, 在A的原型链中层层查找,是否有原型等于B.prototype,不一定要等于B.prototype而是一直往上找他两能找到同一个引用即为同一个对象返回true否则返回false |
互联网寒冬之际,各大公司都缩减了HC,甚至是采取了“裁员”措施,在这样的大环境之下,想要获得一份更好的工作,必然需要付出更多的努力。
一年前,也许你搞清楚闭包,this,原型链,就能获得认可。但是现在,很显然是不行了。本文梳理出了一些面试中有一定难度的高频原生JS问题,部分知识点可能你之前从未关注过,或者看到了,却没有仔细研究,但是它们却非常重要。本文将以真实的面试题的形式来呈现知识点,大家在阅读时,建议不要先看我的答案,而是自己先思考一番。尽管,本文所有的答案,都是我在翻阅各种资料,思考并验证之后,才给出的(绝非复制粘贴而来)。但因水平有限,本人的答案未必是最优的,如果您有更好的答案,欢迎给我留言。
本文篇幅较长,但是满满的都是干货!并且还埋伏了可爱的表情包,希望小伙伴们能够坚持读完。
衷心的祝愿大家都能找到心仪的工作。
首先 typeof 能够正确的判断基本数据类型,但是除了 null, typeof null输出的是对象。
但是对象来说,typeof 不能正确的判断其类型, typeof 一个函数可以输出 'function',而除此之外,输出的全是 object,这种情况下,我们无法准确的知道对象的类型。
instanceof可以准确的判断复杂数据类型,但是不能正确判断基本数据类型。(正确判断数据类型请戳:https://github.com/YvetteLau/Blog/blob/master/JS/data-type.js)
instanceof 是通过原型链判断的,A instanceof B, 在A的原型链中层层查找,是否有原型等于B.prototype,如果一直找到A的原型链的顶端(null;即
Object.prototype.__proto__
),仍然不等于B.prototype,那么返回false,否则返回true.instanceof的实现代码:
PS: Object.keys():返回给定对象所有可枚举属性的字符串数组。
关于forEach是否会改变原数组的问题,有些小伙伴提出了异议,为此我写了代码测试了下(注意数组项是复杂数据类型的情况)。
除了forEach之外,map等API,也有同样的问题。
如还不了解 iterator 接口或 for...of, 请先阅读ES6文档: Iterator 和 for...of 循环
更多细节请戳: https://github.com/YvetteLau/Blog/blob/master/JS/for.js
arr.constructor === Array
. (不准确,因为我们可以指定obj.constructor = Array
)1)拥有length属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理);
2)不具有数组所具有的方法;
类数组是一个普通对象,而真实的数组是Array类型。
常见的类数组有: 函数的参数 arugments, DOM 对象列表(比如通过 document.querySelectorAll 得到的列表), jQuery 对象 (比如 $("div")).
类数组可以转换为数组:
PS: 任何定义了遍历器(Iterator)接口的对象,都可以用扩展运算符转为真正的数组。
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象。
=== 不需要进行类型转换,只有类型相同并且值相等时,才返回 true.
== 如果两者类型不同,首先需要进行类型转换。具体流程如下:
我们来分析一下:
[] == ![]
是true还是false?![]
引用类型转换成布尔值都是true,因此![]
的是falsesplice/reverse/fill/copyWithin/sort/push/pop/unshift/shift
slice/map/forEach/every/filter/reduce/entries/find
注: 数组的每一项是简单数据类型,且未直接操作数组的情况下。
变量提升就是变量在声明之前就可以使用,值为undefined。
在代码块内,使用 let/const 命令声明变量之前,该变量都是不可用的(会抛出错误)。这在语法上,称为“暂时性死区”。暂时性死区也意味着 typeof 不再是一个百分百安全的操作。
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
this的绑定规则有四种:默认绑定,隐式绑定,显式绑定,new绑定.
测试下是否已经成功Get了此知识点(浏览器执行环境):
如果this的知识点,您还不太懂,请戳: 嗨,你真的懂this吗?
执行上下文就是当前 JavaScript 代码被解析和执行时所在环境, JS执行上下文栈可以认为是一个存储函数调用的栈结构,遵循先进后出的原则。
作用域链: 无论是 LHS 还是 RHS 查询,都会在当前的作用域开始查找,如果没有找到,就会向上级作用域继续查找目标标识符,每次上升一个作用域,一直到全局作用域为止。
题难不难?不难!继续挑战一下难!知道难,就更要继续了!
14. 什么是闭包?闭包的作用是什么?闭包有哪些使用场景?
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包最常用的方式就是在一个函数内部创建另一个函数。
闭包的作用有:
call 和 apply 的功能相同,区别在于传参的方式不一样:
fn.call(obj, arg1, arg2, ...),调用一个函数, 具有一个指定的this值和分别地提供的参数(参数的列表)。
fn.apply(obj, [argsArray]),调用一个函数,具有一个指定的this值,以及作为一个数组(或类数组对象)提供的参数。
apply的实现和call很类似,但是需要注意他们的参数是不一样的,apply的第二个参数是数组或类数组.
bind 和 call/apply 有一个很重要的区别,一个函数被 call/apply 的时候,会直接调用,但是 bind 会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
字面量创建对象,不会调用 Object构造函数, 简洁且性能更好;
new Object() 方式创建对象本质上是方法调用,涉及到在proto链中遍历该方法,当找到该方法后,又会生产方法调用必须的 堆栈信息,方法调用结束后,还要释放该堆栈,性能不如字面量的方式。
通过对象字面量定义对象时,不会调用Object构造函数。
在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象。使用原型对象的好处是所有对象实例共享它所包含的属性和方法。
原型链解决的主要是继承问题。
每个对象拥有一个原型对象,通过 proto (读音: dunder proto) 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null(
Object.proptotype.__proto__
指向的是null)。这种关系被称为原型链 (prototype chain),通过原型链一个对象可以拥有定义在其他对象中的属性和方法。构造函数 Parent、Parent.prototype 和 实例 p 的关系如下:
(p.__proto__ === Parent.prototype)
prototype是构造函数的属性。
__proto__
是每个实例都有的属性,可以访问 [[prototype]] 属性。实例的
__proto__
与其构造函数的prototype指向的是同一个对象。其它继承方式实现,可以参考《JavaScript高级程序设计》
浅拷贝是指只复制第一层对象,但是当对象的属性是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
实现一个深拷贝:
看不下去了?别人的送分题会成为你的送命题
22. 防抖和节流的区别是什么?防抖和节流的实现。
防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于设置的时间,防抖的情况下只会调用一次,而节流的情况会每隔一定时间调用一次函数。
防抖的应用场景:
函数节流的应用场景有:
setTimeout() 只是将事件插入了“任务队列”,必须等当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码消耗时间很长,也有可能要等很久,所以并没办法保证回调函数一定会在 setTimeout() 指定的时间执行。所以, setTimeout() 的第二个参数表示的是最少时间,并非是确切时间。
HTML5标准规定了 setTimeout() 的第二个参数的最小值不得小于4毫秒,如果低于这个值,则默认是4毫秒。在此之前。老版本的浏览器都将最短时间设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常是间隔16毫秒执行。这时使用 requestAnimationFrame() 的效果要好于 setTimeout();
0.1 + 0.2 != 0.3 是因为在进制转换和进阶运算的过程中出现精度损失。
下面是详细解释:
JavaScript使用 Number 类型表示数字(整数和浮点数),使用64位表示一个数字。
图片说明:
计算机无法直接对十进制的数字进行运算, 需要先对照 IEEE 754 规范转换成二进制,然后对阶运算。
1.进制转换
0.1和0.2转换成二进制后会无限循环
但是由于IEEE 754尾数位数限制,需要将后面多余的位截掉,这样在进制之间的转换中精度已经损失。
2.对阶运算
由于指数位数不相同,运算时需要对阶运算 这部分也可能产生精度损失。
按照上面两步运算(包括两步的精度损失),最后的结果是
0.0100110011001100110011001100110011001100110011001100
结果转换成十进制之后就是 0.30000000000000004。
promise有三种状态: fulfilled, rejected, pending.
Promise的构造函数是同步执行的。then中的方法是异步执行的。
promise的then实现,详见: Promise源码实现
Promise 是微任务,setTimeout 是宏任务,同一个事件循环中,promise总是先于 setTimeout 执行。
要实现 Promise.all,首先我们需要知道 Promise.all 的功能:
promises 中所有的promise都“完成”时或参数中不包含 promise 时回调完成。
如果想了解更多Promise的源码实现,可以参考我的另一篇文章:Promise的源码实现(完美符合Promise/A+规范)
不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后面的then.
函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
引申:实现一个curry函数,将普通函数进行柯里化:
如果您在面试中遇到了更多的原生JS问题,或者有一些本文未涉及到且有一定难度的JS知识,请给我留言。您的问题将会出现在后续文章中~
本文的写成耗费了非常多的时间,在这个过程中,我也学习到了很多知识,谢谢各位小伙伴愿意花费宝贵的时间阅读本文,如果本文给了您一点帮助或者是启发,请不要吝啬你的赞和Star,您的肯定是我前进的最大动力。https://github.com/YvetteLau/Blog
1.《寒冬求职季之你必须要懂的原生JS》(中)(下)
2.《寒冬求职季之你必须要知道的CSS》
3.《寒冬求职季之你必须要懂的前端安全》
4.《寒冬求职季之你必须要懂的一些浏览器知识》
5.《寒冬求职季之你必须要知道的性能优化》
针对React技术栈:
1.《寒冬求职季之你必须要懂的React》系列
2.《寒冬求职季之你必须要懂的ReactNative》系列
0.1 + 0.2 !== 0.3
此题答案大量使用了此篇文章的图文: https://juejin.im/post/5b90e00e6fb9a05cf9080dffThe text was updated successfully, but these errors were encountered: