You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functioninfo(){console.log(this.age);}varperson={age: 20,
info
}varage=28;varinfo=person.info;info.call(person);//20info.apply(person);//20info.bind(person)();//20
functioninfo(){console.log(this.age);}varperson={age: 20,
info
}varage=28;person.info();//20;执行的是隐式绑定
默认绑定,在不能应用其它绑定规则时使用的默认规则,通常是独立函数调用。
非严格模式: node环境,执行全局对象 global,浏览器环境,执行全局对象 window。
严格模式:执行 undefined
functioninfo(){console.log(this.age);}varage=28;//严格模式;抛错//非严格模式,node下输出 undefined(因为全局的age不会挂在 global 上)//非严格模式。浏览器环境下输出 28(因为全局的age会挂在 window 上)//严格模式抛出,因为 this 此时是 undefinedinfo();
functionflattenDeep(input){conststack=[...input];constres=[];while(stack.length){// 使用 pop 从 stack 中取出并移除值constnext=stack.pop();if(Array.isArray(next)){// 使用 push 送回内层数组中的元素,不会改动原始输入 original inputstack.push(...next);}else{res.push(next);}}// 使用 reverse 恢复原数组的顺序returnres.reverse();}console.log(flattenDeep([1,[2,[3,[4]],5]]));
18. 请实现一个 uniq 函数,实现数组去重
例如:
uniq([1,2,3,5,3,2]);//[1, 2, 3, 5]
1: 利用ES6新增数据类型 Set
Set类似于数组,但是成员的值都是唯一的,没有重复的值。
functionuniq(arry){return[...newSet(arry)];}
2: 利用 indexOf
functionuniq(arry){varresult=[];for(vari=0;i<arry.length;i++){if(result.indexOf(arry[i])===-1){//如 result 中没有 arry[i],则添加到数组中result.push(arry[i])}}returnresult;}
3: 利用 includes
functionuniq(arry){varresult=[];for(vari=0;i<arry.length;i++){if(!result.includes(arry[i])){//如 result 中没有 arry[i],则添加到数组中result.push(arry[i])}}returnresult;}
1. new的实现原理是什么?
new 的实现原理:
2. 如何正确判断this的指向?
如果用一句话说明 this 的指向,那么即是: 谁调用它,this 就指向谁。
但是仅通过这句话,我们很多时候并不能准确判断 this 的指向。因此我们需要借助一些规则去帮助自己:
this 的指向可以按照以下顺序判断:
全局环境中的 this
浏览器环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象 window;
node 环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部),this 都是空对象 {};
是否是 new 绑定
如果是 new 绑定,并且构造函数中没有返回 function 或者是 object,那么 this 指向这个新对象。如下:
函数是否通过 call,apply 调用,或者使用了 bind 绑定,如果是,那么this绑定的就是指定的对象【归结为显式绑定】。
这里同样需要注意一种特殊情况,如果 call,apply 或者 bind 传入的第一个参数值是 undefined 或者 null,严格模式下 this 的值为传入的值 null /undefined。非严格模式下,实际应用的默认绑定规则,this 指向全局对象(node环境为global,浏览器环境为window)
隐式绑定,函数的调用是在某个对象上触发的,即调用位置上存在上下文对象。典型的隐式调用为: xxx.fn()
默认绑定,在不能应用其它绑定规则时使用的默认规则,通常是独立函数调用。
非严格模式: node环境,执行全局对象 global,浏览器环境,执行全局对象 window。
严格模式:执行 undefined
箭头函数的情况:
箭头函数没有自己的this,继承外层上下文绑定的this。
3. 深拷贝和浅拷贝的区别是什么?实现一个深拷贝
深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。
深拷贝
浅拷贝
可以使用 for in、 Object.assign、 扩展运算符 ... 、Array.prototype.slice()、Array.prototype.concat() 等,例如:
可以看出浅拷贝只最第一层属性进行了拷贝,当第一层的属性值是基本数据类型时,新的对象和原对象互不影响,但是如果第一层的属性值是复杂数据类型,那么新对象和原对象的属性值其指向的是同一块内存地址。
深拷贝实现
JSON.parse(JSON.stringify(obj)) 是最简单的实现方式,但是有一些缺陷:
4. call/apply 的实现原理是什么?
call 和 apply 的功能相同,都是改变 this 的执行,并立即执行函数。区别在于传参方式不同。
func.call(thisArg, arg1, arg2, ...):第一个参数是 this 指向的对象,其它参数依次传入。
func.apply(thisArg, [argsArray]):第一个参数是 this 指向的对象,第二个参数是数组或类数组。
一起思考一下,如何模拟实现 call ?
首先,我们知道,函数都可以调用 call,说明 call 是函数原型上的方法,所有的实例都可以调用。即: Function.prototype.call。
bind 的实现思路和 call 一致,仅参数处理略有差别。如下:
5. 柯里化函数实现
在开始之前,我们首先需要搞清楚函数柯里化的概念。
函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
6. 如何让 (a == 1 && a == 2 && a == 3) 的值为true?
== 操作符在左右数据类型不一致时,会先进行隐式转换。
a == 1 && a == 2 && a == 3 的值意味着其不可能是基本数据类型。因为如果 a 是 null 或者是 undefined bool类型,都不可能返回true。
因此可以推测 a 是复杂数据类型,JS 中复杂数据类型只有 object,回忆一下,Object 转换为原始类型会调用什么方法?
如果部署了 [Symbol.toPrimitive] 接口,那么调用此接口,若返回的不是基本数据类型,抛出错误。
如果没有部署 [Symbol.toPrimitive] 接口,那么根据要转换的类型,先调用 valueOf / toString
i. 非Date类型对象,hint 是 default 时,调用顺序为:valueOf >>> toString,即valueOf 返回的不是基本数据类型,才会继续调用 valueOf,如果toString 返回的还不是基本数据类型,那么抛出错误。
ii. 如果 hint 是 string(Date对象的hint默认是string) ,调用顺序为:toString >>> valueOf,即toString 返回的不是基本数据类型,才会继续调用 valueOf,如果valueOf 返回的还不是基本数据类型,那么抛出错误。
iii. 如果 hint 是 number,调用顺序为: valueOf >>> toString
7. 什么是BFC?BFC的布局规则是什么?如何创建BFC?
Box 是 CSS 布局的对象和基本单位,页面是由若干个Box组成的。
元素的类型 和 display 属性,决定了这个 Box 的类型。不同类型的 Box 会参与不同的 Formatting Context。
Formatting Context 是页面的一块渲染区域,并且有一套渲染规则,决定了其子元素将如何定位,以及和其它元素的关系和相互作用。
Formatting Context 有 BFC (Block formatting context),IFC (Inline formatting context),FFC (Flex formatting context) 和 GFC (Grid formatting context)。FFC 和 GFC 为 CC3 中新增。
8. 异步加载JS脚本的方式有哪些?
defer 和 async 的区别在于:
动态创建的 script ,设置 src 并不会开始下载,而是要添加到文档中,JS文件才会开始下载。
9. ES5有几种方式可以实现继承?分别有哪些优缺点?
ES5 有 6 种方式可以实现继承,分别为:
1. 原型链继承
原型链继承的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
2. 借用构造函数
借用构造函数的技术,其基本思想为:
在子类型的构造函数中调用超类型构造函数。
3. 组合继承(原型链 + 借用构造函数)
组合继承指的是将原型链和借用构造函数技术组合到一块,从而发挥二者之长的一种继承模式。基本思路:
使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承,既通过在原型上定义方法来实现了函数复用,又保证了每个实例都有自己的属性。
无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
4. 原型式继承
原型继承的基本思想:
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
在 object() 函数内部,先穿甲一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例,从本质上讲,object() 对传入的对象执行了一次浅拷贝。
ECMAScript5通过新增 Object.create()方法规范了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象(可以覆盖原型对象上的同名属性),在传入一个参数的情况下,Object.create() 和 object() 方法的行为相同。
在没有必要创建构造函数,仅让一个对象与另一个对象保持相似的情况下,原型式继承是可以胜任的。
同原型链实现继承一样,包含引用类型值的属性会被所有实例共享。
5. 寄生式继承
寄生式继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部已某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。
基于 person 返回了一个新对象 -—— person2,新对象不仅具有 person 的所有属性和方法,而且还有自己的 sayHi() 方法。在考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。
6. 寄生组合式继承
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法,基本思路:
不必为了指定子类型的原型而调用超类型的构造函数,我们需要的仅是超类型原型的一个副本,本质上就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下所示:
至此,我们就可以通过调用 inheritPrototype 来替换为子类型原型赋值的语句:
只调用了一次超类构造函数,效率更高。避免在SuberType.prototype上面创建不必要的、多余的属性,与其同时,原型链还能保持不变。
因此寄生组合继承是引用类型最理性的继承范式。
10. 隐藏页面中的某个元素的方法有哪些?
屏幕并不是唯一的输出机制,比如说屏幕上看不见的元素(隐藏的元素),其中一些依然能够被读屏软件阅读出来(因为读屏软件依赖于可访问性树来阐述)。为了消除它们之间的歧义,我们将其归为三大类:
完全隐藏
1.display 属性
2.hidden 属性
HTML5 新增属性,相当于 display: none
1.利用 position 和 盒模型 将元素移出可视区范围
2. 利用 transfrom
3.设置其大小为0
再设置一个层级较高的元素覆盖在此元素上。
读屏软件不可读,占据空间,可见。
11. let、const、var 的区别有哪些?
1.let/const 定义的变量不会出现变量提升,而 var 定义的变量会提升。
2.相同作用域中,let 和 const 不允许重复声明,var 允许重复声明。
3.const 声明变量时必须设置初始值
4.const 声明一个只读的常量,这个常量不可改变。
这里有一个非常重要的点即是:在JS中,复杂数据类型,存储在栈中的是堆内存的地址,存在栈中的这个地址是不变的,但是存在堆中的值是可以变得。有没有相当常量指针/指针常量~
12. prototype 和 proto 区别是什么?
prototype是构造函数的属性。
proto 是每个实例都有的属性,可以访问 [[prototype]] 属性。
实例的__proto__ 与其构造函数的prototype指向的是同一个对象。
13. 防抖函数的作用是什么?请实现一个防抖函数
防抖函数的作用就是控制函数在一定时间内的执行次数。防抖意味着N秒内函数只会被执行一次,如果N秒内再次被触发,则重新计算延迟时间。
举例说明: 小思最近在减肥,但是她非常吃吃零食。为此,与其男朋友约定好,如果10天不吃零食,就可以购买一个包(不要问为什么是包,因为包治百病)。但是如果中间吃了一次零食,那么就要重新计算时间,直到小思坚持10天没有吃零食,才能购买一个包。所以,管不住嘴的小思,没有机会买包(悲伤的故事)... 这就是 防抖。
immediate 为 true 时,表示函数在每个等待时延的开始被调用。immediate 为 false 时,表示函数在每个等待时延的结束被调用。
14. 节流函数的作用是什么?有哪些应用场景,请实现一个节流函数
节流函数的作用是规定一个单位时间,在这个单位时间内最多只能触发一次函数执行,如果这个单位时间内多次触发函数,只能有一次生效。
禁用第一次首先执行,传递 {leading: false} ;想禁用最后一次执行,传递 {trailing: false}
15. 什么是闭包?闭包的作用是什么?
闭包的定义
《JavaScript高级程序设计》:
《JavaScript权威指南》:
《你不知道的JavaScript》
创建一个闭包
闭包使得函数可以继续访问定义时的词法作用域。拜 fn 所赐,在 foo() 执行后,foo 内部作用域不会被销毁。
闭包的作用
能够访问函数定义时所在的词法作用域(阻止其被回收)。
私有化变量
模块模式具有两个必备的条件(来自《你不知道的JavaScript》)
16. 实现 Promise.all 方法
在实现 Promise.all 方法之前,我们首先要知道 Promise.all 的功能和特点,因为在清楚了 Promise.all 功能和特点的情况下,我们才能进一步去写实现。
Promise.all(iterable) 返回一个新的 Promise 实例。此实例在 iterable 参数内所有的 promise 都 fulfilled 或者参数中不包含 promise 时,状态变成 fulfilled;如果参数中 promise 有一个失败rejected,此实例回调失败,失败原因的是第一个失败 promise 的返回结果。
p的状态由 p1,p2,p3决定,分成以下;两种情况:
(1)只有p1、p2、p3的状态都变成 fulfilled,p的状态才会变成 fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被 rejected,p的状态就变成 rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.all 的返回值是一个 promise 实例
17. 请实现一个 flattenDeep 函数,把嵌套的数组扁平化
例如:
ES6 为数组实例新增了 flat 方法,用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数组没有影响。
flat 默认只会 “拉平” 一层,如果想要 “拉平” 多层的嵌套数组,需要给 flat 传递一个整数,表示想要拉平的层数。
当传递的整数大于数组嵌套的层数时,会将数组拉平为一维数组,JS能表示的最大数字为 Math.pow(2, 53) - 1,因此我们可以这样定义 flattenDeep 函数
18. 请实现一个 uniq 函数,实现数组去重
例如:
Set类似于数组,但是成员的值都是唯一的,没有重复的值。
19. 在JS中什么是变量提升?什么是暂时性死区?
变量提升就是变量在声明之前就可以使用,值为undefined。
在代码块内,使用 let/const 命令声明变量之前,该变量都是不可用的(会抛出错误)。这在语法上,称为“暂时性死区”。暂时性死区也意味着 typeof 不再是一个百分百安全的操作。
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
20. JSONP 的原理是什么?
尽管浏览器有同源策略,但是 <script> 标签的 src 属性不会被同源策略所约束,可以获取任意服务器上的脚本并执行。jsonp 通过插入 script 标签的方式来实现跨域,参数只能通过 url 传入,仅能支持 get 请求。
The text was updated successfully, but these errors were encountered: