前端三剑客学习笔记(Update)

HTML

1. src和href的区别

  • 共同点:src和href都是用来引用外部的资源

  • 区别如下:

  • src: 表示对资源的引用,它指向的内容会嵌入到当前标签所在的位置。src会将其指向的资源下载并应用到文档内,如请求js脚本。当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,所以一般js脚本会放在页面底部。

  • href: 表示超文本引用,它指向一些网络资源,建立和当前元素或本文档的链接关系。当浏览器识别到它他指向的文件时,就会下载资源,不会停止对当前文档的处理。 常用在a、link等标签上。

2. 对HTML语义化的理解

语义化是指根据内容的结构化(内容语义化),选择合适的标签(代码语义化)。通俗来讲就是用正确的标签做正确的事情。(HTML5的特点)
语义化的优点如下:

  • 对机器友好,带有语义的文字表现力丰富,更适合搜索引擎的爬虫爬取有效信息,有利于SEO。除此之外,语义类还支持读屏软件,根据文章可以自动生成目录;(利于搜索引擎的爬虫,利于SEO)
  • 对开发者友好,使用语义类标签增强了可读性,结构更加清晰,开发者能清晰的看出网页的结构,便于团队的开发与维护。(标签易懂)

常见的语义化标签:

<header></header>  头部

<nav></nav>  导航栏

<section></section>  区块(有语义化的div)

<main></main>  主要区域

<article></article>  主要内容

<aside></aside>  侧边栏

<footer></footer>  底部

3. DOCTYPE(文档类型) 的作用

DOCTYPE是HTML5中一种标准通用标记语言的文档类型声明,它的目的是告诉浏览器(解析器)应该以什么样(html或xhtml)的文档类型定义来解析文档,不同的渲染模式会影响浏览器对 CSS 代码甚至是JavaScript 脚本的解析。

4. 常用的meta标签有哪些

meta 标签由 name 和 content 属性定义,用来描述网页文档的属性,比如网页的作者,网页描述,关键词等,除了HTTP标准固定了一些name作为大家使用的共识,开发者还可以自定义name。
常用的meta标签:
(1)charset,用来描述HTML文档的编码类型:

(2) keywords,页面关键词:

(3)description,页面描述:

1、Keywords (关键字)    说明:为搜索引擎提供的关键字列表    用法:    注意:各关键词间用英文逗号“,”隔开。META的通常用处是指定搜索引擎用来提高搜索质量的关键词。当数个META元素提供文档语言从属信息时,搜索引擎会使用lang特性来过滤并通过用户的语言优先参照来显示搜索结果。

<Meta name="Kyewords" Lang="EN" Content="vacation,greece,sunshine">   <Meta name="Kyewords" Lang="FR" Content="vacances,grè:ce,soleil">

2、Description (简介)
   说明:Description用来告诉搜索引擎你的网站主要内容。
   用法:<Meta name="Description" Content="你网页的简述">
   注意:
4)refresh,页面重定向和刷新:

<meta http-equiv="refresh" content="0;url=" />

当某个页面需要自动跳转的时候就要用到这个代码,比如一般的网站广告页面打开几秒后自动跳转到另外一个页面去就是用这个代码实现的(当然用js也是可以实现的)

5.媒体标签

audio:音频
<audio src='' controls autoplay loop='true'></audio>

属性:

controls 控制面板
autoplay 自动播放
loop=‘true’ 循环播放

(2)video视频
<video src='' poster='imgs/aa.jpg' controls></video>

属性:

poster:指定视频还没有完全下载完毕,或者用户还没有点击播放前显示的封面。默认显示当前视频文件的第一针画面,当然通过poster也可以自己指定。
controls 控制面板
width
height

(3)source标签
因为浏览器对视频格式支持程度不一样,为了能够兼容不同的浏览器,可以通过source来指定视频源。

<video>
 	<source src='aa.flv' type='video/flv'></source>
 	<source src='aa.mp4' type='video/mp4'></source>
</video>

