JS常见问题

目录

 

JS常见问题

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

Q: null 和 undefined的区别

Q:垃圾收集

Q: 给一个DOM添加捕获和冒泡的两种写法的事件点击,谁先执行?(如何阻止冒泡)

Q:相等运算符和严格相等运算符

Q: 谈谈你对ajax 的理解,以及用原生 JS 实现有哪些要点需要注意;

Q: JS 实现一个闭包函数,每次调用都自增1

Q: JS 实现函数运行一秒后打印输出0-9

Q:函数声明和函数表达式

 Q:js中的this的理解

Q: 怎么解决跨域问题,有哪些方法

Q: 描述下cookie,sessionStorage,localStorage的差异..

Q:promise

Q:async / await

11. js的new操作符做了哪些事情

13. js的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?

17. js的节流和防抖

20.如何理解前端模块化

21.Commonjs、 AMD和CMD

22.对象深度克隆的简单实现

24.将原生的ajax封装成promise

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

补:defineProperty

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

25.js监听对象属性的改变

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

26.如何实现一个私有变量,用getName方法可以访问,不能直接访

27. ==和===、以及Object.is的区别

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

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

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

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

宏任务: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策略

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 左右,跟随请求(请求头),会占用带宽资源,但是若是用来判断用户是否在线这些挺方便
  • 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

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

  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开销。

 
 
 
 
 
 
 
posted @ 2019-04-09 21:41 swallowblank 阅读(...) 评论(...) 编辑 收藏
发布了113 篇原创文章 · 获赞 6 · 访问量 7140

猜你喜欢

转载自blog.csdn.net/swallowblank/article/details/90703030