0、JavaScript的数据类型
JavaScript 共有八种数据类型,
分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。
其中 Symbol 和 BigInt 是 ES6 中新增的数据类型:
- Symbol(), 创建一个唯一的值,并且这个值属于基本数据类型。该值能作为对象属性的标识符。
console.log(Symbol('foo') === Symbol('foo'));
// Expected output: false
- BigInt 是一种数字类型的数据,它可以表示任意大的整数,即使这个数已经超出了Number 能够表示的安全整数范围。
// 可以用在一个整数字面量后面加 n 的方式定义一个 BigInt
const theBiggestInt = 9007199254740991n;
// 调用函数 BigInt()方法创建BigInt类型的数据
const alsoHuge = BigInt(9007199254740991);
// ↪ 9007199254740991n
这些数据可以分为原始数据类型和引用数据类型:
-
基本数据类型(值类型):Number,String,Boolean,Undefined,Null、Symbol、BigInt
存放在栈中,占据空间小、大小固定,属于被频繁使用的数据,按值访问 -
引用数据类型(引用类型):数组、对象、函数
存放在堆中,占据空间大、大小不固定,栈内存中有一个地址指向堆内存中的对象。按地址访问。其中 Symbol 和 BigInt 是 ES6 中新增的数据类型:
区别:
基础数据类型是值引用,存储在栈内存中,
引用数据类型是地址引用,存储在堆内存中。
1、作用域
1.1、js数据存储
- 栈
- 存放基本类型的数据和对象的引用(基本类型的数据在内存中占据固定大小的空间)。
- 内存空间由系统自动释放
- 数据的存取遵循
先进先出
的原则
- 堆
- 存放引用类型的数据(引用类型的数据在内存中占据的大小不固定)、函数执行上下文
- 内存空间不会自动释放,
首先,可由开发者释放,
若开发者不释放,程序结束时,可能由垃圾回收机制回收。 - 数据的存取遵循
先进后出
的原则
1.2、执行上下文
当函数执行时,会创建一个执行上下文的环境,分为创建和执行两个阶段:
- 创建阶段:函数被调用未执行任何代码时,创建一个拥有3个属性的对象
- 执行阶段:分配变量(提升)、函数的引用,赋值;执行代码。
参考链接:https://juejin.cn/post/6890705692643196935
1.3、执行上下文栈
- 代码中只有一个全局执行上下文和无数个函数执行上下文,这些组成了执行上下文栈
- 一个函数的执行上下文,在函数执行完毕后,会被移出执行上下文栈
1.4、作用域与作用域链
-
作用域
全局作用域、函数作用域、ES6块级作用域。作用域的最大用途
就是隔离变量或函数,并控制生命周期。作用域
在函数执行上下文创建时就定义好了,不是函数执行时定义的
-
作用域链
当一个块或函数嵌套在另一个块或函数中时,发生了作用域嵌套。在当前函数中如果无法找到某个变量,就会往上一级嵌套的作用域中去寻找,直到找到该变量或抵达全局作用域,这样的链式关系称为作用域链
2、变量与内存
变量与内存的关系
- 变量名
- 内存地址
- 内存空间
JS 引擎在读取变量时,先找到变量绑定的内存地址,然后找到地址所指向的内存空间,最后读取其中的内容。
当对变量进行重新赋值时,JS 引擎不会用新值覆盖之前旧值的内存空间(虽然从写代码的角度来看,确实像是被覆盖掉了),而**是重新 分配一个新的内存空间来存储新值,并将新的内存地址与变量进行绑定****,JS 引 擎会根据回收机制进行内存空间的回收。
const 定义常量后,变量名与内存地址之间建立了一种不可变的绑定关系,阻隔变量地址被改变,当 const 定义的变量进行重新赋值时,根据前面 的论述,JS 引擎会尝试重新分配新的内存空间,所以会被拒绝,便会抛出异常。
3、改变函数内部this指针的指向函数(apply call)
apply,call功能相同,但是传入的参数的形式不同;
apply的第二个参数是数组形式。eg:[arg1,arg2,arg3];
call的第二个参数是arg1,arg2,arg3这样的形式
4、JavaScript事件模型
浏览器事件模型中的过程主要分为三个阶段:捕获阶段、目标阶段、冒泡阶段
。
阻止事件传播
e.stopPropagation()
阻止冒泡和捕获阶段的传播。e.stopImmediatePropagation()
如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。
但如果其中某个监听函数执行了 event.stopImmediatePropagation() 方法,则当前元素剩下的监听函数将不会被执行
事件模型包括:
- 原始事件模型
就是ele.οnclick=function(){}这种类型的事件模型 - 捕获型事件模型
事件是从document向下传递,直到事件的发生地(目标元素) - 冒泡事件模型
指事件从事件的发生地(目标元素),一直向上传递,直到document。
IE是只支持冒泡事件模型的。
5、let const var区别
-
其是否会挂载到window上
在 ES5 中,顶层对象window的属性就是全局变量,var 命令和 function 命令声 明的全局变量,自然也就是window对象的属性。
但 ES6 规定,
var 命令和 function 命令声明的全局变量,依旧是顶层对象的属 性,
let 命令、const 命令、class 命令声明的全局变量,不属于顶层对象的属 性。即 let和const声明的变量不会挂载到window上
,它存放在自己的块级作用域内。var id1 = 20; let id = 2; let json = { id: 1, show:function(){ setTimeout(function(){ console.log(this) // setTimeout这个函数,并未明确的指出谁调用的setTimeout,所以这里的this指向window console.log(this.id); //undefined, 这是由于let声明的变量并没有挂载到window上,所以这里是 undefined; console.log(id); // 2, 根据作用域链,找到变量id console.log(this.id1); // 20, 由于var声明的变量挂载在window上,故输出值 console.log(id1); // 20, 根据作用域链,找到变量id1 },2000) } } json.show();
-
是否存在变量提升
var声明变量存在变量提升,let和const不存在变量提升 -
是否形成块作用域
let和const声明形成块作用域,var变量提升不会形成作用域 -
同一作用域下是否可以声明同名变量
同一作用域下let和const不能声明同名变量,而var可以 -
是否可以修改
var和let可以可以修改声明的变量,const不可以 -
let 和 const 定义的变量在定义之前调用,会抛出错误(形成了暂时性死区),而 var 不会。
-
const 声明一个只读的常量。一旦声明,常量的值就不能改变(如果声明是一个对象,那么
不能改变的是对象的引用地址
) -
const ,对于基本类型的数据(数值、字符串、布尔值)来说,其值不可改变;
-
const,对于引用类型的数据(主要是对象和数组)来说,变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。
什么是块级作用域
{ }作为块级作用域分界线,例如 if、for语句,由于有{},所以就会形成块级作用域
参考链接 https://www.jianshu.com/p/a55d3328ff8e
6、同源策略
定义: 所谓的同源,指的是协议,域名,端口相同。
浏览器处于安 全方面的考虑,只允许本域名下的接口交互,
不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。
目的: 为了保证用户信息的安全,防止恶意的网站窃取数据。
7、null与undefined的异同点是什么呢?
同: 都是原始类型,判断时都为false.
异:
-
undefined,表示该变量声明过但并未赋过值
1)变量声明但未赋值,获取值时显示undefined
var name; console.log(name)
2)获取对象的属性名不存在时,显示undefined
var obj = {} console.log(obj.age)
3)函数没有写返回值,拿到的是undefined
function add(){} var total = add(); console.log(total);
4)函数定义了形参,调用时没有传实参,显示undefined
function reduce(num1,num2){ console.log(num2)}; reduce(1);
-
null,表示空对象,是一个空指针
作用:
1)一般用于主动释放对象的引用
2)作为对象原型链的终点Object.prototype.__proto__ 为null
-
null,转为数值时为0;
undefined,转为数值时为NaN。 -
当使用双等号对两种类型的值进行比较时会返回 true,
使用三个等号时会返回 false。 -
使用typeof 判断数据类型时,
typeof undefined // undefined
typeof null // object
-
如何安全的获取undefined?
由于undefined 在 JavaScript 中不是一个保留字, 这意味着可以使用 undefined 来作为一个变量名,但是这样的做法是非常危险的, 它会影响对 undefined 值的判断。
因们可以通过一些方法获得安全的 undefined 值, 比如说:void 0
。
8、Axios特性
- 1、可以在浏览器中发送 XMLHttpRequests
- 2、支持 Promise 语法
- 3、支持拦截、取消请求
- 4、转换请求数据和响应数据
- 5、客户端免受 CSRF 攻击
- 6、可以在 node.js 发送 http 请求
9、用new操作符创建对象时发生的事情(new 一个构造函数的过程)
- 1、创建一个空对象
- 2、将这个空对象的原型指向构造函数的 prototype 属性,
即 空对象.__proto__ = 构造函数.prototype
- 3、将this指向这个空对象
- 4、执行构造函数中的代码,为这个空对象添加属性和方法
- 5、如果构造函数返回了一个新对象,则正常返回,如果没有返回,则默认返回新创建的那个对象。
手写new函数
//ctx为构造函数
//...args为ctx初始化时传入的参数
function myNew(ctx, ...args){
//先用Object创建一个空的对象
let obj = new Object();
//新对象的__proto__ 属性指向该构造函数的原型对象
obj.__proto__ = ctx.prototype;
//将构造函数中的this指向该对象,并用res接收构造函数返回的对象
let res = ctx.apply(obj,args);
//如果构造函数返回了一个对象,则返回那个对象
//如果构造函数没有返回一个对象,则返回新创建的对象
//如果构造函数返回一个基础类型的值,也返回新创建的对象
return res instanceof Object ? res : obj;
}
function Animal(name) {
this.name = name;
}
let animal = myNew(Animal, 'dog');
console.log(animal.name) // dog
10、sessionId详解
sessionid是一个浏览器与服务器会话的一个标识符,
浏览器第一次访问服务器会在服务器端生成一个session,同时也会生成唯一的sessionId与该Session对应,并返回sessionId到客户端;
当浏览器再次发送请求的时候,会将这个sessionId带上,服务器接受到请求之后就会依据sessionId找到相应的Session,从而识别用户。
11、创建对象的几种方法
- 1、字面量对象
var obj = {name: '01'};
- 2、通过new Object声明一个对象
var obj = new Object({name: '011'});
- 3、Object.create() 方法
Object.create()中有两个参数, 第一个参数是这个新对象的原型。 第二个参数属性描述符对象
var obj = Object.create(Object.prototype) // 和{}和new Object()一样
- 4、实例化构造函数创建对象
var M = function(){this.name='o2'};
var obj = new M();
更多请参考链接:https://blog.csdn.net/yiyueqinghui/article/details/108726793
12、js内置函数/内置对象
内置函数
Number、String、Boolean、Array、Object、Function、Date、RegExp、Error
内置对象
Math 、JSON
13、dom节点
-
1、创建新节点
createDocumentFragment() //创建一个DOM片段,无需传入参数 createElement() //创建一个具体的元素 createTextNode() //创建一个文本节点
示例:
<ul id="ul"> </ul> var element = document.getElementById('ul'); var fragment = document.createDocumentFragment(); var browsers = ['Firefox', 'Chrome', 'Opera', 'Safari', 'Internet Explorer']; browsers.forEach(function(browser) { var li = document.createElement('li'); li.textContent = browser; fragment.appendChild(li); }); element.appendChild(fragment);
因为文档片段存在于内存中,并不在DOM树中,
所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。 -
2、添加、移除、替换、插入
appendChild()、removeChild()、replaceChild()、insertBefore()
-
3、查找
getElementById() //通过元素Id,唯一性 getElementsByClassName() //通过class属性 getElementsByTagName() //通过标签属性 querySelector() //获取相应节点(返回一个节点) querySelectorAll() //获取相应节点列表 (返回一个节点列表)
-
4、dom节点的attribute和property有何区别?
4.1、property:一个js对象的属性的修改
var p = document.getElementByTagName('p')[0]; console.log(p.nodeName); // nodeName是p的property,即nodeName是p的属性
4.2、attribute:对html标签属性的修改
p.getAttribute('data-name'); p.setAttribute('data-name', 'imooc');
15、js自定义事件CustomEvent方法
//第一步,利用CustomEvent自定义事件
var myEvent = new CustomEvent('event_name', {
detail: { title: 'This is title!'},
});
//第二步,监听自定义的事件
window.addEventListener('event_name', function(event){
console.log('得到标题为:', event.detail.title);
});
// 第三步,触发该事件
if(window.dispatchEvent) {
window.dispatchEvent(myEvent);
} else {
window.fireEvent(myEvent);
}
16、BOM(Browser Object Model)
BOM,即浏览器对象模型,主要用于客户端浏览器的管理。BOM 的核心是window
DOM 的最根本的对象 document 对象也是 BOM的 window 对象的子对象
window中常用的属性或方法如下:
-
document:整个 HTML 文档,可被用来访问文档内容及其所有页面元素。
-
navigator:客户端浏览器有关信息,比如说浏览器的类型和版本号等
var ua = navigator.userAgent; ua.indexOf('chrome');
-
screen:客户端屏幕的信息。
var w = screen.width; var h = screen.height;
-
history:浏览器窗口访问过的 URL 信息。
history.back(); history.forward();
-
location:当前网页文档的 URL 信息。
location.href location.protocol // http 、https location.host location.pathname location.search location.hash
17、XML和JSON的区别
XML 指可扩展标记语言,被设计用来传输和存储数据。
二者区别:
- 1、数据体积方面
JSON相对于XML来讲,数据的体积小,传递的速度更快些。 - 2、传输速度方面
JSON的速度要远远快于XML。 - 3、数据描述方面
JSON对数据的描述性比XML较差。 - 4、数据交互方面
JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。
xml示例:
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
18、Web Worker 和webSocket
1、web worker主线程:
web worker的作用:
在 HTML 页面中,如果在执行脚本时,页面的状态是不可响应的,直到脚本执行完成后, 页面才变成可响应。web worker 是运行在后台的 js,独立于其他脚本,不会影响页面你 的性能。并且通过 postMessage 将结果回传到主线程。这样在进行复杂操作的时候,就 不会阻塞主线程了。
如何创建 web worker:
(1)通过 worker = new Worker( url ) 加载一个JS文件来创建一个worker,同时返回一个worker实例。
(2)通过worker.postMessage( data) 方法来向worker发送数据。
(3)绑定worker.onmessage方法来接收worker发送过来的数据。
(4)可以使用 worker.terminate() 来终止一个worker的执行。
2、WebSocket
WebSocket是Web应用程序的传输协议,与传统的http协议不同,它提供了双向的、可持久的数据流。通过在客户端和服务器之间保持连接,服务器的更新可以被及时推送给客户端,而不需要客户端以一定时间间隔去轮询。
19、js延迟加载的方式有哪些
- script标签中的defer(延时加载,dom加载完毕再加载js)
- script标签中的async(异步加载,加载完成后立即执行)属性
- js动态创建script,插入到DOM中
- 将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来执行。
23、http协议的主要特点
- 简单快速:每个资源的url是固定的。
- 灵活:通过一个http协议,可以完成不同类型数据的传输。
- 无永久连接:连接一次就会断掉,不会保持连接。
- 无状态:客户端、服务端是两种身份,http传输完成后,连接中断,下次连接再过来,服务端无法确认是否是上一次连接者的身份。
34、浏览器在生成页面的时候,会生成那两颗树?
构造两棵树,DOM 树和 CSS对象模型,
当浏览器接收到服务器相应来的 HTML 文档后,会遍历文档节点,生成 DOM 树,
CSS对象模型由浏览器解析 CSS 文件生成。
35、ie各版本和chrome可以并行下载多少个资源
- IE6 2个并发,iE7升级之后为6个并发
- Firefox,chrome也是6个
36、Flash、Ajax各自的优缺点
-
Flash适合处理多媒体、矢量图形;对CSS、处理文本上不足,不容易被搜索。
-
Ajax对CSS、文本支持很好,支持搜索;多媒体、矢量图形不足。
-
共同点:
与服务器的无刷新传递消息、用户离/在线状态
37、IE与火狐的事件机制有什么区别? 如何阻止冒泡?
-
事件处理机制:IE是事件冒泡,firefox同时支持捕获型事件和冒泡型事件。
-
阻止冒泡:
ev.stopPropagation()
38、IE请求被缓存的问题
问题
在IE浏览器下,如果请求的方法是GET,并且请求的URL不变,那么这个请求的结果就会被缓存。
解决方法
这个问题的解决方法可以通过实时改变请求的URL,只要URL改变,就不会被缓存,可以通过在URL末尾添加上随机的时间戳参数
,eg:
open('GET','demo.php?rand=+Math.random()',true)
39、什么是事件代理
事件代理,又称之为事件委托,即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。
事件代理的原理是DOM元素的事件冒泡
。使用事件代理的好处是可以提高性能。
40、数组中的forEach和map的区别?
相同点:
-
都是
循环遍历数组中的每一项
-
forEach和map方法里每次执行
匿名函数都支持3个参数
,参数分别是item(当前每一项),index(索引值),arr(原数组) -
只能
遍历数组
-
都
不会改变原数组
不同点:
-
map方法
1.map方法
返回一个新的数组,数组中的元素为原始数组调用函数处理后的值
。 -
forEach方法
1.forEach
无返回
,该方法用来调用数组的每个元素,将元素传给回调函数
41、文件上传如何做断点续传
文件断点续传是HTML5引入的新特性,HTML5的FILE api,有一个slice方法,可以将BLOB对象进行分割。前端通过FileList对象获取到相应的文件,按照指定的分割方式将大文件分段,然后一段一段地传给后端,后端再按顺序一段段将文件进行拼接。
目前比较常用的断点续传的方法
有两种,
一种是通过websocket接口进行文件上传,
另一种是通过ajax,
两种方法各有千秋,虽然websocket听起来比较高端些,但是除了用了不同的协议外其他的算法基本上都是很相似的,并且服务端要开启ws接口,这里用相对方便的ajax来说明断点上传的思路
断点续传最核心的内容
就是把文件“切片”然后再一片一片的传给服务器。
42、介绍this各种情况
this的情况:
1.以函数形式调用
时,this永远都是window
2.以方法的形式
调用时,this是调用方法的对象
3.以构造函数的形式
调用时,this是新创建的那个对象
4.使用call和apply调用
时,this是指定的那个对象
5.箭头函数:箭头函数的this看外层是否有函数,
如果有,外层函数的this就是内部箭头函数的this,
如果没有,就是window
6.特殊情况:通常意义上this指针指向为最后调用它的对象。这里需要注意的一点就是如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例
参考链接:https://blog.csdn.net/yiyueqinghui/article/details/106792408
43、== 和 ===的区别,什么情况下用相等
-
==:默认会进行一次类型转换,检测两个变量的
值
是否相等,
1)如果两个值都是null,或是undefined,那么相等;
如果一个是null,一个是undefined,那么相等;
2)存在boolean类型
,则将boolean类型转为number类型再比较,true等同于1,false等同于0;
3)存在number类型
,则将其他类型均转为number类型再进行比较;
4)存在object类型
,则将object类型应用ToPrimitive方法转换到基本类型再进行比较;
5) 特殊情况:"" == [null]、"" == [undefined]
,这里null及undefined作为empty值进行处理; -
===:用来检测两个变量的
值和类型
是否严格相等,如果两边的类型不一致时,不
会做强制类型准换,直接返回 false -
使用
Object.is()
来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0 和 +0 不再相等,两个 NaN是相等的
44、for in和for of
区别
-
for…in循环出的是key
for…of循环出的是value -
for… in 会遍历对象的整个原型链,
for … of 只遍历当前对象不会遍历原型链; -
对于数组的遍历,
for…in 会返回数组每一项的下标
for…of 会返回数组的第一项值; -
for…in 既可循环对象,又可循环数组
for…of 可以循环数组,不能直接循环对象,需要通过Object.keys()转化为数组,再利用for…of进行循环。
总结:
for…in 循环主要是为了遍历对象而生,不适用于遍历数组;
for…of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象
45、typeof和instanceof 区别
typeof(能检测8种值):
-
1、数字类型
typeof(1)
返回number -
2、字符串类型
typeof(“name”)
返回string -
3、布尔值类型
typeof(true)
返回true -
4、对象、数组、null返回的值是object。
typeof(window),typeof(document),typeof(null)
返回的值都是object -
5、 函数类型,返回的值是function。
typeof(Date)返回function
。 -
6、不存在的变量、函数或者undefined,将返回undefined。
typeof(undefined)都返回undefined
-
7、
symbol
表示独一无二的值let sy = Symbol("KK"); console.log(sy); // Symbol(KK) typeof(sy); // "symbol"
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
ES6 的数据类型除了 Number 、 String 、 Boolean 、 Objec t、 null 和 undefined ,还新增了 Symbol 。 -
8、
BigInt
可以表示任意大的,任意精度的整数
比如说表示大于 2的53次方 - 1 的整数。(因为Number 表示的最大数字 2的53次方 - 1 ),可以表示比Number还要大的整数。let valB = BigInt(10); console.log(typeof valB) // bigint
instanceof(判断该函数构造的原型对象,是否在该对象的原型链上):
let list = []
list instanceof Array // true
list instanceof Object // true
let info = {}
info instanceof Array // false
info instanceof Object //true
46、null、undefined区分
-
js判断undefined (
利用typeof
)var exp = undefined; if (typeof(exp) == "undefined") { alert("undefined"); }
-
js判断null (
利用typeof为object 与 Boolean为false 二者的交集作为null类型的判断
)1、js中Boolean() 判断返回false的情况:
false 、null 、 0 、 undefined、 空字符串''、 NaN
注意事项:
typeof null === "object" typeof NaN === "number"
2、typeof 中返回"object"的情况:
数组、对象、null
二者求交集即可判断null
let val = null; if(!val && typeof val === "object"){ alert('val是null') }
-
NaN类型
NaN 表示非数字类型的数据,但是用 typeof 检测是 number 类型
isNaN --------- 可以用来判断值是否不是一个数字
isNaN(123); // false isNaN(-1.23); // false isNaN(5-2); // false isNaN(0) // false isNaN('87') // false 存在默认类型的转化 isNaN(null) // false 存在默认类型的转化 isNaN("Hello") //true isNaN("2005/12/12"); //true
判断是否是NaN类型
利用 NaN 是唯一一个不等于自身的特点
n !== n
来判断是否是NaN; -
Js 中 null 与 undefined 异同点:
相同点:Boolean判断时,两者都会被转成 false .
不同点:
1、 转换为数字时,结果不同:Number ( null ) // 0 Number (undefined) // NaN
2、Null 表示这个值被定义了,但是这个值是空值,而 Undefined 变量未定义
47、长列表的优化
一般情况下的是通过分页优化
的,
如果不分页的话,那我就需要通过监听滚动条,来实现按需加载
,进而限制首次加载的列表数目。
什么是按需加载
当用户触发了动作时才加载对应的功能
。
触发的动作,包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更 改等。加载的文件,可以是 JS、图片、CSS、HTML 等。
48、iframe的优缺点?
iframe也称作嵌入式框架,嵌入式框架和框架网页类似,它可以把一个网页的框架和内容嵌入在现有的网页中。
提示:
- 可以将提示文字放在
<iframe></iframe>
之间,来提示某些不支持 iframe 的浏览器 缺点:
优点:
- 解决加载缓慢的第三方内容,如图标和广告等的加载问题
- 并行加载脚本
缺点:
- iframe会阻塞主页面的onload事件
- 不利于 SEO
49、普通函数和构造函数的区别
- 1、定义时的区别 ----- 构造函数也是一个普通函数,但是构造函数习懊上首字母大写
- 2、调用方式不一样,晋通函数直接调用,构造函数要用关链字 new 来调用
- 3 、调用时内部解析过程不同,构造函数内部会仓健一个新对象,就是实例,普通函数不会创建新对象
- 4、 this 指向-----构造函数内部的 this 指向实例,普通函数内的this 指向调用函数的对象
- 5、返回值 ----- 构造函数默认的返回值是创建的对象(也就是实例).普通函数的返回值由 return 语句决定
- 6、构造函数的函数名与类名相同
50、get 请求传参长度的误区
经常说 get 请求参数的大小存在限制,而 post 请求的参数大小是无限制的.
实际上 Http 协议从未规定 GET/POST 的请求长度限制是多少,
由于get请求,是将参数拼接在访问地址url中,而浏览器或 web 服务器限制了 url 的长度
,因此,get请求参数的大小存在限制。
不同的浏览器和 WEB 服务器,限制的最大长度不一样,比如说 IE ,则最大长度为 2083byte ,若只支持 Chrome ,则最大长度 8182byte
51、Window.onload和 DOMContentloaded事件的先后顺序
顺序
一般情况下,DOMContentloaded事件要在 window.onload之前执行
,
当DOM树构建完成的时候就会执行 DOMContentloaded事件,
而 window.onload是在页面载入完成的时候,才执行。
区别
1.当 onload事件触发时,页面上所有的DOM、样式表、脚本、图片、 flash都已经加载完成了
。
2.当 DOMContentloaded事件触发时,仅为DOM加载完成
,不包括样式表、图片、f1ash等。
52、node服务器优缺点?
1.优点:事件驱动,输入输出性能很高
2.缺点:cpu计算能力差,比如做很多计算操作,代码运行效率要求高,所以用底层的语言来做,比如c
3.io密集型:需要处理比较多的任务
54、fetch 发送 2 次请求的原因
fetch 发送 post 请求的时候,总是发送 2 次,第一次状态码是 204,第二次才成功?
原因很简单,因为你用 fetch 的 post 请求的时候,导致 fetch
第一次发送了一个 Options 请求,询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的 请求。
57、强,协商缓存
缓存分为两种:强缓存和协商缓存,根据响应的 header 内容来决定
。
强缓存:
- 不向服务器发送请求,直接从缓存取数据
- 强缓存相关字段有
expires,cache-control
。如果 cache-control 与 expires 同时存在的话, cache-control 的优先级高于 expires。
协商缓存:
- 向服务器发送一次请求,通过服务器来告知缓存是否可用
- 协商缓存相关字段有
Last-Modified / If-Modified-Since,Etag/If-None-Match
参考地址:https://blog.csdn.net/yiyueqinghui/article/details/109166703
58、如何解决异步回调地狱
- promise、
- generator、
- async/await
59、说说前端中的事件流
HTML 中与 javascript 交互是通过事件驱动来实现的,例如鼠标点击事件 onclick、页面 的滚动事件 onscroll 等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。 想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
什么是事件流:
事件流描述的是从页面中触发事件的顺序
,
DOM2 级事件流包括下面几个 阶段。
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
addEventListener
:
这个方法接收 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最 后这个布尔值参数如果是 true
,表示在捕获阶段调用事件处理程序;如果是 false
,表示 在冒泡阶段调用事件处理程序。
IE 只支持事件冒泡。
60、如何让事件先冒泡后捕获
在 DOM 标准事件模型中,是先捕获后冒泡。
但是如果要实现先冒泡后捕获的效果,则对于同一个事件,分别监听捕获和冒泡,监听到捕获事件,先暂缓 执行,直到冒泡事件被捕获后再执行捕获事件。
61、说一下事件委托
是什么:
事件委托指的是,不在事件的发生地(直接 dom)上设置监听函数,而是在其父 元素上设置监听函数。
好处:
比较适合动态绑定的元素,新添加的子元素也会有监听函数,也可以有事件触发机制。
62、图片的懒加载和预加载
是什么
-
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
-
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。 常用场景,比如说页面列表较大时,可以只请求第一屏或前两屏的数据。当下拉浏览到时,再去请求相应的数据。
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。 懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
63、clientHeight,scrollHeight,offsetHeight ,以及 scrollTop, offsetTop,clientTop 的区别?
clientHeight
:表示的是可视区域的高度,不包含 border 和滚动条offsetHeight
:表示可视区域的高度,包含了 border 和滚动条scrollHeight
:表示了所有区域的高度,包含了因为滚动被隐藏的部分。clientTop
:表示边框 border 的厚度。scrollTop
:滚动后被隐藏的高度,获取对象相对于由 offsetParent 属性指定的父坐标(css 定位的元素或 body 元素)距离顶端的高度
64、JS 拖拽功能的实现、
首先是三个事件,分别是 mousedown,mousemove,mouseup。
- 容器position:absolute定位,mousedown时,记录下容器初始的top、left的值,标记为初始值;
- 移动过程中 与 最后松开时,计算移动的距离:
移动距离 = 鼠标移动时候的坐标 - 鼠标按下去时候的坐标 + 初始值; - 更新样式中的top、left的值;
补充:也可以通过 html5 的拖放(Drag 和 drop)来实现
65、Ajax 解决浏览器缓存问题
- 方法一、在 ajax 发送请求头加上
Cache-Control: no-cache"
。 - 方法二、在 URL 后面加上一个值为随机数的参数: “fresh=” + Math.random()。
或者在URL后面加一个值为当前时间戳的参数
- 方法三、使用 ajax的话, 新增设置
$.ajaxSetup({cache:false})
即可
66、eval 是做什么的
它的功能是将对应的字符串解析成 JS 并执行
67、如何理解前端模块化,ES6 模块与 CommonJS 模块有什么异同?
前端模块化就是复杂的文件编成一个一个独立的模块,比如 将一个JS 文件分割成一个个独立的模块,这样有利于重用(复用性)和维护(版本迭代)。
但是这样会引来模块之间相互依赖的问题, 所以有了 commonJS 规范,AMD,CMD 规范等等,以及用于 JS 打包的工具 webpack。
68、JS 监听对象属性的改变
- 方法一、在 ES5 中可以通过
Object.defineProperty
来实现已有属性的监听
Object.defineProperty(user,'name',{
set:function(key,value){ }
})
缺点:如果为对象新增加一个属性id,需要重新指定对id属性的监听才行,否则不能监听 id 的变化。
- 方法二、在 ES6 中可以通过
Proxy
来实现
var user = new Proxy({},{
set:function(target,key,value,receiver){ }
})
优点:这样即使有属性在 user 中不存在,后新增的属性,同样可以这样监听这个属性的 变化。
69、如何实现一个私有变量,用 getName 方法可以访问,不能直接访问
- 方法一、闭包内的变量,通过调用方法返回
let Girl = (function() {
var _weight = 0
function P(name, weight) {
this.name = name
_weight = weight
}
P.prototype.getWeight = function() {
return _weight
}
return P
})()
let girl = new Girl('zizi', 49)
console.log(girl)
console.log(girl.getWeight())
- 方法二、对象的key值使用symbol
利用symbol的特性,来生成一个受保护的key。从而实现属性私有化。
let Girl = (function() {
var n = Symbol('weight')
function P(name, weight) {
this.name = name
this[n] = weight
}
P.prototype.getWeight = function() {
return this[n]
}
return P
})()
let girl = new Girl('zizi', 49)
console.log(girl)
console.log(girl.getWeight())
70、 Object.is 的区别
Object.is() ----- 判断两个值是否为同一个值。
71、实现一个两列等高布局
为了实现两列等高,
可以给每列加上 padding-bottom:9999px; margin-bottom:-9999px
;
同时父元素设置 overflow:hidden
;
72、JS 判断类型
判断方法:
- typeof(),
- instanceof,
- constructor
- Object.prototype.toString.call()
73、有一个游戏叫做 Flappy Bird,就是一只小鸟在飞,前面是无尽的沙漠,上 下不断有钢管生成,你要躲避钢管。然后小明在玩这个游戏时候老是卡顿 甚至崩溃,说出原因(3-5 个)以及解决办法(3-5 个)
原因可能是:
1.内存溢出问题。
2.资源过大问题。
3.资源加载问题。
4.canvas 绘制频率问题
解决办法:
1.针对内存溢出问题,我们应该在钢管离开可视区域后,销毁钢管,让垃圾收集器回收 钢管,因为不断生成的钢管不及时清理容易导致内存溢出游戏崩溃。
2.针对资源过大问题,我们应该选择图片文件大小更小的图片格式,比如使用 webp、png 格式的图片,因为绘制图片需要较大计算量。
3.针对资源加载问题,我们应该在可视区域之前就预加载好资源,如果在可视区域生成 钢管的话,用户的体验就认为钢管是卡顿后才生成的,不流畅。
4.针对 canvas 绘制频率问题,我们应该需要知道大部分显示器刷新频率为 60 次/s,因此 游戏的每一帧绘制间隔时间需要小于 1000/60=16.7ms,才能让用户觉得不卡顿。
74、有了解过事件模型吗,DOM 的分级是 什么?
JSDOM 事件流存在如下三个阶段:
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
JSDOM 事件流的触发的先后顺序为:先捕获再冒泡
,
点击 DOM 节点时,事件传播顺序:事件捕获阶段,从上往下传播,然后到达事件目标节点,最后是冒泡阶段,从下往上传播 。
DOM 节点添加事件监听方法 addEventListener,其中参数 capture 可以指定该监听是添加在 事件捕获阶段还是事件冒泡阶段,为 true 是事件捕获,为false 是事件冒泡,
,并非所有的事件都支持冒泡,比如 focus,blur 等等,我们可以通过 event.bubbles 来判断。
事件模型有三个常用方法:
- 1、event.preventDefault(); // 阻止默认行为,阻止a链接默认的跳转行为
- 2、event.stopPropagation(); // 阻止冒泡
- 3、event.stopImmediatePropagation(); // 按钮绑定了2个响应函数,依次注册a,b两个事件,点击按钮,a事件中加event.stopImmediatePropagation()就能阻止b事件
- 4、event.currentTarget; // 指向事件绑定的元素
- 5、event.target; // 指向引起触发事件的元素
75、setTimeout(fn,100);100 毫秒是如何权衡的
setTimeout()函数只是在指定的时间将事件插入了任务列表,如果任务列表中有任务的话,就必须等到它之前的任务都执行完,主线程才会去 执行它指定的回调函数,有可能要等很久,所以没有办法保证回调函数一定会在 setTimeout 指定的时间内执行,
指定的时间(100ms) 是将回调插入队列的时间,而非等待的时间
76、怎么获得对象上的属性
for(let I in obj)
该方法依次访问一个对象及其原型链中所有可枚举的类型object.keys
: 返回一个数组,包括所有可枚举的属性名称object.getOwnPropertyNames
: 返回一个数组包含不可枚举的属性
77、parseInt(string, radix)方法
- 解析一个字符串并返回指定基数的十进制整数,
- radix 是2-36之间的整数,表示被解析字符串的基数。
- radix 小于 2 或大于 36 ,返回NaN;
- 第一个参数,string不能转换为数字,返回NaN;
78、介绍下 Set、Map、WeakSet 和 WeakMap 的区别
-
Set
——是一个集合(类似于数组),- 1.每一项都是唯一的。
- 2.只有健值,没有健名,有点类似数组。
- 3.可以遍历,方法有add, delete,has
-
WeakSet
——是一个对象的集合,与Set相比,- 每一项都是唯一的,且每一项都是对象
- 每一项都是弱引用,可以被垃圾回收机制回收,不容易造成内存泄漏。
- 不能遍历,方法有add, delete,has
-
Map
——本质上是键值对的集合;- 键值对的集合
- 可以被遍历。
-
WeakMap
——只接受对象作为键名(null 除外),不接受其他类型的值作为键名;- 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
- 不能遍历,方法有 get、set、has、delete。
79、ES5/ES6 的继承除了写法以外还有什么区别
-
ES5 的继承,实质上是
先创建子类的实例对象,然后再将父类的方法添加到 this 上
(Parent.apply(this)).ES5 的继承时,通过原型或构造函数机制来实现。
-
ES6 的继承机制完全不同,实质上是
先创建父类的实例对象 this(所以必 须先调用父类的 super()方法),然后再用子类的构造函数修改 this
。ES6 通过 class 关键字定义类,类之间通过 extends 关 键字实现继承。
子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。
因为子类没有自己的 this 对象,而是继承了父类的 this 对象,然后对其进行加工
。 如果不调用 super 方法,子类得不到 this 对象。注意:super 关键字指代父类的实例,即父类的 this 对象。
注意:在子类构造函数中,调用 super 后,才可使用 this 关键字,否则 报错。
80、算法手写题
已知如下数组,编写一个程序将数组扁平化去并除其中重复部分数据,最终得 到一个升序且不重复的数组。
答: 使用 Set 方法去重,
flat(Infinity)扁平化,
sort排序,
Array.from()类数组转数组
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
let list = Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{
return a-b
})
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
Array.prototype.flat()
方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
81、介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块?
- 发出 npm install 命令
- 查询 node_modules 目录之中是否已经存在指定模块
- 若存在,不再重新安装 。
- 若不存在, 查寻npm 的 registry(注册表)中指定的源地址,从这个地址下载压缩包,存放在根目录下的.npm 目录里。
- 解压压缩包到当前项目的 node_modules 目录
82、介绍下观察者模式和订阅-发布模式的区别,各自适 用于什么场景
- 观察者模式中主体和观察者是互相感知的,
- 发布-订阅模式是借助第三方来实现 调度的,发布者和订阅者之间互不感知。
83、cookie 和 token 都存放在 header 中,为什么不会 劫持 token?
- 1、攻击者通过 xss拿到用户的 cookie 然后就可以伪造 cookie 了。
- 2、或者通过 csrf在同个浏览器下面,通过浏览器会自动带上 cookie 的特性 ,在通过 用户网站-攻击者网站-攻击者请求用户网站的方式 浏览器会自动带上 cookie,而token却不会。
- token 是放在 jwt 里面下发给客户端的。不能通过
document.cookie
直接拿到,通过 jwt+ip 的方式 可以防止被劫持,即使被劫持 也是无效的 jwt
84、下面代码中 a 在什么情况下会打印 1
var a = ?;
if(a == 1 && a == 2 && a == 3){
console.log(1);
}
办法一,重置Object的toString()方法
var a = {
i: 1,
toString() { // 这里,重置对象的toString方法
return a.i++;
}
}
if( a == 1 && a == 2 && a == 3 ) { // == 有个默认的类型转化,即a中会调用toString方法
console.log(1);
}
办法二,重置数组的toString方法
let a = [1,2,3];
a.toString = a.shift;
if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}
85、实现一个 sleep 函数
比如 sleep(1000) 意味着等待 1000 毫秒,可从 Promise、Generator、Async/Await 等角度实现。
const sleep = (time) => {
return new Promise( resolve => {
setTimeout(resolve, time)
})
}
sleep(1000).then(() => {
// 这里写你的骚操作
})
86、使用 sort() 对数组 [3, 15, 8, 29, 102, 22] 进 行排序,输出结果
var list = [3, 15, 8, 29, 102, 22];
console.log(list.sort()); // [102, 15, 22, 29, 3, 8]
console.log(list.sort((a,b) => a - b)) [3, 8, 15, 22, 29, 102]
Array.sort()默认的排序方法,会将数组元素转换为字符串,然后比较字符串中字符的 UTF-16 编码顺序来进行排序。所以’102’ 会 排在 ‘15’ 前面
87、HTTPS 握手过程中,客户端如何验证证书的合法性
- 1、校验证书的颁发机构是否受客户端信任。
- 2、通过 CRL 或 OCSP 的方式校验证书是否被吊销。
- 3、对比系统时间,校验证书是否在有效期内。
- 4、通过校验对方是否存在证书的私钥,判断网站域名是否与证书颁发的域名一致。
88、为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片?
- 没有跨域问题,一般这种上报数据,代码要写通用的;(排除 ajax)
- 不会阻塞页面加载,影响用户的体验,只要 new Image 对象就好了;(排 除 JS/CSS 文件资源方式上报)
- 在所有图片中,体积最小;(比较 PNG/JPG)
89、实现 (5).add(3).minus(2) 功能
例: 5 + 3 - 2,结果为 6
Number.prototype.add = function(n) {
return this.valueOf() + n;
};
Number.prototype.minus = function(n) {
return this.valueOf() - n;
};
var num = 5;
num.add(3).minus(2)
// 注意:这里this.valueOf()代表本身的值
91、ES6 代码转成 ES5 代码的实现思路是什么
ES6 转 ES5 目前行业标配是用 Babel
,
转换的大致流程如下:
解析
:解析代码字符串,生成 AST( 抽象语法树 );转换
:按一定的规则转换、修改 AST;生成
:将修改后的 AST 转换成普通代码。
92、为什么普通 for 循环的性能远远高于 forEach 的 性能,请解释其中的原因
- for 循环没有任何额外的函数调用栈和上下文;
- forEach 函数实际上是
array.forEach(function(currentValue, index, arr), thisValue)
它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;
93、写出如下代码的打印结果
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com"
o = new Object()
o.siteUrl = "http://www.google.com"
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl); // "http://www.baidu.com"
webSite 属于复合数据类型,函数参数中以地址传递,修改值会影响到原始值, 但如果将其完全替换成另一个值,则原来的值不会受到影响
94、栈与队列的异同点
1、 栈
- 定义
栈也称为堆栈
,是一种线性表 - 特性
最先放入栈中的内容最后被拿出来,最后放入栈中的内容最先被拿出来, 被称为先进后出、后进先出
。
2、队列
- 定义
队列也是一种特殊的线性表。 - 特性
队列的原则是先进先出,后进后出
。
3、相同点
1.都是线性结构。
2.插入操作都是限定在表尾进行。
3.都可以通过顺序结构和链式结构实现。
4.插入与删除的时间复杂度与空间复杂度上两者均相同。
4、不同点
1.队列先进先出,栈先进后出。
2.删除数据元素的位置不同,栈的删除操作在表尾进行,队列的删除操作在表头进行。
3.顺序栈能够实现多栈空间共享,而顺序队列不能。
什么是SSE
严格地说,HTTP 协议无法做到服务器主动向客户端推送信息。但是,有一种方法,就是服务器向客户端声明,接下来要 发送的是一个数据流,而不是一次性的数据包,这样数据就会连续不断地发送过来,这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是如此。
95、数据类型检测的方式有哪些
(1)、typeof
其中数组、对象、null 都会被判断为 object,
其他判断都正确(即使symbol、bigint数据也可通过typeof 判断)
console.log(typeof Symbol()) // symbol
console.log(typeof 10937453n ) // bigint
(2)、instanceof
instanceof运算符用于检测构造函数的prototype属性是否出现在 **某个实例对象** 的原型链上
[] instanceof Array //true
{} instanceof Array //false
{} instanceof Object //true
[] instanceof Object //true
let a = ''
a instanceof String //false 由于a并不是一个实例对象,它是一个字面量变量
let b = new String('xxx')
b instanceof String //true
所有对象类型 instanceof Object 都是 true
(3) constructor ----- 可用于检测是否是[ ]和{ }
constructor 有两个作用,
一是判断数据的类型,
二是对象实例通过constrcutor 属性访问它的构造函数。
所有对象都会从它的原型上继承一个 constructor 属性:
需要注意,如果创建一个对象来改变它的原型,constructor 就不能用来判断数据类型了:
(4)Object.prototype.toString.call()
Object.prototype.toString.call() 使用 Object 对象的原型方法toString 来判断数据类型:
同样是检测对象 obj 调用 toString 方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为 toString 是 Object 的原型方法,而 Array、function 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(function 类型返回内容为函数体的字符串,Array
类型返回元素组成的字符串…),而不会去调用 Object 上原型的toString 方法(返回对象的具体类型),所以采用 obj.toString()不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。
96、什么是 JavaScript 中的包装类型?
- 在 JavaScript 中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时 JavaScript 会在后台隐式地将基本类型的值转换为对象,如:
const a = 'abc'
a.length; // 3
a.toUpperCase(); // "ABC"
因此,在 访 问 ‘abc’.length 时 , JavaScript 将 ‘abc’ 在 后 台 转 换 成String(‘abc’),然后再访问其 length 属性。
- JavaScript 也可以使用 Object 函数,显式地将基本类型转换为包装类型:
var a = 'abc'
Object(a) // String("abc")
- JavaScript也可以使用 valueOf 方法将包装类型倒转成基本类型:
var a = 'abc';
var b = Object(a)
var c = b.valueOf() // 'abc'
- 看看如下代码会打印出什么:
var a = new Boolean(false)
if(!a){
console.log(1)
}
答案是什么都不会打印,因为虽然包裹的基本类型是 false,但是false 被包裹成包装类型后就成了对象,所以其非值为 false
,所以循环体中的内容不会运行
97、如何判断一个对象是空对象
-
方法一:使用 JSON 自带的.stringify 方法来判断
-
方法二:使用 ES6 新增的方法 Object.keys()来判断
if(Object.keys(obj).length == 0){
console.log('空对象')
}
98、如果 new 一个箭头函数的会怎么样
箭头函数是ES6中的提出来的,
- 它没有prototype,
- 也没有自己的this指向,
- 更不可以使用 arguments 参数,
所以不能 New 一个箭头函数
new 操作符的实现步骤如下:
1.创建一个空对象
2.将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的 prototype 属性
)
3.将构造函数中的 this 指向该对象
4.为这个对象添加属性和方法
5.返回新的对象
所以,上面的第二、三步,箭头函数都是没有办法执行的。
99、对 JSON 的理解
- 在项目开发中,使用 JSON 作为前后端数据交换的方式。在
前端通过将一个符合 JSON 格式的数据结构序列化为JSON 字符串,然后将它传递到后端
,后端通过 JSON 格式的字符串解析后生成对应的数据结构,以此来实现前后端数据的一个传递。 - JSON 和 js 中的对象不是一回事,JSON中对象格式更加严格,比如说在 JSON 中属性值不能为函数,不能出现 NaN 这样的属性值等
- 在 js 中提供了两个函数来实现 js 数据结构和 JSON 格式的转换处理,
JSON.stringify()
、JSON.parse()
100、[‘1’, ‘2’, ‘3’].map(parseInt) what & why ?
真正的答案是[1, NaN, NaN]
Array.prototype.map(callbackFn )
:
该方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。参数callbackFn 中有三个默认的参数
- currentValue
callbackFn 数组中正在处理的当前元素。 - index
callbackFn 数组中正在处理的当前元素的索引。 - array
map 方法调用的数组。
parseInt(string, radix)
解析一个字符串并返回指定基数的十进制整数,radix 是 2-36 之间的整数,表示被解析字符串的基数。
- string
要被解析的值。如果参数不是一个字符串,则将其转换为字符串 。 - radix
从 2 到 36 的整数,表示进制的基数。
如果超出这个范围,将返回 NaN。
假如指定 0 或未指定,基数将会根据字符串的值进行推算。注意,推算的结果不会永远是默认值 10
分析
- parseInt(‘1’, 0) // radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
- parseInt(‘2’, 1) // 第二个参数不在2-36的这个范围,返回NaN
- parseInt(‘3’, 2) // 第二个参数不在2-36的这个范围,返回NaN
所以,最终map函数返回的是一个数组,所以最后结果为[1, NaN, NaN]
101、介绍下 npm 模块安装机制
- 发出npm install命令
- 查询node_modules目录之中是否已经存在指定模块
- 若存在,不再重新安装
- 若不存在
npm 向 registry 查询模块压缩包的网址
下载压缩包,存放在根目录下的.npm目录里
解压压缩包到当前项目的node_modules目录
102、==
中的隐式类型转换
修改如下代码,使其打印1
var a = {num:0};
if(a == 1 && a == 2 && a == 3){
console.log(1);
}
这题考察的应该是类型的隐式转换,
引用类型在做比较时候,会时行隐式类型转换,而隐式转换会调用本类型toString或valueOf方法
修改如下:
1、利用valueOf方法
let a = {
i: 1,
valueOf () {
return a.i++
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('1');
}
2、利用toString方法
let a = {
i: 1,
toString () {
return a.i++
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('1');
}
103、使用 sort() 对数组 [3, 15, 8, 29, 102, 22] 进行排序,输出结果
根据MDN上对Array.sort()的解释,默认的排序方法会将数组元素转换为字符串,然后比较字符串中字符的编码顺序
来进行排序。所以’102’ 会排在 ‘15’ 前面。
输出:[102, 15, 22, 29, 3, 8]
104、有关连续赋值的坑
var a = {n: 1}; // a保持对{n:1}对象的引用
var b = a; // b保持对{n:1}对象的引用
// `.` 的优先级低于 `=` 的优先级
a.x = a = {n: 2}; // a的引用被改变
console.log(a.x) // --> undefined
console.log(b.x) // --> {n: 2}
分析过程:
第一行,第二行,首先,a和b同时引用了{n:2}对象,(对象的赋值,变量中保存的只是对象的引用地址)
第三行,接着执行到a.x = a = {n:2}语句,尽管赋值是从右到左的没错,但是.的优先级比=要高
,
所以这里首先执行a.x,相当于为a(或者b)所指向的{n:1}对象新增了一个属性x,即此时对象将变为{n:1;x:undefined}
。
之后按正常情况,从右到左进行赋值,
此时执行a ={n:2}的时候,a的引用地址改变,指向了新对象{n:2}, 而b依然指向的是旧对象
。
之后执行a.x = {n:2}的时候,并不会重新解析一遍a,而是沿用最初解析a.x时候的a
,也即旧对象,故此时旧对象的x的值为{n:2},旧对象为 {n:1;x:{n:2}},它被b引用着。
后面输出a.x的时候,又要解析a了,此时的a是指向新对象的a
,而这个新对象是没有x属性的,故访问时输出undefined;
而访问b.x的时候,将输出旧对象的x的值,即{n:2}。
105、函数传参为对象时的示例
函数传参,如果参数是对象的话,传入的其实是对象的内存地址
function changeObjProperty(o) { // 指向内存中的对象,在这里叫做引用o1, o是函数内的一个声明的对象,与下面传进来的webSite的引用相同
o.siteUrl = "http://www.baidu.com" // 引用在引用o1上加属性
o = new Object() // 这里是引用o2,与下面webSite的引用不同了
o.siteUrl = "http://www.google.com" // 引用o2上加属性
}
let webSite = new Object(); // webSite的引用一直是o1,没改变过
changeObjProperty(webSite);
console.log(webSite.siteUrl); // 所以是baidu.com
106、输出以下代码运行结果
这题考察的是对象的键名的转换。
对象的键名只能是字符串和 Symbol 类型
。- 其他类型的键名会被转换成字符串类型。
- 对象转字符串默认会调用 toString 方法,而结果为’[object Object]’
测试一
// example 1
var a={}, b='123', c=123;
a[b]='b';
// c 的键名会被转换成字符串'123',这里会把 b 覆盖掉。
a[c]='c';
// 输出 c
console.log(a[b]);
测试二
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
// b 是 Symbol 类型,不需要转换。
a[b]='b';
// c 是 Symbol 类型,不需要转换。任何一个 Symbol 类型的值都是不相等的,所以不会覆盖掉 b。
a[c]='c';
// 输出 b
console.log(a[b]);
测试三
// example 3
var a={}, b={key:'123'}, c={key:'456'};
// b 不是字符串也不是 Symbol 类型,需要转换成字符串。
// 对象类型会调用 toString 方法转换成字符串 [object Object]。
a[b]='b';
// c 不是字符串也不是 Symbol 类型,需要转换成字符串。
// 对象类型会调用 toString 方法转换成字符串 [object Object]。这里会把 b 覆盖掉。
a[c]='c';
// 输出 c
console.log(a[b]);
107、input 输入中文时如何防抖
-
输入到input框触发input事件
-
失去焦点后内容有改变触发change事件
-
识别到你开始使用中文输入法在打拼音时(此时input内还没有填入真正的内容) 触发
compositionstart 事件
-
未输入结束但还在输入中触发
compositionupdate事件
-
输入完成(也就是我们回车或者选择了对应的文字插入到输入框的时刻)触发
compositionend事件
。简单来说就是切换中文输入法时在打拼音时(此时input内还没有填入真正的内容),会首先触发compositionstart,
然后每打一个拼音字母,触发compositionupdate,
最后将输入好的中文填入input中时触发compositionend。
触发compositionstart时,文本框会填入 “虚拟文本”(待确认文本),同时触发input事件;在触发compositionend时,就是填入实际内容后(已确认文本),所以这里如果不想触发input事件的话就得设置一个bool变量来控制。