6. 其他

  • 拖放:拖放是一种常见的特性,即抓取对象以后拖到另一个位置。设置元素可拖放:

    <img src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.zhimg.com%2Fv2-6125bb539c57efde89cfc16ad0bdcba0_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic1.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1652606059&t=60e17cd2e5d8d5ecbfa64cd76b58dc8d" draggable="true" />

  • 画布(canvas ): canvas 元素使用 JavaScript 在网页上绘制图像。画布是一个矩形区域,可以控制其每一像素。canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。

    <canvas id="myCanvas" width="200" height="100"></canvas>

  • SVG:SVG 指可伸缩矢量图形,用于定义用于网络的基于矢量的图形,使用 XML 格式定义图形,图像在放大或改变尺寸的情况下其图形质量不会有损失,它是万维网联盟的标准
    地理定位:Geolocation(地理定位)用于定位用户的位置。‘

7. iframe 有那些优点和缺点?

iframe 元素会创建包含另外一个文档的内联框架(即行内框架)。
优点:

  • 用来加载速度较慢的内容(如广告)
  • 可以使脚本可以并行下载
  • 可以实现跨子域通信

缺点:

  • iframe 会阻塞主页面的 onload 事件
  • 无法被一些搜索引擎索识别
  • 会产生很多页面,不容易管理

8.文档声明(Doctype)和<!Doctype html>有何作用? 严格模式与混杂模式如何区分?它们有何意义?

文档声明的作用: 文档声明是为了告诉浏览器,当前HTML文档使用什么版本的HTML来写的,这样浏览器才能按照声明的版本来正确的解析。
的作用:<!doctype html> 的作用就是让浏览器进入标准模式,使用最新的 HTML5 标准来解析渲染页面;如果不写,浏览器就会进入混杂模式,我们需要避免此类情况发生。
严格模式与混杂模式的区分:

严格模式: 又称为标准模式,指浏览器按照W3C标准解析代码;
混杂模式: 又称怪异模式、兼容模式,是指浏览器用自己的方式解析代码。混杂模式通常模拟老式浏览器的行为,以防止老站点无法工作;

区分:网页中的DTD,直接影响到使用的是严格模式还是浏览模式,可以说DTD的使用与这两种方式的区别息息相关。

如果文档包含严格的DOCTYPE ,那么它一般以严格模式呈现(严格 DTD ——严格模式);
包含过渡 DTD 和 URI 的 DOCTYPE ,也以严格模式呈现,但有过渡 DTD 而没有 URI (统一资源标识符,就是声明最后的地址)会导致页面以混杂模式呈现(有 URI 的过渡 DTD ——严格模式;没有 URI 的过渡 DTD ——混杂模式);
DOCTYPE 不存在或形式不正确会导致文档以混杂模式呈现(DTD不存在或者格式不正确——混杂模式);
HTML5 没有 DTD ,因此也就没有严格模式与混杂模式的区别,HTML5 有相对宽松的 法,实现时,已经尽可能大的实现了向后兼容(HTML5 没有严格和混杂之分)。

总之,严格模式让各个浏览器统一执行一套规范兼容模式保证了旧网站的正常运行。

CSS

1. CSS选择器及其优先级

在这里插入图片描述

对于选择器的优先级:

  • 继承得到的样式的优先级最低;
  • 标签选择器、伪元素选择器:1
  • 类选择器、伪类选择器、属性选择器:10
  • id 选择器:100
  • 内联样式:1000
  • !important:最高

2. CSS中可继承与不可继承属性有哪些

一、无继承性的属性

display:规定元素应该生成的框的类型
文本属性:
vertical-align:垂直文本对齐
text-decoration:规定添加到文本的装饰
text-shadow:文本阴影效果
white-space:空白符的处理
unicode-bidi:设置文本的方向

盒子模型的属性:width、height、margin、border、padding
背景属性:background、background-color、background-image、background-repeat、background-position、background-attachment
定位属性:float、clear、position、top、right、bottom、left、min-width、min-height、max-width、max-height、overflow、clip、z-index
生成内容属性:content、counter-reset、counter-increment
轮廓样式属性:outline-style、outline-width、outline-color、outline
页面样式属性:size、page-break-before、page-break-after
声音样式属性:pause-before、pause-after、pause、cue-before、cue-after、cue、play-during

二、有继承性的属性

字体系列属性
font-family:字体系列
font-weight:字体的粗细
font-size:字体的大小
font-style:字体的风格

文本系列属性
text-indent:文本缩进
text-align:文本水平对齐
line-height:行高
word-spacing:单词之间的间距
letter-spacing:中文或者字母之间的间距
text-transform:控制文本大小写(就是uppercase、lowercase、capitalize这三个)
color:文本颜色

元素可见性
visibility:控制元素显示隐藏

