JS常见问题

Q: JS有几种数据类型,其中基本数据类型有哪些!

7种!javascript

  • Boolean
  • Number
  • String
  • Null
  • Undefined
  • Object
  • Symbol (ECMAScript 6 新定义)

其中,除了Object是引用类型外,都是基本类型css

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------html

Q: null 和 undefined的区别

null是一个空指针,是一个特殊的object,会被前端

undefined是指一个被调用但未被赋值的变量html5

他们在if条件下都为falsejava

null 转为数字是0,而undefined转为数字是NaNwebpack

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ios

Q:垃圾收集

一、标记清除nginx

二、引用计数git

对于性能问题:若是在运行期间,分配的内存不少,那么垃圾收集的工做量也会至关大,所以,每一次垃圾收集的时间间隔如何肯定是一个很重要的问题。

从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 () {}是函数表达式,必须先声明,后使用。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

加法运算 +号会优先尝试字符链接;
减法运算 会尝试转化成数值型进行计算
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

宏任务:script(总体代码), setTimeout, setInterval, setImmediate的回调是宏任务, I/O, UI rendering。
微任务:Promises的回调是微任务(promise自己的函数是马上执行), Object.observe, MutationObserver
代码执行顺序是先宏任务,再微任务,若是还有则须要开始新一轮循环宏任务、微任务。
 
因此原代码执行顺序是:
1.宏任务 script,先弹出D,new建立promise对象时的EF,而后弹出H
2.微任务 promise,Promise对象执行完resolve()后.then的G
3.宏任务 setTimeout的C
即DEFHGC
 
2019/4/25补充:
1.准确的说是先执行宏任务,若是有回调就丢到宏任务队列。
2.再执行微任务,若是有回调就丢到微任务队列。
3.检查微任务队列,非空就执行。
4.检查宏任务队列,非空就执行。
5.渲染dom。
6.再来一次。
例题:

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策略

IE六、七、八、Safari会默认拦截第三方本地Cookie(Third-party Cookie)的发送。可是Firefox二、三、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 左右,跟随请求(请求头),会占用带宽资源,可是如果用来判断用户是否在线这些挺方便
  • sessionStoragelocalStorage大同小异,大小看浏览器支持,通常为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有两个做用:

  1. 使函数老是返回一个promise
  2. 许在这其中使用await

promise前面的await关键字可以使JavaScript等待,直到promise处理结束。而后:
若是它是一个错误,异常就产生了,就像在那个地方调用了throw error同样。

  1. 不然,它会返回一个结果,咱们能够将它分配给一个值
  2. 他们一块儿提供了一个很好的框架来编写易于读写的异步代码。

有了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

http://www.javashuo.com/article/p-qasukviu-dp.html

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的区别

  1. ==:等同,比较运算符,两边值类型不一样的时候,先进行类型转换,再比较;
  2. ===:恒等,严格比较运算符,不作类型转换,类型不一样就是不等;
  3. Object.is()ES6新增的用来比较两个值是否严格相等的方法,与===的行为基本一致。
    1. 先说= = =,这个比较简单,只须要利用下面的规则来判断两个值是否恒等就好了:

      1. 若是类型不一样,就不相等
      2. 若是两个都是数值,而且是同一个值,那么相等; 
        1. 值得注意的是,若是两个值中至少一个是NaN,那么不相等(判断一个值是不是NaN,能够用isNaN()Object.is()来判断)。
      3. 若是两个都是字符串,每一个位置的字符都同样,那么相等;不然不相等
      4. 若是两个值都是一样的Boolean值,那么相等
      5. 若是两个值都引用同一个对象或函数,那么相等,即两个对象的物理地址也必须保持一致;不然不相等
      6. 若是两个值都是null,或者都是undefined,那么相等

再说Object.is(),其行为与===基本一致,不过有两处不一样:

  1. +0不等于-0
  2. 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开销。

相关文章
相关标签/搜索