赞
踩
JavaScript 共有八种数据类型,
分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。
其中 Symbol 和 BigInt 是 ES6 中新增的数据类型:
console.log(Symbol('foo') === Symbol('foo'));
// Expected output: false
// 可以用在一个整数字面量后面加 n 的方式定义一个 BigInt
const theBiggestInt = 9007199254740991n;
// 调用函数 BigInt()方法创建BigInt类型的数据
const alsoHuge = BigInt(9007199254740991);
// ↪ 9007199254740991n
这些数据可以分为原始数据类型和引用数据类型:
基本数据类型(值类型):Number,String,Boolean,Undefined,Null、Symbol、BigInt
存放在栈中,占据空间小、大小固定,属于被频繁使用的数据,按值访问
引用数据类型(引用类型):数组、对象、函数
存放在堆中,占据空间大、大小不固定,栈内存中有一个地址指向堆内存中的对象。按地址访问。
其中 Symbol 和 BigInt 是 ES6 中新增的数据类型:
区别:
基础数据类型是值引用,存储在栈内存中,
引用数据类型是地址引用,存储在堆内存中。
1.1、js数据存储
先进先出
的原则先进后出
的原则1.2、执行上下文
当函数执行时,会创建一个执行上下文的环境,分为创建和执行两个阶段:
参考链接:https://juejin.cn/post/6890705692643196935
1.3、执行上下文栈
1.4、作用域与作用域链
作用域
全局作用域、函数作用域、ES6块级作用域。
作用域的最大用途
就是隔离变量或函数,并控制生命周期。
作用域在函数执行上下文创建时就定义好了,不是函数执行时定义的
作用域链
当一个块或函数嵌套在另一个块或函数中时,发生了作用域嵌套。在当前函数中如果无法找到某个变量,就会往上一级嵌套的作用域中去寻找,直到找到该变量或抵达全局作用域,这样的链式关系称为作用域链
变量与内存的关系
JS 引擎在读取变量时,先找到变量绑定的内存地址,然后找到地址所指向的内存空间,最后读取其中的内容。
当对变量进行重新赋值时,JS 引擎不会用新值覆盖之前旧值的内存空间(虽然从写代码的角度来看,确实像是被覆盖掉了),而**是重新 分配一个新的内存空间来存储新值,并将新的内存地址与变量进行绑定****,JS 引 擎会根据回收机制进行内存空间的回收。
const 定义常量后,变量名与内存地址之间建立了一种不可变的绑定关系,阻隔变量地址被改变,当 const 定义的变量进行重新赋值时,根据前面 的论述,JS 引擎会尝试重新分配新的内存空间,所以会被拒绝,便会抛出异常。
apply,call功能相同,但是传入的参数的形式不同;
apply的第二个参数是数组形式。eg:[arg1,arg2,arg3];
call的第二个参数是arg1,arg2,arg3这样的形式
浏览器事件模型中的过程主要分为三个阶段:捕获阶段、目标阶段、冒泡阶段
。
阻止事件传播
e.stopPropagation()
阻止冒泡和捕获阶段的传播。e.stopImmediatePropagation()
如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。事件模型包括:
IE是只支持冒泡事件模型的。
其是否会挂载到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
定义: 所谓的同源,指的是协议,域名,端口相同。
浏览器处于安 全方面的考虑,只允许本域名下的接口交互,
不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。
目的: 为了保证用户信息的安全,防止恶意的网站窃取数据。
同: 都是原始类型,判断时都为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
。
即 空对象.__proto__ = 构造函数.prototype
手写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
sessionid是一个浏览器与服务器会话的一个标识符,
浏览器第一次访问服务器会在服务器端生成一个session,同时也会生成唯一的sessionId与该Session对应,并返回sessionId到客户端;
当浏览器再次发送请求的时候,会将这个sessionId带上,服务器接受到请求之后就会依据sessionId找到相应的Session,从而识别用户。
var obj = {name: '01'};
var obj = new Object({name: '011'});
Object.create()中有两个参数, 第一个参数是这个新对象的原型。 第二个参数属性描述符对象
var obj = Object.create(Object.prototype) // 和{}和new Object()一样
var M = function(){this.name='o2'};
var obj = new M();
更多请参考链接:https://blog.csdn.net/yiyueqinghui/article/details/108726793
内置函数
Number、String、Boolean、Array、Object、Function、Date、RegExp、Error
内置对象
Math 、JSON
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
XML 指可扩展标记语言,被设计用来传输和存储数据。
二者区别:
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协议不同,它提供了双向的、可持久的数据流。通过在客户端和服务器之间保持连接,服务器的更新可以被及时推送给客户端,而不需要客户端以一定时间间隔去轮询。
构造两棵树,DOM 树和 CSS对象模型,
当浏览器接收到服务器相应来的 HTML 文档后,会遍历文档节点,生成 DOM 树,
CSS对象模型由浏览器解析 CSS 文件生成。
Flash适合处理多媒体、矢量图形;对CSS、处理文本上不足,不容易被搜索。
Ajax对CSS、文本支持很好,支持搜索;多媒体、矢量图形不足。
共同点:与服务器的无刷新传递消息、用户离/在线状态
事件处理机制:IE是事件冒泡,firefox同时支持捕获型事件和冒泡型事件。
阻止冒泡:ev.stopPropagation()
问题
在IE浏览器下,如果请求的方法是GET,并且请求的URL不变,那么这个请求的结果就会被缓存。
解决方法
这个问题的解决方法可以通过实时改变请求的URL,只要URL改变,就不会被缓存,可以通过在URL末尾添加上随机的时间戳参数
,eg:
open('GET','demo.php?rand=+Math.random()',true)
事件代理,又称之为事件委托,即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。
事件代理的原理是DOM元素的事件冒泡
。使用事件代理的好处是可以提高性能。
相同点:
都是循环遍历数组中的每一项
forEach和map方法里每次执行匿名函数都支持3个参数
,参数分别是item(当前每一项),index(索引值),arr(原数组)
只能遍历数组
都不会改变原数组
不同点:
map方法
1.map方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值
。
forEach方法
1.forEach无返回
,该方法用来调用数组的每个元素,将元素传给回调函数
文件断点续传是HTML5引入的新特性,HTML5的FILE api,有一个slice方法,可以将BLOB对象进行分割。前端通过FileList对象获取到相应的文件,按照指定的分割方式将大文件分段,然后一段一段地传给后端,后端再按顺序一段段将文件进行拼接。
目前比较常用的断点续传的方法
有两种,
一种是通过websocket接口进行文件上传,
另一种是通过ajax,
两种方法各有千秋,虽然websocket听起来比较高端些,但是除了用了不同的协议外其他的算法基本上都是很相似的,并且服务端要开启ws接口,这里用相对方便的ajax来说明断点上传的思路
断点续传最核心的内容
就是把文件“切片”然后再一片一片的传给服务器。
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
==:默认会进行一次类型转换,检测两个变量的值
是否相等,
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 对象
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
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 变量未定义
一般情况下的是通过分页优化
的,
如果不分页的话,那我就需要通过监听滚动条,来实现按需加载
,进而限制首次加载的列表数目。
什么是按需加载
当用户触发了动作时才加载对应的功能
。
触发的动作,包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更 改等。加载的文件,可以是 JS、图片、CSS、HTML 等。
iframe也称作嵌入式框架,嵌入式框架和框架网页类似,它可以把一个网页的框架和内容嵌入在现有的网页中。
提示:
<iframe></iframe>
之间,来提示某些不支持 iframe 的浏览器 缺点:优点:
缺点:
普通函数和构造函数的区别
经常说 get 请求参数的大小存在限制,而 post 请求的参数大小是无限制的.
实际上 Http 协议从未规定 GET/POST 的请求长度限制是多少,
由于get请求,是将参数拼接在访问地址url中,而浏览器或 web 服务器限制了 url 的长度
,因此,get请求参数的大小存在限制。
不同的浏览器和 WEB 服务器,限制的最大长度不一样,比如说 IE ,则最大长度为 2083byte ,若只支持 Chrome ,则最大长度 8182byte
Window.onload和 DOMContentloaded事件的先后顺序
顺序
一般情况下,DOMContentloaded事件要在 window.onload之前执行
,
当DOM树构建完成的时候就会执行 DOMContentloaded事件,
而 window.onload是在页面载入完成的时候,才执行。
区别
1.当 onload事件触发时,页面上所有的DOM、样式表、脚本、图片、 flash都已经加载完成了
。
2.当 DOMContentloaded事件触发时,仅为DOM加载完成
,不包括样式表、图片、f1ash等。
1.优点:事件驱动,输入输出性能很高
2.缺点:cpu计算能力差,比如做很多计算操作,代码运行效率要求高,所以用底层的语言来做,比如c
3.io密集型:需要处理比较多的任务
fetch 发送 post 请求的时候,总是发送 2 次,第一次状态码是 204,第二次才成功?
原因很简单,因为你用 fetch 的 post 请求的时候,导致 fetch
第一次发送了一个 Options 请求,询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的 请求。
强,协商缓存
缓存分为两种:强缓存和协商缓存,根据响应的 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
如何解决异步回调地狱
说说前端中的事件流
HTML 中与 javascript 交互是通过事件驱动来实现的,例如鼠标点击事件 onclick、页面 的滚动事件 onscroll 等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。 想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
什么是事件流:
事件流描述的是从页面中触发事件的顺序
,
DOM2 级事件流包括下面几个 阶段。
addEventListener
:
这个方法接收 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最 后这个布尔值参数如果是 true
,表示在捕获阶段调用事件处理程序;如果是 false
,表示 在冒泡阶段调用事件处理程序。
IE 只支持事件冒泡。
在 DOM 标准事件模型中,是先捕获后冒泡。
但是如果要实现先冒泡后捕获的效果,则对于同一个事件,分别监听捕获和冒泡,监听到捕获事件,先暂缓 执行,直到冒泡事件被捕获后再执行捕获事件。
是什么:
事件委托指的是,不在事件的发生地(直接 dom)上设置监听函数,而是在其父 元素上设置监听函数。
好处:
比较适合动态绑定的元素,新添加的子元素也会有监听函数,也可以有事件触发机制。
是什么
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。 常用场景,比如说页面列表较大时,可以只请求第一屏或前两屏的数据。当下拉浏览到时,再去请求相应的数据。
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。 懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
clientHeight
:表示的是可视区域的高度,不包含 border 和滚动条offsetHeight
:表示可视区域的高度,包含了 border 和滚动条scrollHeight
:表示了所有区域的高度,包含了因为滚动被隐藏的部分。clientTop
:表示边框 border 的厚度。scrollTop
:滚动后被隐藏的高度,获取对象相对于由 offsetParent 属性指定的父坐标(css 定位的元素或 body 元素)距离顶端的高度首先是三个事件,分别是 mousedown,mousemove,mouseup。
补充:也可以通过 html5 的拖放(Drag 和 drop)来实现
Ajax 解决浏览器缓存问题
Cache-Control: no-cache"
。或者在URL后面加一个值为当前时间戳的参数
$.ajaxSetup({cache:false})
即可它的功能是将对应的字符串解析成 JS 并执行
前端模块化就是复杂的文件编成一个一个独立的模块,比如 将一个JS 文件分割成一个个独立的模块,这样有利于重用(复用性)和维护(版本迭代)。
但是这样会引来模块之间相互依赖的问题, 所以有了 commonJS 规范,AMD,CMD 规范等等,以及用于 JS 打包的工具 webpack。
JS 监听对象属性的改变
Object.defineProperty
来实现已有属性的监听Object.defineProperty(user,'name',{
set:function(key,value){ }
})
缺点:如果为对象新增加一个属性id,需要重新指定对id属性的监听才行,否则不能监听 id 的变化。
Proxy
来实现var user = new Proxy({},{
set:function(target,key,value,receiver){ }
})
优点:这样即使有属性在 user 中不存在,后新增的属性,同样可以这样监听这个属性的 变化。
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())
利用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())
Object.is() ----- 判断两个值是否为同一个值。
实现一个两列等高布局
为了实现两列等高,
可以给每列加上 padding-bottom:9999px; margin-bottom:-9999px
;
同时父元素设置 overflow:hidden
;
判断方法:
原因可能是:
1.内存溢出问题。
2.资源过大问题。
3.资源加载问题。
4.canvas 绘制频率问题
解决办法:
1.针对内存溢出问题,我们应该在钢管离开可视区域后,销毁钢管,让垃圾收集器回收 钢管,因为不断生成的钢管不及时清理容易导致内存溢出游戏崩溃。
2.针对资源过大问题,我们应该选择图片文件大小更小的图片格式,比如使用 webp、png 格式的图片,因为绘制图片需要较大计算量。
3.针对资源加载问题,我们应该在可视区域之前就预加载好资源,如果在可视区域生成 钢管的话,用户的体验就认为钢管是卡顿后才生成的,不流畅。
4.针对 canvas 绘制频率问题,我们应该需要知道大部分显示器刷新频率为 60 次/s,因此 游戏的每一帧绘制间隔时间需要小于 1000/60=16.7ms,才能让用户觉得不卡顿。
有了解过事件模型吗,DOM 的分级是 什么?
JSDOM 事件流存在如下三个阶段:
JSDOM 事件流的触发的先后顺序为:先捕获再冒泡
,
点击 DOM 节点时,事件传播顺序:事件捕获阶段,从上往下传播,然后到达事件目标节点,最后是冒泡阶段,从下往上传播 。
DOM 节点添加事件监听方法 addEventListener,其中参数 capture 可以指定该监听是添加在 事件捕获阶段还是事件冒泡阶段,为 true 是事件捕获,为false 是事件冒泡,
,并非所有的事件都支持冒泡,比如 focus,blur 等等,我们可以通过 event.bubbles 来判断。
事件模型有三个常用方法:
setTimeout(fn,100);100 毫秒是如何权衡的
setTimeout()函数只是在指定的时间将事件插入了任务列表,如果任务列表中有任务的话,就必须等到它之前的任务都执行完,主线程才会去 执行它指定的回调函数,有可能要等很久,所以没有办法保证回调函数一定会在 setTimeout 指定的时间内执行,
指定的时间(100ms) 是将回调插入队列的时间,而非等待的时间
怎么获得对象上的属性
for(let I in obj)
该方法依次访问一个对象及其原型链中所有可枚举的类型object.keys
: 返回一个数组,包括所有可枚举的属性名称object.getOwnPropertyNames
: 返回一个数组包含不可枚举的属性介绍下 Set、Map、WeakSet 和 WeakMap 的区别
Set
——是一个集合(类似于数组),
WeakSet
——是一个对象的集合,与Set相比,
Map
——本质上是键值对的集合;
WeakMap
——只接受对象作为键名(null 除外),不接受其他类型的值作为键名;
ES5 的继承,实质上是先创建子类的实例对象,然后再将父类的方法添加到 this 上
(Parent.apply(this)).
ES5 的继承时,通过原型或构造函数机制来实现。
ES6 的继承机制完全不同,实质上是先创建父类的实例对象 this(所以必 须先调用父类的 super()方法),然后再用子类的构造函数修改 this
。
ES6 通过 class 关键字定义类,类之间通过 extends 关 键字实现继承。
子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因为子类没有自己的 this 对象,而是继承了父类的 this 对象,然后对其进行加工
。 如果不调用 super 方法,子类得不到 this 对象。
注意:super 关键字指代父类的实例,即父类的 this 对象。
注意:在子类构造函数中,调用 super 后,才可使用 this 关键字,否则 报错。
算法手写题
已知如下数组,编写一个程序将数组扁平化去并除其中重复部分数据,最终得 到一个升序且不重复的数组。
答: 使用 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()
方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块?
介绍下观察者模式和订阅-发布模式的区别,各自适 用于什么场景
cookie 和 token 都存放在 header 中,为什么不会 劫持 token?
document.cookie
直接拿到,通过 jwt+ip 的方式 可以防止被劫持,即使被劫持 也是无效的 jwt下面代码中 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);
}
实现一个 sleep 函数
比如 sleep(1000) 意味着等待 1000 毫秒,可从 Promise、Generator、Async/Await 等角度实现。
const sleep = (time) => {
return new Promise( resolve => {
setTimeout(resolve, time)
})
}
sleep(1000).then(() => {
// 这里写你的骚操作
})
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’ 前面
HTTPS 握手过程中,客户端如何验证证书的合法性
为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片?
实现 (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()代表本身的值
ES6 代码转成 ES5 代码的实现思路是什么
ES6 转 ES5 目前行业标配是用 Babel
,
转换的大致流程如下:
解析
:解析代码字符串,生成 AST( 抽象语法树 );转换
:按一定的规则转换、修改 AST;生成
:将修改后的 AST 转换成普通代码。array.forEach(function(currentValue, index, arr), thisValue)
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 属于复合数据类型,函数参数中以地址传递,修改值会影响到原始值, 但如果将其完全替换成另一个值,则原来的值不会受到影响
堆栈
,是一种线性表先进后出、后进先出
。先进先出,后进后出
。1.都是线性结构。
2.插入操作都是限定在表尾进行。
3.都可以通过顺序结构和链式结构实现。
4.插入与删除的时间复杂度与空间复杂度上两者均相同。
1.队列先进先出,栈先进后出。
2.删除数据元素的位置不同,栈的删除操作在表尾进行,队列的删除操作在表头进行。
3.顺序栈能够实现多栈空间共享,而顺序队列不能。
什么是SSE
严格地说,HTTP 协议无法做到服务器主动向客户端推送信息。但是,有一种方法,就是服务器向客户端声明,接下来要 发送的是一个数据流,而不是一次性的数据包,这样数据就会连续不断地发送过来,这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是如此。
其中数组、对象、null 都会被判断为 object,
其他判断都正确(即使symbol、bigint数据也可通过typeof 判断)
console.log(typeof Symbol()) // symbol
console.log(typeof 10937453n ) // bigint
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
constructor 有两个作用,
一是判断数据的类型,
二是对象实例通过constrcutor 属性访问它的构造函数。
所有对象都会从它的原型上继承一个 constructor 属性:
需要注意,如果创建一个对象来改变它的原型,constructor 就不能用来判断数据类型了:
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 方法。
const a = 'abc'
a.length; // 3
a.toUpperCase(); // "ABC"
因此,在 访 问 ‘abc’.length 时 , JavaScript 将 ‘abc’ 在 后 台 转 换 成String(‘abc’),然后再访问其 length 属性。
var a = 'abc'
Object(a) // String("abc")
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
,所以循环体中的内容不会运行
方法一:使用 JSON 自带的.stringify 方法来判断
方法二:使用 ES6 新增的方法 Object.keys()来判断
if(Object.keys(obj).length == 0){
console.log('空对象')
}
箭头函数是ES6中的提出来的,
所以不能 New 一个箭头函数
new 操作符的实现步骤如下:
1.创建一个空对象
2.将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的 prototype 属性
)
3.将构造函数中的 this 指向该对象
4.为这个对象添加属性和方法
5.返回新的对象
所以,上面的第二、三步,箭头函数都是没有办法执行的。
前端通过将一个符合 JSON 格式的数据结构序列化为JSON 字符串,然后将它传递到后端
,后端通过 JSON 格式的字符串解析后生成对应的数据结构,以此来实现前后端数据的一个传递。JSON.stringify()
、JSON.parse()
真正的答案是[1, NaN, NaN]
Array.prototype.map(callbackFn )
:
该方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。参数callbackFn 中有三个默认的参数
parseInt(string, radix)
解析一个字符串并返回指定基数的十进制整数,radix 是 2-36 之间的整数,表示被解析字符串的基数。
分析
所以,最终map函数返回的是一个数组,所以最后结果为[1, NaN, NaN]
==
中的隐式类型转换修改如下代码,使其打印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');
}
根据MDN上对Array.sort()的解释,默认的排序方法会将数组元素转换为字符串,然后比较字符串中字符的编码顺序
来进行排序。所以’102’ 会排在 ‘15’ 前面。
输出:[102, 15, 22, 29, 3, 8]
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}。
函数传参,如果参数是对象的话,传入的其实是对象的内存地址
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
这题考察的是对象的键名的转换。
对象的键名只能是字符串和 Symbol 类型
。测试一
// 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]);
输入到input框触发input事件
失去焦点后内容有改变触发change事件
识别到你开始使用中文输入法在打拼音时(此时input内还没有填入真正的内容) 触发compositionstart 事件
未输入结束但还在输入中触发compositionupdate事件
输入完成(也就是我们回车或者选择了对应的文字插入到输入框的时刻)触发 compositionend事件
。
简单来说就是切换中文输入法时在打拼音时(此时input内还没有填入真正的内容),会首先触发compositionstart,
然后每打一个拼音字母,触发compositionupdate,
最后将输入好的中文填入input中时触发compositionend。
触发compositionstart时,文本框会填入 “虚拟文本”(待确认文本),同时触发input事件;在触发compositionend时,就是填入实际内容后(已确认文本),所以这里如果不想触发input事件的话就得设置一个bool变量来控制。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。