目录
Q: 给一个DOM添加捕获和冒泡的两种写法的事件点击,谁先执行?(如何阻止冒泡)
Q: 谈谈你对ajax 的理解,以及用原生 JS 实现有哪些要点需要注意;
13. js的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
26.如何实现一个私有变量,用getName方法可以访问,不能直接访
28.setTimeout、setInterval和requestAnimationFrame之间的区别
JS常见问题
Q: JS有几种数据类型,其中基本数据类型有哪些!
7种!
- Boolean
- Number
- String
- Null
- Undefined
- Object
- Symbol (ECMAScript 6 新定义)
其中,除了Object是引用类型外,都是基本类型
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q: null 和 undefined的区别
null是一个空指针,是一个特殊的object,会被
undefined是指一个被调用但未被赋值的变量
他们在if条件下都为false
null 转为数字是0,而undefined转为数字是NaN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q:垃圾收集
1、标记清除
2、引用计数
对于性能问题:如果在运行期间,分配的内存很多,那么垃圾收集的工作量也会相当大,因此,每一次垃圾收集的时间间隔如何确定是一个很重要的问题。
从IE7开始,js改变了垃圾收集的工作方式:触发垃圾收集的变量分配。类似于TCP拥塞窗口的控制。。。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q: 给一个DOM添加捕获和冒泡的两种写法的事件点击,谁先执行?(如何阻止冒泡)
先捕获,再冒泡
如上图所示,你点击了一个div里的text,会先从window捕获,直到text,然后从text冒泡,一直到冒泡到window
那么什么是捕获,什么是冒泡?
比如:
element.addEventListener('click', function(e){
console.log(e.target)
}, true|false);
其中,我们常用的false就是冒泡,true就是捕获。
return不仅阻止事件冒泡,也阻止事件本身,stopPropagation()只阻止事件冒泡
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q:相等运算符和严格相等运算符
1. 严格相等运算符‘===’
- 先比较两个值的类型,如不同,直接返回false
- 同一类型的原始类型的值(Number、string、boolean),如果相同就返回true,否则返回false
- 若是引用类型(比如,对象、数组),比较双方所指向的地址是否相等
{} === {} // false
[] === [] // false
(function () {} === function () {}) // false
上面代码分别比较两个空对象、两个空数组、两个空函数,结果都是不相等。
原因是对于复合类型的值,严格相等运算比较的是,它们是否引用同一个内存地址,而运算符两边的空对象、空数组、空函数的值,都存放在不同的内存地址,结果当然是false
。
注意:NaN与任何值都不相等,包括自身。
NaN === NaN // false
2.严格不相等运算符
a !== b 等价于
!(a === b)
3.相等运算符
- 若两个值类型相同,等价于严格相等
- 若类型不同,对于原始类型值之间的比较会先转换为数值类型
1 == true // true
// 等同于 1 === Number(true)
0 == false // true
// 等同于 0 === Number(false)
2 == true // false
// 等同于 2 === Number(true)
2 == false // false
// 等同于 2 === Number(false)
'true' == true // false
// 等同于 Number('true') === Number(true)
// 等同于 NaN === 1
'' == 0 // true
// 等同于 Number('') === 0
// 等同于 0 === 0
'' == false // true
// 等同于 Number('') === Number(false)
// 等同于 0 === 0
'1' == true // true
// 等同于 Number('1') === Number(true)
// 等同于 1 === 1
'\n 123 \t' == 123 // true
// 因为字符串转为数字时,省略前置和后置的空格
- 若类型不同,对于原始类型和引用类型之间,会把引用类型转换为原始类型
// 对象与数值比较时,对象转为数值
[1] == 1 // true
// 等同于 Number([1]) == 1
// 对象与字符串比较时,对象转为字符串
[1] == '1' // true
// 等同于 String([1]) == '1'
[1, 2] == '1,2' // true
// 等同于 String([1, 2]) == '1,2'
// 对象与布尔值比较时,两边都转为数值
[1] == true // true
// 等同于 Number([1]) == Number(true)
[2] == true // false
// 等同于 Number([2]) == Number(true)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q: 谈谈你对ajax 的理解,以及用原生 JS 实现有哪些要点需要注意;
ajax
全称是异步 javascript 和 XML
,用来和服务端进行数据交互的,让无刷新替换页面数据成了可能。
https://www.html5rocks.com/zh/tutorials/file/xhr2/#toc-examples
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest
这个东西,细节很多,短时间内想要基本熟悉很难。
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText);
}
}
xhr.open();
xhr.send();
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q: JS 实现一个闭包函数,每次调用都自增1
let add = (function () {
let i = 0;
return function () {
return ++i;
}
})()
可以这样理解这个闭包,add函数return的是一个匿名函数,因此,访问不到函数内的全局变量i,因此i被隐藏起来了。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q: JS 实现函数运行一秒后打印输出0-9
for(var i=0;i<10;i++) {
setTimeout((function (x) {
return function () {
console.log(x)
}
})(i), 1000)
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q:函数声明和函数表达式
其中,function meili () {}是函数声明,会有函数声明提升,全局可用。
function mogu () {}是函数表达式,必须先声明,后使用。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 - 2 - 3 - be - also - 4 - test。
Q:js中的this的理解
https://blog.csdn.net/cjgeng88/article/details/79846670
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q: 怎么解决跨域问题,有哪些方法
我一般用这三种,cors
,nginx反向代理
,jsonp
jsonp
: 单纯的 get 一些数据,局限性很大...就是利用script标签的src属性来实现跨域。nginx 反向代理
: 主要就是用了nginx.conf
内的proxy_pass http://xxx.xxx.xxx
,会把所有请求代理到那个域名,有利也有弊吧..cors
的话,可控性较强,需要前后端都设置,兼容性 IE10+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CSRF工具的防御手段
1. 尽量使用POST,限制GET
GET接口太容易被拿来做CSRF攻击,看第一个示例就知道,只要构造一个img标签,而img标签又是不能过滤的数据。接口最好限制为POST使用,GET则无效,降低攻击风险。
当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。
2. 浏览器Cookie策略
IE6、7、8、Safari会默认拦截第三方本地Cookie(Third-party Cookie)的发送。但是Firefox2、3、Opera、Chrome、Android等不会拦截,所以通过浏览器Cookie策略来防御CSRF攻击不靠谱,只能说是降低了风险。
PS:Cookie分为两种,Session Cookie(在浏览器关闭后,就会失效,保存到内存里),Third-party Cookie(即只有到了Exprie时间后才会失效的Cookie,这种Cookie会保存到本地)。
PS:另外如果网站返回HTTP头包含P3P Header,那么将允许浏览器发送第三方Cookie。
3. 加验证码
验证码,强制用户必须与应用进行交互,才能完成最终请求。在通常情况下,验证码能很好遏制CSRF攻击。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案。
4. Referer Check
Referer Check在Web最常见的应用就是“防止图片盗链”。同理,Referer Check也可以被用于检查请求是否来自合法的“源”(Referer值是否是指定页面,或者网站的域),如果都不是,那么就极可能是CSRF攻击。
但是因为服务器并不是什么时候都能取到Referer,所以也无法作为CSRF防御的主要手段。但是用Referer Check来监控CSRF攻击的发生,倒是一种可行的方法。
5. Anti CSRF Token
现在业界对CSRF的防御,一致的做法是使用一个Token(Anti CSRF Token)。
例子:
1. 用户访问某个表单页面。
2. 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie中。
3. 在页面表单附带上Token参数。
4. 用户提交请求后, 服务端验证表单中的Token是否与用户Session(或Cookies)中的Token一致,一致为合法请求,不是则非法请求。
这个Token的值必须是随机的,不可预测的。由于Token的存在,攻击者无法再构造一个带有合法Token的请求实施CSRF攻击。另外使用Token时应注意Token的保密性,尽量把敏感操作由GET改为POST,以form或AJAX形式提交,避免Token泄露。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Q: 描述下cookie
,sessionStorage
,localStorage
的差异..
cookie
: 大小4KB 左右,跟随请求(请求头),会占用带宽资源,但是若是用来判断用户是否在线这些挺方便sessionStorage
和localStorage
大同小异,大小看浏览器支持,一般为5MB,数据只保留在本地,不参与服务端交互.sessionStorage
的生存周期只限于会话中,关闭了储存的数据就没了.localStorage
则保留在本地,没有人为清除会一直保留
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q:promise
https://www.cnblogs.com/lvdabao/p/es6-promise-1.html
let p = new Promise(function(resolve, reject) {
setTimeout(function () {
console.log('执行完成');
resolve('hello');
}, 2000);
});
我new了一个Promise对象,先不管这里面的每句话是什么意思,这里注意,在new的同时,里面的函数已经被执行了,因此我们可以利用函数,让Promise延迟执行。
function dosomethingAsync () {
let p = new Promise(function(resolve, reject) {
setTimeout(function () {
console.log('执行完成');
resolve('hello');
}, 2000);
});
return p;
}
dosomethingAsync();
这里说一个概念,promise的状态,promise的状态分为三种,pending(等待),fulfilled(成功),rejected(失败)。一个promise只能从pending到另外两种状态之一转变,且这种转变是单向的,一次性的。也就是说,只能转变一次,且不可更改结果。
那么要怎么转变呢?就通过resolve(),和reject()。
比如我们定义,我们从数据库取年龄为100岁的人的信息,如果取到了,就是成功,如果没取到就是失败,我们写一下伪代码
1 function getSomeoneInfo () {
2 let p = new Promise(function (resolve, reject) {
3 axios.get('http://获取年龄的api/&age=100', function (response) {
4 // 接收响应的回调函数
5 if (response.data 不为空) {
6 // 成功了
7 resolve(response.data);
8 } else {
9 // 失败了
10 reject('error');
11 }
12 })
13 })
14 return p;
15 }
16
17 getSomeoneInfo ();
可以看到,Promise在还没有执行的时候状态为pending,在通过resolve和reject函数后分别转为两种对应的状态。
那么知道状态改变了以后,要怎么做呢?通过then
function getSomeoneInfo () {
let p = new Promise(function (resolve, reject) {
axios.get('http://获取年龄的api/&age=100', function (response) {
// 接收响应的回调函数
if (response.data 不为空) {
// 成功了
resolve(response.data);
} else {
// 失败了
reject('error');
}
})
})
return p;
}
getSomeoneInfo().then(function (data) {
console.log(data);
}, function (data) {
console.log(data);
});
可以看到then函数的参数是两个匿名函数,可以这么理解,用then来监听promise的状态变化,如果转向成功,就执行第一个函数,且参数data 和 response.data一致,否则转向第二个函数,data等于'error'。
promise.all()
同时执行多个异步函数,全部都执行完了,进到then里
Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
console.log(results);
});
result是三个函数执行结果组成的数组。
promise.race()
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Q:async / await
https://segmentfault.com/a/1190000013292562?utm_source=channel-newest
放在一个函数前的async有两个作用:
- 使函数总是返回一个promise
- 许在这其中使用await
promise前面的await关键字能够使JavaScript等待,直到promise处理结束。然后:
如果它是一个错误,异常就产生了,就像在那个地方调用了throw error一样。
- 否则,它会返回一个结果,我们可以将它分配给一个值
- 他们一起提供了一个很好的框架来编写易于读写的异步代码。
有了async/await,我们很少需要写promise.then/catch,但是我们仍然不应该忘记它们是基于promise的,因为有些时候(例如在最外面的范围内)我们不得不使用这些方法。Promise.all也是一个非常棒的东西,它能够同时等待很多任务。
举例
一个获取头像的需求,如果用promise只能以链式这么写,如果不以promise写,甚至会造成回调地狱
loadJson('/article/promise-chaining/user.json')
.then(user => loadGithubUser(user.name))
.then(showAvatar)
.then(githubUser => alert(`Finished showing ${githubUser.name}`));
如果用async / await
async function showAvatar() {
// read our JSON
let response = await fetch('/article/promise-chaining/user.json')
let user = await response.json()
// read github user
let githubResponse = await fetch(`https://api.github.com/users/${user.name}`)
let githubUser = await githubResponse.json()
// 展示头像
let img = document.createElement('img')
img.src = githubUser.avatar_url
img.className = 'promise-avatar-example'
documenmt.body.append(img)
// 等待3s
await new Promise((resolve, reject) => {
setTimeout(resolve, 3000)
})
img.remove()
return githubUser
}
showAvatar()
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
11. js的new操作符做了哪些事情
new 操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
13. js的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
-
clientHeight:表示的是可视区域的高度,不包含border和滚动条
-
offsetHeight:表示可视区域的高度,包含了border和滚动条
-
scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分。
-
clientTop:表示边框border的厚度,在未指定的情况下一般为0
-
scrollTop:滚动后被隐藏的高度,获取对象相对于由offsetParent属性指定的父坐标(css定位的元素或body元素)距离顶端的高度。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
17. js的节流和防抖
http://www.cnblogs.com/coco1s/p/5499469.html
针对高频度触发的事件(例如页面 scroll ,屏幕 resize,监听用户输入等)应该减少操作,下面介绍两种常用的解决方法,防抖和节流。
函数节流是指一定时间内js方法只跑一次。比如人的眨眼睛,就是一定时间内眨一次。这是函数节流最形象的解释。
函数防抖是指频繁触发的情况下,只在最终停止的时候,才执行代码一次。比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷卡了,司机才开车。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
20.如何理解前端模块化
前端模块化就是复杂的文件编程一个一个独立的模块,比如js文件等等,分成独立的模块有利于重用(复用性)和维护(版本迭代),这样会引来模块之间相互依赖的问题,所以有了commonJS规范,AMD,CMD规范等等,以及用于js打包(编译等处理)的工具webpack。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
21.Commonjs、 AMD和CMD
一个模块是能实现特定功能的文件,有了模块就可以方便的使用别人的代码,想要什么功能就能加载什么模块。
- Commonjs:开始于服务器端的模块化,同步定义的模块化,每个模块都是一个单独的作用域,模块输出,modules.exports,模块加载require()引入模块。
- AMD:中文名异步模块定义的意思。
requireJS实现了AMD规范,主要用于解决下述两个问题。
1.多个文件有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
2.加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应的时间越长。
语法:requireJS定义了一个函数define,它是全局变量,用来定义模块。
requireJS的例子:
//定义模块
define(['dependency'], function(){
var name = 'Byron';
function printName(){
console.log(name);
}
return {
printName: printName
};
});
//加载模块
require(['myModule'], function (my){
my.printName();
}
requirejs定义了一个函数define,它是全局变量,用来定义模块:
define(id?dependencies?,factory)
在页面上使用模块加载函数:
require([dependencies],factory);
总结AMD规范:require()函数在加载依赖函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块加载成功,才会去执行。
因为网页在加载js的时候会停止渲染,因此我们可以通过异步的方式去加载js,而如果需要依赖某些,也是异步去依赖,依赖后再执行某些方法。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
22.对象深度克隆的简单实现
function deepClone(obj){
var newObj= obj instanceof Array ? []:{};
for(var item in obj){
var temple= typeof obj[item] == 'object' ? deepClone(obj[item]):obj[item];
newObj[item] = temple;
}
return newObj;
}
ES5的常用的对象克隆的一种方式。注意数组是对象,但是跟对象又有一定区别,所以我们一开始判断了一些类型,决定newObj是对象还是数组~
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
24.将原生的ajax封装成promise
var myNewAjax=function(url){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
xhr.open('get',url);
xhr.send(data);
xhr.onreadystatechange=function(){
if(xhr.status==200&&readyState==4){
var json=JSON.parse(xhr.responseText);
resolve(json)
}else if(xhr.readyState==4&&xhr.status!=200){
reject('error');
}
}
})
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
补:defineProperty
https://segmentfault.com/a/1190000007434923
value:对应的值,默认为undefined。
writable:是否可以被重写。默认为false。用内部方法也不可被修改
enumeable:此属性是否可以被枚举到(注意这个说法,“被枚举到”,当你用for in或object.keys()的时候能不能被枚举出来)。设置为true可以被枚举;设置为false,不能被枚举。默认为false。
configurable:是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)。设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
25.js监听对象属性的改变
我们假设这里有一个user对象,
(1)在ES5中可以通过Object.defineProperty来实现已有属性的监听
Object.defineProperty(user,'name',{
set:function(key,value){
}
})
缺点:如果id不在user对象中,则不能监听id的变化
(2)在ES6中可以通过Proxy来实现
var user = new Proxy({},{
set:function(target,key,value,receiver){
}
})
这样即使有属性在user中不存在,通过user.id来定义也同样可以这样监听这个属性的变化哦~
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
26.如何实现一个私有变量,用getName方法可以访问,不能直接访
(1)通过defineProperty来实现(这个我测了,是不行的!!!反而可以用上面的proxy实现)
obj={
name:yuxiaoliang,
getName:function(){
return this.name
}
}
object.defineProperty(obj,"name",{
//不可枚举不可配置
});
(2)通过函数的创建形式
function product(){
var name='yuxiaoliang';
this.getName=function(){
return name;
}
}
var obj=new product();
27. ==和===、以及Object.is的区别
==
:等同,比较运算符,两边值类型不同的时候,先进行类型转换,再比较;===
:恒等,严格比较运算符,不做类型转换,类型不同就是不等;Object.is()
是ES6新增的用来比较两个值是否严格相等的方法,与===
的行为基本一致。-
-
先说
= = =
,这个比较简单,只需要利用下面的规则来判断两个值是否恒等就行了:- 如果类型不同,就不相等
- 如果两个都是数值,并且是同一个值,那么相等;
- 值得注意的是,如果两个值中至少一个是NaN,那么不相等(判断一个值是否是NaN,可以用
isNaN()
或Object.is()
来判断)。
- 值得注意的是,如果两个值中至少一个是NaN,那么不相等(判断一个值是否是NaN,可以用
- 如果两个都是字符串,每个位置的字符都一样,那么相等;否则不相等。
- 如果两个值都是同样的Boolean值,那么相等。
- 如果两个值都引用同一个对象或函数,那么相等,即两个对象的物理地址也必须保持一致;否则不相等。
- 如果两个值都是null,或者都是undefined,那么相等。
-
再说Object.is()
,其行为与===
基本一致,不过有两处不同:
- +0不等于-0。
- NaN等于自身。
举个栗子☺:
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
28.setTimeout、setInterval和requestAnimationFrame之间的区别
这里有一篇文章讲的是requestAnimationFrame:http://www.cnblogs.com/xiaohuochai/p/5777186.html
与setTimeout和setInterval不同,requestAnimationFrame不需要设置时间间隔,
大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms。
RAF采用的是系统时间间隔,不会因为前面的任务,不会影响RAF,但是如果前面的任务多的话,
会响应setTimeout和setInterval真正运行时的时间间隔。
特点:
(1)requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率。
(2)在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量
(3)requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销。