列表布局属性
list-style:列表风格,包括list-style-type、list-style-image等

光标属性
cursor:光标显示为何种形态

JavaScript

数据类型

image-20220215002905263

**null表示"没有对象",即该处不应该有值。**典型用法是:

(1) 作为函数的参数,表示该函数的参数不是对象。

(2) 作为对象原型链的终点。

Object.getPrototypeOf(Object.prototype)
// null

**undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。**典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

var i;
i // undefined

function f(x){console.log(x)}
f() // undefined

var  o = new Object();
o.p // undefined

var x = f();
x // undefined

image-20220215004056358

image-20220215010655171

image-20220215011050357

image-20220215011627588

个人心得:内存里面有堆和栈,变量放在栈中,引用类型的数据放在堆内存之中,

image-20220217161118794

image-20220217161511763

函数传值

问题:js调用函数是传递的是?

fn(a)括号里面的a会赋值给fn{}作用域里面的a相当于 var a=a

var a = 3;
function fn(a) {undefined
a = a + 1;
console.log(a);//4
}
fn(a);
console.log(a);//3
//执行到:fn(a);先读到a是3,把a的值传给形参a,形参a去加1,输出为4。实参a还是实参a,所以最后输出a仍然是3。这里是基本值传递。

function fn2(obj) {undefined
console.log(obj.name);//tom
}
var obj = {name:‘tom’};
fn2(obj);
//执行到:fn2(obj);把obj中的内容(不是把{name:‘tom’}传递给形参obj,是把obj的地址值传给形参obj,只是地址值中是 {name:‘tom’})传递给形参obj。这里是地址值传递。

image-20220217162050099

var obj={}
    function jjj(){
      console.log(11);
      console.log(this); //obj
    }
    jjj.call(obj)

image-20220217163052229

image-20220217165050543

回调函数就是你自己定义的,但是你没有自己去调用他就最终执行了(调用就是根据他的函数名字进行调用)

IIFE(立即执行函数)

image-20220217191451635

 (
      function () {
        var a = 1;
        function test() {
          console.log(a);
        }
        window.$ = function () {
          return {
            test:test
          }
        }
        console.log(window.$());
      }
    )()

this

image-20220217200633689

function Person(color){
      console.log(this);
      this.color=color;
      this.getColor=function(){
        console.log(this);
        return this.color
      }
      this.setColor=function(color){
        console.log(this);
        this.color=color
      }
    }
    //  Person('red') // window
    let p=new Person('blue'); //p
    var obj={}
    p.setColor.call(obj,"yellow")  //this就是obj,
    // call可以改变this指向,相当于obj.setColor
    // 就是在一个对象里面调用不在对象里面的方法
    p.setColor.call(obj) //obj

    function fun1(){
      function fun2(){
        console.log(this);
      }
      fun2() //window
    }
    fun1() //window

原型链

image-20220217205100307

显与隐式原型都是保存地址值

image-20220217210055416

当创建对象的时候,会在里面运行这个语句

image-20220217210532940

Object对象

image-20220218140407116

 // 用构造函数
    console.log(Object); //object在这里已经存在了
    console.log(Object.prototype);
    function fn(name){
      this.name=name,
      this.test1=function(){
        console.log(this.name)
      }
    }

    fn.prototype.test2=function(){
      console.log("test2");
    }

    let p=new fn('lqc')
    console.log(fn.prototype);
    p.test1()
    p.test2()
    console.log(fn.prototype===p.__proto__);

image-20220218140240933

个人心得:不管是自己创建多少个object类型或者是function型,他们的最终都会找到他们的原型对象.例如:在创建构造函数的时候,传入的this.prototype={}这个Object字面量对象,那么他的隐式原型就会找会object的原型对象了,可以更好理解下面0*345的地址

image-20220218140407116

原型链图片1:这个是实例的图片

原型链1

原型链2

  • 函数的显示原型都是Object空实例,是在创建构造函数的时候创建的Object空实例,他们都指向Object.prototype
  • var Foo=new Function()
  • Function=new Function()
  • 所有函数的隐式原型就是Function.prototype
  • 需要注意实例与函数的原型路线

image-20220221104237826

image-20220221105518583

instanceof原理

//假设instanceof运算符左边是L,右边是R.L是一个实例
L instanceof R 
//instanceof运算时,通过判断L的原型链上是否存在R.prototype
L.__proto__.__proto__ ..... === R.prototype ?
//如果存在返回true 否则返回false

