Open YvetteLau opened 5 years ago
mark 第五点arguments拼错哒
mark 第五点arguments拼错哒
哈哈哈~谢谢,我更正一下~
迷弟报道~
原始类型有6种,分别是undefined,null,bool,string,number,symbol(ES6新增)。
原始类型里又新加了 BigInt
迷弟报道~
感谢,O(∩_∩)O哈哈~
原始类型有6种,分别是undefined,null,bool,string,number,symbol(ES6新增)。
原始类型里又新加了 BigInt
赞赞赞,学习了~
深拷贝那里不能用 for...in
会把原型链上的属性也拷贝进来的,用 Object.keys
上面实现Promise.all的函数中在onRejected回调函数里写的return真的有用吗?并不能阻止其他promise继续执行吧?这个return相当于return undefined给一下一个.then的onFullfilled回调了
深拷贝那里不能用
for...in
会把原型链上的属性也拷贝进来的,用Object.keys
我觉得拷贝上也没什么问题?如果使用Object.keys会增加算法使用的空间.
深拷贝那里不能用
for...in
会把原型链上的属性也拷贝进来的,用Object.keys
上面实现Promise.all的函数中在onRejected回调函数里写的return真的有用吗?并不能阻止其他promise继续执行吧?这个return相当于return undefined给一下一个.then的onFullfilled回调了
Promise.all的return意思是返回一个promise对象,return不能阻止其他的promise执行~~~
深拷贝那里不能用
for...in
会把原型链上的属性也拷贝进来的,用Object.keys
可能需要看下深拷贝是否应该拷贝原型链上的方法~ 这个我确实没有确认~
是否拷贝原型链的属性可以作为配置项吧。不过从直觉上来说更倾向于不拷贝原型链,如果需要则再去做一次原型链的深拷贝。
是否拷贝原型链的属性可以作为配置项吧。不过从直觉上来说更倾向于不拷贝原型链,如果需要则再去做一次原型链的深拷贝。
嗯嗯,不知道深拷贝的定义中有没有涉及到这个~
柯里化有点儿问题... 我好像看错了...
可参见 https://github.com/BuptStEve/blog/issues/11
// 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])
~柯里化有点儿问题...~ 我好像看错了... 可参见 BuptStEve/blog#11
// 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 我看错了...没啥问题 😄
@YvetteLau 我看错了...没啥问题 😄
O(∩_∩)O哈哈~ 还是谢谢了~~~ 看得真仔细~手动点赞~👍
作者,我还想问一个问题。关于Promise的缺点,我们应该如何捕捉异常呢,如果在最后一个.catch中也发生异常我们怎么办,没有东西可以捕获这个异常了?直接等待浏览器自动进行垃圾回收吗?
Promise.prototype.finally = function (callback) {
return this.then((value) => {
return Promise.resolve(callback()).then(() => {
return value;
});
}, (err) => {
return Promise.resolve(callback()).then(() => {
throw err;
});
});
}
关于这个Promise.prototype.finally, 我想问问为什么需要对callback的结果进行resolve呢,反正最后返回给下一个promise的结果是finally之前promise决议的结果,所以可不可以直接这样,如下
Promise.prototype.finally = function(cb) {
return this.then(function(value) {
cb();
return value;
}, function(error) {
cb();
throw error;
});
}
Promise.prototype.finally = function (callback) { return this.then((value) => { return Promise.resolve(callback()).then(() => { return value; }); }, (err) => { return Promise.resolve(callback()).then(() => { throw err; }); }); }
关于这个Promise.prototype.finally, 我想问问为什么需要对callback的结果进行resolve呢,反正最后返回给下一个promise的结果是finally之前promise决议的结果,所以可不可以直接这样,如下
Promise.prototype.finally = function(cb) { return this.then(function(value) { cb(); return value; }, function(error) { cb(); throw error; }); }
需要用Promise.resolve,因为callback返回的可能是一个Promise对象~ 你可以使用下面的测试demo感受一下~
let p2 = new Promise((resolve, reject) => { resolve(200); }); p2.then((value)=>{ console.log(value); }).finally(()=>{ return new Promise((resolve, reject) => { reject(400) }) }).then((value)=>{ console.log('resolved, ', value); }, (err)=>{ console.log('rejected ', err); });
作者,我还想问一个问题。关于Promise的缺点,我们应该如何捕捉异常呢,如果在最后一个.catch中也发生异常我们怎么办,没有东西可以捕获这个异常了?直接等待浏览器自动进行垃圾回收吗?
通常情况下,我们不会在最后一个catch中做一些操作,一般只会打印错误日志。 如果最后一个catch发生异常,的确没法捕获~ 异步不能被try catch,这也是async await 的好处之一~
Promise.prototype.finally = function (callback) { return this.then((value) => { return Promise.resolve(callback()).then(() => { return value; }); }, (err) => { return Promise.resolve(callback()).then(() => { throw err; }); }); }
关于这个Promise.prototype.finally, 我想问问为什么需要对callback的结果进行resolve呢,反正最后返回给下一个promise的结果是finally之前promise决议的结果,所以可不可以直接这样,如下
Promise.prototype.finally = function(cb) { return this.then(function(value) { cb(); return value; }, function(error) { cb(); throw error; }); }
需要用Promise.resolve,因为callback返回的可能是一个Promise对象~ 你可以使用下面的测试demo感受一下~
let p2 = new Promise((resolve, reject) => { resolve(200); }); p2.then((value)=>{ console.log(value); }).finally(()=>{ return new Promise((resolve, reject) => { reject(400) }) }).then((value)=>{ console.log('resolved, ', value); }, (err)=>{ console.log('rejected ', err); });
对对对对,我也是刚刚反映过来了,传给finally的回调函数也可以是异步的。谢谢
作者,我还想问一个问题。关于Promise的缺点,我们应该如何捕捉异常呢,如果在最后一个.catch中也发生异常我们怎么办,没有东西可以捕获这个异常了?直接等待浏览器自动进行垃圾回收吗?
通常情况下,我们不会在最后一个catch中做一些操作,一般只会打印错误日志。 如果最后一个catch发生异常,的确没法捕获~ 异步不能被try catch,这也是async await 的好处之一~
感谢作者的回答
题目里面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,因此![]
的是falseES6 class 子类必须在父类的构造函数中调用super(),这样才有this对象;ES5中类继承的关系是相反的,先有子类的this,然后用父类的方法应用在this上。
splice/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. 什么是闭包?闭包的作用是什么?闭包有哪些使用场景?
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包最常用的方式就是在一个函数内部创建另一个函数。
闭包的作用有:
实现JS的模块
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 的功能:
如果想了解更多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/5b90e00e6fb9a05cf9080dff