image-20220221123820560

原型链面试题

image-20220221125633060

答案是:1,undfind,2,3

因为创建实例的时候,this.__ proto __=构造函数的显示原型,但是后面的显示原型地址改变了

image-20220221163318104

答案:a() 未定义, a() ,b()

变量提升

image-20220221163653810

image-20220221163833038

fn3是一个变量名,不是函数名.只会提升变量,不会提升函数

执行上下文

全局执行上下文

image-20220221170436920

函数执行上下文

image-20220221170426431

image-20220221201130773

a,bar,foo是全局执行上下文,遇到函数调用就压栈,等到函数执行完就出栈,全局执行只有一个,公式就是n+1

面试题1:

image-20220221204820165

答案:这个需要注意,给js的传值基本数据类型传递的是值,而不是引用数据类.每个函数里面的作用域是不一样的

image-20220221205636170

面试题2:

function fn(){
    }
 var fn;
 console.log(fn);//function fn()

考察变量和函数提升谁先提升.答案是变量的优先级更高,而且变量被覆盖了

面试题3:

if (!(b in window)){
    var b=1
  }
  console.log(b);//undefined

面试题4:(易错)

var c = 1;
function c(c) {
    console.log(c);
}
c(2) //报错

ps:变量提升要看上面有没有输出语句或者函数等等,只要提升了

作用域与 作用域链

image-20220221221106539

es5只有全局作用域还有函数作用域,es6多了块级作用域

image-20220221221256352

面试题1:

 var x = 10;
    function fn() {
      console.log(x);
    }
    function show(f) {
      var x = 20;
      f();
    }
    show(fn) //10

var f=fn是赋值地址,所以fn()的位置是不变的,不会受到f()的影响

Object创建对象的方式

方式一:

let p=new Object()
   p.name='Tom'
   p.age=12
   p.setName=function(name){
     this.name=name
   }

方式二:字面量

image-20220221225621093

方式三:工厂函数

function createPerson(name,age){
      var obj={
        name:name,
        age:age,
        setName:function(name){
          this.name=name
        }
      }
        return obj
    }
    var p=createPerson('lqc',21)
    console.log(p);

定时器

image-20220223143049999

如果是多线程的,两个线程中,对同一个节点p,一个线程对节点P进行删除,另一个对节点p进行文本更新,那么会导致报错!

 //  var start=Date.now()
    //   setTimeout(()=>{
    //     console.log(Date.now()-start);

    //   },1000)
    // 定时器的回调函数执行要等到js主线程执行完

    setTimeout(() => {
      console.log("1");
      // alert()
    }, 1000)
    setTimeout(() => {
      console.log("2");
    }, 2000)
    setTimeout(() => {
      console.log("3");
    }, 0)
    function fn(){
      console.log('fn');
    }
    fn()

    console.log('之前');
    alert('alert') //alert会暂停主线程
    console.log('之后');
    // settimout的回调要到主线程执行

要知道异步执行原理,就先要了解同步执行。因为计算机程序执行分为同步执行和异步执行。

所谓的同步执行,就是正常的计算机执行的顺序流程:

1.顺序控制语句  从上至下  从左至右

2.分支控制语句  if  switch

3.循环控制语句  for  while  do…while  for…in  forEach()

所谓的异步执行(回调函数),是一种特殊的程序的执行方式:

1.setInterval  setTimeout

2.事件的绑定  onclick…

3.ajax请求

所谓异步程序的执行:

  1,所有的异步程序的执行,都会在同步程序执行结束之后,再来执行。

2,异步程序的执行顺序,如果时间相同,看代码的先后顺序,如果时间不同,时间短的,先执行。

image-20220223145234548

js分为同步任务和异步任务,异步任务分为宏任务和微任务

image-20220222163859659

Object

Object.keys方法

Object.keys方法是JavaScript中用于遍历对象属性的一个方法 。它传入的参数是一个对象,返回的是一个数组,数组中包含的是该对象所有的属性名。
如:

var cat= { 
name:’mini’, 
age:2, 
color:’yellow’, 
desc:”cute” 
}
console.log(Object.keys(cat)); // ["name", "age", "color", "desc"]

这里有一道关于Object.keys的题目

输出对象中值大于2的key的数组

var data = {a: 1, b: 2, c: 3, d: 4};
Object.keys(data).filter(function(x) { return 1 ;})

期待输出:[“c”,”d”]
请问1处填什么?

正确答案:1 :data[x]>2

Object.keys是es5中新增的方法,用来获取对象自身所有的可枚举的属性名,但不包括原型中的属性,然后返回一个由属性名组成的数组。注意它同for…in一样不能保证属性按对象原来的顺序输出。
Object.getOwnPropertyNames也是es5中新增的方法,返回对象的所有自身属性的属性名(包括不可枚举的属性)组成的数组,但不会获取原型链上的属性。

Array.filter(function)对数组进行过滤返回符合条件的数组。

Object.values()方法

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历( enumerable )属性的键值。

var obj = { foo: "bar", baz: 42 };  
Object.values(obj)  
// ["bar", 42]  

返回数组的成员顺序,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b、c、a。Object.values只返回对象自身的可遍历属性。

var obj = { 100: 'a', 2: 'b', 7: 'c' };  
Object.values(obj)  
// ["b", "c", "a"]  

如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。

Object.values('foo')  
// ['f', 'o', 'o']  

上面代码中,字符串会先转成一个类似数组的对象。字符串的每个字符,就是该对象的一个属性。因此,Object.values返回每个属性的键值,就是各个字符组成的一个数组。
如果参数不是对象,Object.values会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values会返回空数组。

Object.create()

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
语法
Object.create(proto, [propertiesObject])
参数
proto
新创建对象的原型对象。
propertiesObject
可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。
返回值
一个新对象,带着指定的原型对象和属性。
如:

var parent = {
    x : 1,
    y : 1
}
var child = Object.create(parent,{
    z : {                           // z会成为创建对象的属性
        writable:true,
        configurable:true,
        value: "newAdd"
    }
});
console.log(child)//{z: "newAdd"}z: "newAdd"__proto__: x: 1y: 1__proto__: Object

Object.create()创建继承

function A(){
  this.a = 1;
  this.b = 2;
}
A.prototype.drive = function(){
  console.log('drivvvvvvvvvv');
}
//方式1
function B(){}
B.prototype = Object.create(new A()); //这里采用了new 一个实例
//方式2
function C(){
  A.call(this);
}
C.prototype = Object.create(A.prototype) //这里使用的是父类的原型

以上两种方式有什么区别?
1的缺点:
执行了 new,相当于运行了一遍 A ,如果在 A 里做了一些其它事情(如改变全局变量)就会有副作用。
用 A 创建的对象做原型,里面可能会有一些冗余的属性。
2模拟了 new 的执行过程

Object.hasOwnProperty()方法

判断对象自身属性中是否具有指定的属性。
obj.hasOwnProperty('name')
在某个对象是否拥有某个属性,判断的方法有很多,常用的方法就是object.hasOwnProperty(‘×××’),这个方法是不包括对象原型链上的方法的

var obj = {
    name:'fei'
}
    console.log(obj.hasOwnProperty('name'))//true
    console.log(obj.hasOwnProperty('toString'))//false

以上,obj对象存在的name属性的时候,调用这个方法才是返回true,我们知道其实每个对象实例的原型链上存在toString方法,在这里打印false,说明这个方法只是表明实例对象的属性,不包括原型链上的属性。

Object.getOwnPropertyNames()方法

Object.getOwnPropertyNames()方法返回对象的所有自身属性的属性名(包括不可枚举的属性)组成的数组,但不会获取原型链上的属性。

function A(a,aa) {
  this.a = a;
  this.aa = aa;
  this.getA = function() {
    return this.a;
  }
}
// 原型方法
A.prototype.aaa = function () {};

var B = new A('b', 'bb');
B.myMethodA = function() {};
// 不可枚举方法
Object.defineProperty(B, 'myMethodB', {
  enumerable: false,
  value: function() {}
});

Object.getOwnPropertyNames(B); // ["a", "aa", "getA", "myMethodA", "myMethodB"]

Object.getOwnPropertyNames和Object.keysq区别

Object.getOwnPropertyNames和Object.keys的区别,即Object.keys只适用于可枚举的属性,而Object.getOwnPropertyNames返回对象自动的全部属性名称。

'use strict';
(function(){
    if(!Object.getOwnPropertyNames){
        console.log('浏览器不支持getOwnPropertyNames');
        return;
    }

    //人类的构造函数
    var person = function(name, age, sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
        
        this.sing = function(){
            console.log('sing');
        }
    }
    //new 一个ladygaga
    var gaga = new person('ladygaga', 26, 'girl');

    //给嘎嘎发放一个不可枚举的身份证
    Object.defineProperty(gaga, 'id', {
        value : '1234567890',
        enumerable : false
    });

    //查看gaga的个人信息
    var arr = Object.getOwnPropertyNames(gaga);
    document.write(arr); //output: name,age,sex,sing,id

    document.write('</br>');
    
   //注意和getOwnPropertyNames的区别,不可枚举的id没有输出
    var arr1 = Object.keys(gaga);
    document.write(arr1); //output: name,age,sex,sing
})();

包装类

// 1.包装类和基本数据类型.基本数据类型遇到调用方法会包装成包装类,才可以调用方法
			let num=123;
			console.log(typeof num.toString());
			let str="hello lqc"
			console.log(str.split(" "))
			console.log(str.length);  //系统会自动把str变成new String(str)
		// 2. 包装类转换后,立即销毁
			str.name="op" //new String("op") 在这里创建并且添加数据,但是立马进行销毁
			console.log(str.name); //在在上一行已经销毁了,所以找不到了,显示undefined
			
			// 总结:Js中,我们不太需要使用包装类,系统会自动帮我们进行包装

Load 和 DOMContentLoaded 区别

Load 事件触发代表⻚⾯中的 DOM,CSS,JS,图⽚已经全部加载完毕。
DOMContentLoaded 事件触发代表初始的 HTML 被完全加载和解析,不需要等待 CSS,
JS,图⽚加载。

Event Loop

1.定义: Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
2. 众所周知 JS 是阻塞单线程语言,因为在最初 JS 就是为了和浏览器交互诞生的。如果
JS 是多线程的语言的话,我们在多个线程中处理 DOM 就可能会发⽣问题(一个线程中新加
节点,另一个线程中删除节点),当然可以引用读写锁解决这个问题。
JS 在执行的过程中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异
步的代码,会被挂起并加⼊到 Task(有多种 task) 队列中。一旦执行栈为空,Event Loop
就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步
还是同步。
以上代码虽然 setTimeout 延时为 0,其实还是异步。这是因为 HTML5 标准规定这个函数
第2个参数不得少于 4 毫秒,不足会自动增加。所以 setTimeout 还是会在 script end
之后打印。
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask) 和 宏任
务(macrotask)。在 ES6 规范中,microtask 称为 jobs ,macrotask 称为 task 。

console.log('script start');
setTimeout(function() {
 console.log('setTimeout');
}, 0);
new Promise((resolve) => {
 console.log('Promise')
 resolve()
 }).then(function() {
 console.log('promise1');
}).then(function() {
 console.log('promise2');
});
console.log('script end');

// script start => Promise => script end => promise1 => promise2 => setTimeout

以上代码虽然 setTimeout 写在 Promise 之前,但是因为 Promise的then回调函数是属于微任务
setTimeout 属于宏任务,所以会有以上的打印。
微任务包括 process.nextTick , promise then, Object.observe , MutationObserver
宏任务包括 script , setTimeout , setInterval , setImmediate , I/O文件读写 , UI
rendering
很多有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script,
浏览器会先执一个宏任务,接下来有异步代码的话就先执行微任务。
所以正确的一次 Event loop 顺序是这样的

  1. 执行同步代码,这属于宏任务
  2. 执行栈为空,查询是否有微任务需要执行
  3. 执行所有微任务
  4. 必要的话渲染 UI
  5. 然后开始下一轮 Event loop,执行宏任务中的异步代码

通过上述的 Event loop 顺序可知,如果宏任务中的异步代码有大量的计算并且需要操作
DOM 的话,为了更快的界面响应,我们可以把操作 DOM 放在微任务中。

图层

一般来说,可以把普通文档流看成一个图层。特定的属性可以⽣成⼀个新的图层。不同的图
层渲染互不影响,所以对于某些频繁需要渲染的建议单独生成一个新图层,提高性能。但也
不能生成过多的图层,会引起反作用。
通过以下几个常用属性可以生成新图层

  • 3D 变换: translate3d 、 translateZ
  • will-change
  • video 、 iframe 标签
  • 通过动画实现的 opacity 动画转换
  • position: fixed
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@追求卓越

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值