1 申明

1-1 「js函数申明三种形式:」

(1) Function()结构器

var f =new Function()

(2) 函数申明

function f (){     console.log(2);}

(3) 函数表达式

var f = function() {      console.log(1);  }

1-2「js变量申明:」

  • var申明的变量会挂载在window上,而let和const申明的变量不会
  • var申明变量存在变量晋升,let和const不存在变量晋升(严格来说,let也存在)
  • let和const申明造成块作用域
  • let存在暂存死区
  • const申明必须赋值

(1) var申明的变量会挂载在window上,而let和const申明的变量不会:

var a = 100;console.log(a,window.a);    // 100 100let b = 10;console.log(b,window.b);    // 10 undefinedconst c = 1;console.log(c,window.c);    // 1 undefined

(2) var申明变量存在变量晋升,let和const不存在变量晋升

console.log(a); // undefined  ===>  a已申明还没赋值,默认失去undefined值var a = 100;console.log(b); // 报错:b is not defined  ===> 找不到b这个变量let b = 10;console.log(c); // 报错:c is not defined  ===> 找不到c这个变量const c = 10;

(3) let和const申明造成块作用域

if(1){    var a = 100;    let b = 10;}console.log(a); // 100console.log(b)  // 报错:b is not defined  ===> 找不到b这个变量//if(1){    var a = 100;         const c = 1;} console.log(a); // 100 console.log(c)  // 报错:c is not defined  ===> 找不到c这个变量

(4) 同一作用域下let和const不能反复申明,而var能够

var a = 100;console.log(a); // 100var a = 10;console.log(a); // 10let a = 100;let a = 10;//  控制台报错:Identifier 'a' has already been declared  ===> 标识符a曾经被申明了。

(5) 暂存死区

var a = 100;if(1){    a = 10;    let a = 1;    //在以后块作用域中存在a应用let/const申明的状况下,给a赋值10时,只会在以后作用域查找变量a,    // 而这时,还未到申明时候,所以控制台Error:a is not defined    // 即let 和 const 不会申明提前}

(6) const

一旦申明必须赋值,不能应用null占位。申明后不能再批改如果申明的是援用类型数据,能够批改其属性const a = 100; const list = [];list[0] = 10;console.log(list);  // [10]const obj = {a:100};obj.name = 'apple';obj.a = 10000;console.log(obj);  // {a:10000,name:'apple'}

2 数据类型的分类:

2-1 根本类型:

  • string(字符串)--原始类型
  • boolean(布尔值)--原始类型
  • number(数字)--原始类型
  • symbol(符号)--原始类型
  • null(空值)
  • undefined(未定义)
  • BigInt(BigInt数据类型的目标是比Number数据类型反对的范畴更大的整数值,精度在(2^53-1)范畴内,BigInt(10) 值为:10n) ES2020新出的

其中 SymbolBigIntES6 中新增的数据类型:

  • Symbol 代表创立后举世无双且不可变的数据类型,它次要是为了解决可能呈现的全局变量抵触的问题。
  • BigInt 是一种数字类型的数据,它能够示意任意精度格局的整数,应用 BigInt 能够平安地存储和操作大整数,即便这个数曾经超出了 Number 可能示意的平安整数范畴。

留神:NaN不是数据类型

typeof NaN === 'number' //trueNaN==NaN  //false

2-2 对象类型(援用类型),有以下3种:

A.内置对象/原生对象

String、Number、Boolean、Array、Date、RegExp、Math、 Error、 Object、Function、 Global

B.宿主对象

  • (1)BOM对象: Window、Navigator、Screen、History、Location
  • (2)DOM对象:Document、Body、Button、Canvas等

C.自定义对象--(指由用户创立的对象,兼容性问题须要由编写者留神)

创立自定义对象几种形式:

(1)对象间接量:

var obj1 = {};var obj2 = {x:0,y:0};var obj3 = {name:‘Mary’,age:18}

(2)工厂模式--用函数来封装以特定接口创建对象的细节:

function createPerson(name,age,job){    var o = new Object();    o.name = name;    o.age = age;    o.job = job;    return o;}var person1 = createPerson('zhang',30,'java');

(3)构造函数模式:

function Person(name,age,job){    this.name= name;    this.age = age;    this.job = job;}var person1 = new Person('zhang',30,'java');

(4)原型模式:

function Person(){}Person.prototype.name = 'zhang';Person.prototype.age = '22';Person.prototype.job = 'html5';var person1 = new Person();

2-3 数据类型的判断:

2-3-1: typeof

个别通过 typeof 操作符来判断一个值属于哪种根本类型

毛病:无奈分辨对象类型

typeof 'seymoe'    // 'string'typeof true        // 'boolean'typeof 10          // 'number'typeof Symbol()    // 'symbol'typeof null        // 'object' 无奈断定是否为 nulltypeof undefined   // 'undefined'typeof {}           // 'object'typeof []           // 'object'typeof(() => {})    // 'function'

为什么typeof null为object:

js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息

  • 000:对象
  • 010:浮点数
  • 100:字符串
  • 110: 布尔值
  • 1:整数

然而, 对于 undefined 和 null 来说,这两个值的信息存储是有点非凡的。

  • null:所有机器码均为0
  • undefined:用 −2^30 整数来示意

所以,typeof 在判断 null 的时候就呈现问题了,因为null 的所有机器码均为0,因而间接被当做了对象来对待。

2-3-2:instanceof

判断对象类型:测试构造函数的 prototype 是否呈现在被检测对象的原型链上。

毛病:无奈判断一个值到底属于数组还是一般对象

[] instanceof Array            // true({}) instanceof Object         // true(()=>{}) instanceof Function   // truelet arr = []let obj = {}arr instanceof Array    // truearr instanceof Object   // trueobj instanceof Object   // true在这个例子中,arr 数组相当于 new Array() 出的一个实例,所以 arr.__proto__ === Array.prototype,又因为 Array 属于 Object 子类型,即 Array.prototype.__proto__ === Object.prototype,所以 Object 构造函数在 arr 的原型链上

判断不了原始类型

console.log(true instanceof Boolean);// falseconsole.log(undefined instanceof Object); // falseconsole.log(arr instanceof Array);  // trueconsole.log(null instanceof Object); // falseconsole.log({} instanceof Object); // trueconsole.log(function(){} instanceof Function);// true

2-3-3: Object.prototype.toString.call()

全能型,简直都能判断
Object.prototype.toString.call({})// '[object Object]'Object.prototype.toString.call([])// '[object Array]'Object.prototype.toString.call(() => {})// '[object Function]'Object.prototype.toString.call('abc')// '[object String]'

传入原始类型却可能断定出后果是因为对值进行了包装。

「那么,什么是包装对象:」

所谓“包装对象”,指的是与数值、字符串、布尔值别离绝对应的Number、String、Boolean三个原生对象。这三个原生对象能够把原始类型的值变成(包装成)对象。

具体点击这里

3 js运算符:

3-1 delete 运算符

delete 运算符用来删除对象属性或者数组元素,如果删除胜利或所删除的指标不存在,delete 将返回 true
然而,并不是所有的属性都可删除:

  • 一些内置外围和客户端属性是不能删除的
  • 通过 var 语句申明的变量不能删除
  • 通过 function 语句定义的函数也是不能删除的。

例如:

var o = { x: 1, y: 2};          // 定义一个对象console.log(delete o.x);        // true,删除一个属性console.log(delete o.x);        // true,什么都没做,x 在已上一步被删除console.log("x" in o);          // false,这个属性在对象中不再存在console.log(delete o.toString); // true,什么也没做,toString是继承来的console.log(delete 1);          // true,无意义var a = [1,2,3];                // 定义一个数组console.log(delete a[2]);       // true,删除最初一个数组元素console.log(2 in a);            // false,元素2在数组中不再存在console.log(a.length);          // 3,数组长度并不会因 delete 而扭转console.log(a[2]);              // undefined,元素2所在的地位被空了进去console.log(delete a);          // false,通过 var 语句申明的变量不能删除function f(args){}              // 定义一个函数console.log(delete f);          // false,通过 function 语句申明的函数不能删除

3-2 void 运算符

void 运算符能够利用于任何表类型的表达式,表达式会被执行,但计算后果会被疏忽并返回undefined

例如:

void 0;void "you are useless?";void false;void [];void /(useless)/ig;void function(){ console.log("you are so useless?"); }void alert(1)// always return undefined

3-3 ++ -- 运算符

++ -- 递增递加运算符借鉴自 C 语言,它们分前置型和后置型,作用是扭转一个变量的值。

例如:

var a = 5;console.log(a++);   // 5   后加表不加console.log(a);     // 6  console.log(++a);   // 7   先加,都有加console.log(a)      // 7console.log(a--);   // 7console.log(a)      // 6console.log(--a);   // 5console.log(a)      // 5

奇淫技巧先家都有家,后家表不家 (加号在后面,自身和表达式都加1;加号在前面,表达式不加1,自身加1 ),减法同理。

3-4 valueOf

var a = '你好', b = 1, c = [], d = {}, e = function (){}a.valueOf()  // '好'b.valueOf()  // 1c.valueOf()  //[]d.valueOf()  // {}e.valueOf()  //ƒ (){}

3-5 +和-

"+" 操作符,如果有一个为字符串,那么都转化到字符串而后执行字符串拼接

"-" 操作符,转换为数字,相减 (-a, a * 1 a/1) 都能进行隐式强制类型转换

[] + {}     // "[object Object]"{} + []     // 01 + true    //21 + false   //1

4 内存

4-1 执行上下文

当代码运行时,会产生一个对应的执行环境,在这个环境中,所有变量会被当时提出来(变量晋升),有的间接赋值,有的为默认值 undefined,代码从上往下开始执行,就叫做执行上下文。

「执行环境有三种」:

  • 1.全局环境:代码首先进入的环境
  • 2.函数环境:函数被调用时执行的环境
  • 3.eval函数

「执行上下文特点:」

  • 1.单线程,在主过程上运行
  • 2.同步执行,从上往下按程序执行
  • 3.全局上下文只有一个,浏览器敞开时会被弹出栈
  • 4.函数的执行上下文没有数目限度
  • 5.函数每被调用一次,都会产生一个新的执行上下文环境

「执行3个阶段:」

1.创立阶段

  • (1).生成变量对象
  • (2).建设作用域链
  • (3).确定 this 指向

2.执行阶段

  • (1).变量赋值
  • (2).函数援用
  • (3).执行其余代码

3.销毁阶段

  • (1).执行结束出栈,期待回收被销毁

具体点击这里

4-2 堆栈

「概念:」

  • 栈: 栈会主动调配内存空间,它由零碎主动开释;寄存根本类型,简略的数据段,占据固定大小的空间
  • 堆: 动态分配的内存,大小不定也不会主动开释。寄存援用类型,那些可能由多个值形成的对象,保留在堆内存中

具体点击这里

5 垃圾回收机制

MDN上有说: 从2012年起,所有古代浏览器都应用了标记-革除垃圾回收算法。所有对于js垃圾回收算法的改良都是基于标记-革除算法的改良

「什么是垃圾:」 一般来说,没有援用的对象就是垃圾,就是要才革除的。但有个例外,如果几个对象互相援用造成一个环,但根拜访不到他们,他们也是垃圾(援用计数法,无奈革除他们)

「垃圾回收的几种算法:」

5-1援用计数法

概念: 记录有多少“程序”在援用本人,当援用的数值为0时,就开始革除它。

劣势:

  • 马上回收垃圾,当被援用数值为0时,对象马上会把本人作为闲暇空间连到闲暇链表上,也就是说。在变成垃圾的时候就立即被回收。
  • 因为是即时回收,那么‘程序’不会暂停去独自应用很长一段时间的GC,那么最大暂停工夫很短
  • 不必去遍历堆外面的所有流动对象和非流动对象

劣势:

  • 计数器须要占很大地位,因为不能预估被援用的下限,打个比方,可能呈现32位即2的32次方个对象同时援用一个对象,那么计数器就须要32位。
  • 最大的劣势是无奈解决循环援用无奈回收的问题 这就是前文中IE9之前呈现的问题

5-2 标记革除法

次要将GC的垃圾回收过程分为两个阶段

标记阶段:把所有流动对象做上标记

革除阶段:把没有标记(也就是非流动对象)销毁

劣势:

  • 实现简略,打标记也就是打或者不打两种可能,所以就一位二进制位就能够示意
  • 解决了循环援用问题

毛病

  • 造成碎片化(有点相似磁盘的碎片化)
  • 再调配时遍次数多,如果始终没有找到适合的内存块大小,那么会遍历闲暇链表(保留堆中所有闲暇地址空间的地址造成的链表)始终遍历到尾端

5-3 复制算法

  1. 将一个内存空间分为两局部,一部分是From空间,另一部分是To空间
  2. From空间外面的流动对象复制To空间
  3. 开释掉整个From空间
  4. 再将From空间和To空间的身份调换,那么就实现了一次GC。

具体点击这里1

具体点击这里2

6 内存透露

「概念:」 申请的内存没有及时回收掉,造成零碎内存节约,导致程序运行速度减慢甚至零碎解体等重大结果

「内存透露产生的场景:」

(1) 意外的全局变量

function leaks(){    leak = 'xxxxxx';//leak 成为一个全局变量,不会被回收}

(2) 忘记的定时器

setTimeout 和 setInterval 是由浏览器专门线程保护它的生命周期,如果在某个页面应用了定时器,当销毁页面时,没有手动去开释清理这些定时器的话,那么这些定时器还是存活着的

(3) 使用不当的闭包

var leaks = (function(){      var leak = 'xxxxxx';// 被闭包所援用,不会被回收    return function(){        console.log(leak);    }})()

(4) 脱漏的 DOM 元素

<div id="container">  </div>$('#container').bind('click', function(){    console.log('click');}).remove();//dom移除了,然而js还持有对它的援用

解决:

$('#container').bind('click', function(){    console.log('click');}).off('click').remove();//把事件革除了,即可从内存中移除

(5) 网络回调

「如何监控内存透露」

  • 应用控制台
  • 具体点击这里

7 作用域

扩大

JavaScript是门动静语言,跟Java不一样,JavaScript能够随便定义全局变量和局部变量,每一个函数都是一个作用域,当函数执行时会优先查找以后作用域,而后逐级向上。

JavaScript是动态作用域,在对变量进行查问时,变量值由函数定义时的地位决定,和执行时的所处的作用域无关。

ES6曾经有块级作用域了,而且用 let 和 const 定义的变量不会晋升。

概念

作用域:变量或者函数的无效作用范畴

作用域链:咱们须要查找某个变量值,会先在以后作用域查找,如果找不到会往上一级查,如果找到的话,就返回进行查找,返回查找的值,这种向上查找的链条关系,叫作用域

7-1 相干案例

(1)变量晋升/变量由函数定义时的地位决定

var a = 1;function fn() {  console.log('1:' + a);  var a = 2;  bar()  console.log('2:' + a)}function bar() {  console.log('3:' + a)}fn()

别离打印:1:undefined 3:1 2:2

「解析:」

第一个 a 打印的值是 1:undefined 而不是 1。因为咱们在 fn() 中定义了变量 a,用 var 定义的变量会晋升到以后作用域的顶部(即以后函数作用域顶部),然而赋值还在原地,所以是undefined。

第二个a 打印的值是 3:1 而不是 2。因为函数 bar 是定义在全局作用域中的,所以作用域链是 bar -> global,bar 外面没有定义a,所以就会顺着作用域链向上找,而后在 global 中找到了 a。留神:查找是在其定义的执行上下文环境中查找。

第三个 a 打印的值是 2:2。这句话所在的作用域链是 fn -> global,执行 console.log('2:' + a) 会首先在 fn 作用域里查找 a,找到有 a,并且曾经赋值为2,所以后果就是2。

(2)变量赋值

var a = 1;function fn() {  console.log('1:' + a);  a = 2}a = 3;function bar() {  console.log('2:' + a);}fn();bar();

别离打印:1:3 2:2

「解析:」

第一个 打印的值是 1:3。首先, fn 中的 a = 2 是给变量 a 赋值,并不是申明变量。而后,执行函数 fn,此时 a 曾经赋值为3了,留神,fn()是在a=3前面执行。

第二个 打印的值是 2:2。函数 bar 所能拜访的作用域链为 bar->global,在执行函数 bar 时,因为在bar前执行了fn()将a批改为2了,所以这个时候拿到的a为2。

(3)全局变量申明提前

if(!(a in window)){    var a = 10;}console.log(a);

打印:undefined

「解析:」

相当于:

var a;if(!(a in window)){    a = 10;}console.log(a);

用 var 定义的变量会晋升到以后作用域的顶部(即以后全局作用域), 所以a会申明提前到window中,但值还是在原地,即为undefined。 所以if失去是a in window是ture 故不走外面赋值 console.log(a) == undefined

上一个例子的变种:

(function(){ var  x = c =  b = {a:1}})()console.log(c,b) // {a: 1} {a: 1}console.log(x.a); // error , x is not defined

留神: x是在函数中申明的,是局部变量,c和b未声明,间接赋值,所以是全局变量。 赋值过程是从右往左的,即b={a:1},c=b,x=c

(4)变量晋升/运算符程序

(function(){  var a = b = 3;})()console.log(typeof a === "undefined"); // trueconsole.log(typeof b === "undefined"); // falseconsole.log(typeof b === "number" && b ===3); // true

// 这里波及的就是立刻执行和闭包的问题,还有变量晋升,运算符执行方向(=号自右向左)

// 那个函数能够拆成这样

(function()  var a; /* 局部变量,内部没法拜访*/  b = 3; /* 全局变量,so . window.b === 3 , 内部能够拜访到*/  a = b;})()

(5)变量晋升/运算符程序

var x = 1;if (function f(){console.log(2)}) {x += typeof f;  }console.log(x);  // 1undefined

//因为函数体在()中会以表达式去运行,fn函数不起作用,函数不会执行。

//最初表达式转换为true,f未声明(下面的函数没起作用),值为undefined

「知识点:」

(1) 在JavaScript中,通过 let 和 const 定义的变量具备块级作用域的个性。

(2) 通过 var 定义的变量会在它本身的作用域中进行晋升,而 let 和 const 定义的变量不会。

(3) 每个JavaScript程序都具备一个全局作用域,每创立一个函数都会创立一个作用域。

(4) 在创立函数时,将这些函数进行嵌套,它们的作用域也会嵌套,造成作用域链,子作用域能够拜访父作用域,然而父作用域不能拜访子作用域。

(5) 在执行一个函数时,如果咱们须要查找某个变量值,那么会去这个函数被 定义 时所在的作用域链中查找,一旦找到须要的变量,就会进行向上查找。

(6) “变量的值由函数定义时的地位决定”这句话有歧义,精确说是查找变量时,是去定义这个函数时所在的作用域链查找。

8 闭包

「概念:」

闭包就是一个函数,这个函数可能拜访其余函数作用域中的变量

「利用场景:」

  • 函数防抖
  • 封装公有变量

JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段代码执行阶段。编译阶段由编译器实现,将代码翻译成可执行代码,这个阶段作用域规定会确定。执行阶段由引擎实现,次要工作是执行可执行代码,执行上下文在这个阶段创立。

9 this

9-1 this的指向:

ES5中:

this 永远指向最初调用它的那个对象

ES6箭头函数:

箭头函数的 this 始终指向函数定义时的 this,而非执行时

9-2 怎么扭转this的指向:

  • 应用 ES6 的箭头函数
  • 在函数外部应用 _this = this
  • 应用 apply、call、bind
  • new 实例化一个对象
    案例1:

    var name = "windowsName";  var a = {      name : "Cherry",      func1: function () {          console.log(this.name)           },      func2: function () {          setTimeout(  function () {              this.func1()          },100);      }  };  a.func2()     // this.func1 is not a function

    在不应用箭头函数的状况下,是会报错的,因为最初调用 setTimeout 的对象是
    window,然而在 window 中并没有 func1 函数。能够看做window.setTimeout

案例2:

var webName="long";let func=()=>{  console.log(this.webName);}func();//long

//箭头函数在全局作用域申明,所以它捕捉全局作用域中的this,this指向window对象

案例3:

var webName = "long";function wrap(){  let func=() => {    console.log(this.webName);  }  func();}wrap();//long

//wrap函数执行时,箭头函数func定义在wrap中,func会找到它最近一层非箭头函数的this

//也就是wrap的this,而wrap函数作用域中的this指向window对象。

9-3 箭头函数:

「Tips:」 家喻户晓,ES6 的箭头函数是能够防止 ES5 中应用 this 的坑的。箭头函数的 this 始终指向函数定义时的 this,而非执行时

箭头函数须要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值(箭头函数自身没有this,然而在它申明时能够捕捉他人的this供本人应用。),如果箭头函数被非箭头函数蕴含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。

特点

  • 没有this
  • 没有arguments
  • 不能通过new关键字调用
  • 没有new.target
  • 没有原型
  • 没有super

具体点击这里

10 原型和原型链

10-1 背景:

一个函数能够看成一个类,原型是所有类都有的一个属性,原型的作用就是给这个类的每一个对象都增加一个对立的办法

10-2基本概念

「prototype :」 每个函数都会这个属性,这里强调,是函数,一般对象没有这个属性的(这里为什么说一般对象呢,因为JS外面,所有皆为对象,所以这里的一般对象不包含函数对象)。它是构造函数的原型对象;

「「proto」 :」 每个对象这个属性,这里强调,是对象,同样,因为函数也是对象,所以函数也有这个属性。它指向构造函数的原型对象;

「constructor :」 这是原型对象上的一个指向构造函数的属性。

var webName = "long";// Pig的构造函数function Pig(name, age) {    this.name = name;    this.age = age;}// 创立一个Pig的实例,小猪佩奇var Peppa = new Pig('Peppa', 5);Peppa.__proto__ === Pig.prototype。 //truePig.__proto__ === Function.prototype //truePig.prototype.constructor === Pig //true

奇淫技巧: 韩信对饮(函数的显示原型 = 对象的隐士原型)
具体点击这里

10-3 什么是原型继承

一个对象能够应用另外一个对象的属性或者办法,就称之为继承

具体是通过将这个对象的原型设置为另外一个对象,这样依据原型链的规定,如果查找一个对象属性且在本身不存在时,就会查找另外一个对象,相当于一个对象能够应用另外一个对象的属性和办法了。

11 深浅拷贝

11-1 浅克隆

function shallowClone(obj) {  let cloneObj = {};    for (let i in obj) {    cloneObj[i] = obj[i];  }    return cloneObj;}

11-2 深克隆

深克隆:

  • 思考根底类型
  • 援用类型
  • RegExp、Date、函数 不是 JSON 平安的
  • 会失落 constructor,所有的构造函数都指向 Object
  • 破解循环援用

    function deepCopy(obj) {if (typeof obj === 'object') {  var result = obj.constructor === Array ? [] : {};    for (var i in obj) {    result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];  }} else {  var result = obj;}return result;}

具体点击这里

12 数组

12-1数组去重

1. es6的new Set

let arr=[1,2,2,3]let arrNew=[...new Set(arr)]console.log(arrNew)//[1,2,3]

2. 遍历旧数组往新数组中增加惟一的元素

function unique(arr) {   var newArr = []   for (var i = 0; i < arr.length; i++) {      f (newArr.indexOf(arr[i])===-1) {//形式1          newArr.push(arr[i])       }       // if (!newArr.includes(arr[i])) {//形式2         // newArr.push(arr[i])       //}    }    return newArr    }   console.log(unique(arr))//[1,2,3]

3. 利用Map数据结构去重

function unique(){  let map =new Map()  let arr=new Array()  for(let i=0;i<arr.length;i++){   if(map.has(arr[i])){//如果有key值,它是把元素值作为key      map.set(arr[i],true)   }else{     map.set(arr[i],false)//如果没有该key值     array.push(arr[i])   } } return array}console.log(unique([1,2,2,3]))

解析:

把每一个元素作为key值存到Map中,因为Map不会呈现雷同的key,所以最终失去去重后的后果。

12-2数组开展

1. flat办法

  let arr = [1,2,[3,4],[5[6,7]]]  let arr1 = arr.flat(Infinity)//[1,2,3,4,5,6,7]

2.join,split

let arr = [1,2,[3,4],[5[6,7]]]let arr1 = arr.join().split(",")  //["1", "2", "3", "4", ""]

3.toString,split

let arr = [1,2,[3,4],[5[6,7]]]let arr1 = arr.toString().split(",")   //["1", "2", "3", "4", ""]

12-3数组合并

1. es6开展合并

  let arr1 = [1,2]  let arr2 = [3,4]  let arr = [...arr1,...arr2]//[1,2,3,4]

2. concat

  let arr = arr1.concat(arr2)

12-4判断数组

instanceof

   console.log(arr instanceof Array)

constructor

   console.log(arr.constructor === Array)

Array.isArray

   console.log(Array.isArray(arr))

toString

   console.log(Object.prototype.toString.call(arr) === "[object Array]")

判断是否有数组的push等办法

   console.log(!!arr.push && !!arr.concat)

13 对象

13-1 如何判断一个对象是不是空对象

1.将json对象转化为json字符串,再判断该字符串是否为"{}"

var data = {};var b = (JSON.stringify(data) == "{}"); //true

2.for in 循环判断

var obj = {};var b = function() {    for(var key in obj) {        return false;    }    return true;}b();//true

3.jquery的isEmptyObject办法

此办法是jquery将2办法(for in)进行封装,应用时须要依赖jquery

var data = {};var b = $.isEmptyObject(data);alert(b);//true

4.Object.getOwnPropertyNames()办法

此办法是应用Object对象的getOwnPropertyNames办法,获取到对象中的属性名,存到一个数组中,返回数组对象,咱们能够通过判断数组的length来判断此对象是否为空

留神:此办法不兼容ie8,其余浏览器没有测试

var data = {};var arr = Object.getOwnPropertyNames(data);alert(arr.length == 0);//true

5.应用ES6的Object.keys()办法

与4办法相似,是ES6的新办法, 返回值也是对象中属性名组成的数组

var data = {};var arr = Object.keys(data);alert(arr.length == 0);//true

13 v8引擎

13-1 v8垃圾回收

「背景:」

V8的垃圾回收策略基于分代回收机制,该机制又基于 世代假说

该假说有两个特点

  • 大部分新生对象偏向于早死
  • 不死的对象,会更久

基于这个实践,古代垃圾回收算法依据对象的存活工夫将内存进行了分代,并对不同分代的内存采纳不同的高效算法进行垃圾回收。

「V8的内存分代」

在V8中,将内存分为了新生代(new space)和老生代(old space)。

它们特点如下:

新生代:对象的存活工夫较短。新生对象或只通过一次垃圾回收的对象。

老生代:对象存活工夫较长。经验过一次或屡次垃圾回收的对象。

「V8堆的空间」

V8堆的空间等于新生代空间加上老生代空间。咱们能够通过 --max-old-space-size命令设置老生代空间的最大值,--max-new-space-size 命令设置新生代空间的最大值。老生代与新生代的空间大小在程序初始化时设置,一旦失效则不能动静扭转。

  • node --max-old-space-size=1700 test.js // 单位为 MB
  • node --max-new-space-size=1024 test.js // 单位为KB

默认设置下,64位零碎的老生代大小为1400M,32位零碎为700M。

对于新生代,它由两个 reserved_semispace_size 组成。每个reserved_semispace_size 的大小在不同位数的机器上大小不同。默认设置下,在64位与32位的零碎下别离为16MB和8MB。咱们将新生代、老生代、reserved_semispace_size 空间大小总结如下表。

类型\零碎位数64位32位
老生代1400MB700MB
reserved_semispace_size16MB8MB
新生代32MB16MB

具体点击这里

14 event loop

14-1 什么是事件循环

具体点击这里1

具体点击这里2

15 严格模式的优缺点

15-1 概念

ECMAScript 5 中引入的一种将更好的谬误查看引入代码中的办法, 当初曾经被大多浏览器实现. 这种模式使得Javascript在更严格条件下运行

15-2 长处

  • 无奈再意外创立全局变量。
  • 会使引起静默失败(silently fail,即:不报错也没有任何成果)的赋值操抛出异样。
  • 试图删除不可删除的属性时会抛出异样(之前这种操作不会产生任何成果)。
  • 要求函数的参数名惟一。
  • 全局作用域下,this的值为undefined。
  • 捕捉了一些常见的编码谬误,并抛出异样。
  • 禁用令人困惑或欠佳的性能。

15-3 毛病

  • 缺失许多开发人员曾经习惯的性能。
  • 无法访问function.caller和function.arguments。
  • 以不同严格模式编写的脚本合并后可能导致问题。

16 ES6

16-1 背景

EC版本公布工夫新增个性
2009(ES5)2009年11月扩大了Object、Array、Function的性能等新增个性
2015(ES6)2015年6月类,模块化,箭头函数,函数参数默认值等
2016(ES7)2016年3月includes,指数操作符
2017(ES8)2017年6月sync/await,Object.values(),Object.entries(),String padding等

16-2 罕用个性

  • 模块化
  • 箭头函数
  • 函数参数默认值
  • 模板字符串
  • 解构赋值
  • 延展操作符
  • 对象属性简写
  • Promise
  • Let与Const

「(1) 类(class)」

传统的javascript中只有对象,没有类的概念。

它是基于原型的面向对象语言。原型对象特点就是将本身的属性共享给新对象。
这样的写法绝对于其它传统面向对象语言来讲,很有一种自成一家的感脚!非常容易让人困惑!
如果要生成一个对象实例,须要先定义一个构造函数,而后通过new操作符来实现。

上面用一个例子演示构造函数到class的演变:

构造函数--

function Person(name,age) {    this.name = name;    this.age=age;}Person.prototype.say = function(){    return "我的名字叫" + this.name+"往年"+this.age+"岁了";}var obj=new Person("laotie",88);//通过构造函数创建对象,必须应用new 运算符console.log(obj.say());//我的名字叫laotie往年88岁了

ES6引入了Class(类)这个概念,通过class关键字能够定义类。

该关键字的呈现使得其在对象写法上更加清晰,更像是一种面向对象的语言。

如果将之前的代码改为ES6的写法就会是这个样子:

class--

class Person{//定义了一个名字为Person的类    constructor(name,age){//constructor是一个构造方法,用来接管参数        this.name = name;//this代表的是实例对象        this.age=age;    }    say(){//这是一个类的办法,留神千万不要加上function和逗号        return "我的名字叫" + this.name+"往年"+this.age+"岁了";    }}var obj=new Person("laotie",88);console.log(obj.say());//我的名字叫laotie往年88岁了console.log(typeof Person);//function--类本质上就是一个函数console.log(Person===Person.prototype.constructor);//true//类本身指向的就是构造函数。所以能够认为ES6中的类其实就是构造函数的另外一种写法!

留神项:

  1. 在类中申明办法的时候,千万不要给该办法加上function关键字
  2. 办法之间不要用逗号分隔,否则会报错
  3. class不存在变量晋升,所以须要先定义再应用。因为ES6不会把类的申明晋升到代码头部,然而ES5就不一样,ES5存在变量晋升,能够先应用,而后再定义。

    //ES5能够先应用再定义,存在变量晋升new A();function A(){}//ES6不能先应用再定义,不存在变量晋升 会报错new B();//B is not definedclass B{}

    「(2) 模块化(Module)」

背景

在之前的javascript中是没有模块化概念的。如果要进行模块化操作,须要引入第三方的类库。随着技术的倒退,前后端拆散,前端的业务变的越来越复杂化。直至ES6带来了模块化,才让javascript第一次反对了module。ES6的模块化分为导出(export)导入(import)两个模块。

export的用法

在ES6中每一个块即是一个文件,在文件中定义的变量,函数,对象在内部无奈获取的。如果你心愿内部能够读取模块当中的内容,就必须应用export来对其进行裸露(输入)。

先来看个例子,来对一个变量进行模块化。

//咱们先来创立一个test.js文件,来对这一个变量进行输入:export let myName="laowang";
//而后能够创立一个index.js文件,以import的模式将这个变量进行引入:import {myName} from "./test.js";console.log(myName);//laowang

如果要输入多个变量能够将这些变量包装成对象进行模块化输入:

let myName="laowang";let myAge=90;let myfn=function(){    return "我是"+myName+"!往年"+myAge+"岁了"}export {    myName,    myAge,    myfn}******************************接管的代码调整为********************* import {myfn,myAge,myName} from "./test.js";console.log(myfn());//我是laowang!往年90岁了console.log(myAge);//90console.log(myName);//laowang

如果你不想裸露模块当中的变量名字,能够通过as来进行操作:

let myName="laowang";let myAge=90;let myfn=function(){    return "我是"+myName+"!往年"+myAge+"岁了"}export {    myName as name,    myAge as age,    myfn as fn}/******************************接管的代码调整为**********************/import {fn,age,name} from "./test.js";console.log(fn());//我是laowang!往年90岁了console.log(age);//90console.log(name);//laowang

也能够间接导入整个模块,将下面的接管代码批改为:

import * as info from "./test.js";//通过*来批量接管,as 来指定接管的名字console.log(info.fn());//我是laowang!往年90岁了console.log(info.age);//90console.log(info.name);//laowang

默认导出(default export) 一个模块只能有一个默认导出,对于默认导出,导入的名称能够和导出的名称不统一。

/******************************导出**********************/export default function(){    return "默认导出一个办法"}/******************************引入**********************/import myFn from "./test.js";//留神这里默认导出不须要用{}。console.log(myFn());//默认导出一个办法

能够将所有须要导出的变量放入一个对象中,而后通过default export进行导出

/******************************导出**********************/export default {    myFn(){        return "默认导出一个办法"    },    myName:"laowang"}/******************************引入**********************/import myObj from "./test.js";console.log(myObj.myFn(),myObj.myName);//默认导出一个办法 laowang

同样也反对混合导出

/******************************导出**********************/export default function(){    return "默认导出一个办法"}export var myName="laowang";/******************************引入**********************/import myFn,{myName} from "./test.js";console.log(myFn(),myName);//默认导出一个办法 laowang

重命名export和import 如果导入的多个文件中,变量名字雷同,即会产生命名抵触的问题,为了解决该问题,ES6为提供了重命名的办法,当你在导入名称时能够这样做:

/******************************test1.js**********************/export let myName="我来自test1.js";/******************************test2.js**********************/export let myName="我来自test2.js";/******************************index.js**********************/import {myName as name1} from "./test1.js";import {myName as name2} from "./test2.js";console.log(name1);//我来自test1.jsconsole.log(name2);//我来自test1.js

「(3) 箭头(Arrow)函数」

查看1-5-3 箭头函数大节:

箭头函数的 this 始终指向函数定义时的 this,而非执行时。箭头函数须要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数蕴含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。

「(4) 函数参数默认值」

ES6反对在定义函数的时候为其设置默认值:

function foo(height = 50, color = 'red'){    // ...}

不应用默认值:

function foo(height, color){    var height = height || 50;    var color = color || 'red';    //...}

这样写个别没问题,但当参数的布尔值为false时,就会有问题了。
比方,咱们这样调用foo函数: foo(0, "") 因为0的布尔值为false,这样height的取值将是50。同理color的取值为‘red’。 所以说,函数参数默认值不仅能是代码变得更加简洁而且能躲避一些问题。

「(5) 模板字符串」

ES6反对在定义函数 ES6反对模板字符串,使得字符串的拼接更加的简洁、直观。

不应用模板字符串:

var name = 'Your name is ' + first + ' ' + last + '.'

应用模板字符串:

var name = `Your name is ${first} ${last}.`

在ES6中通过${}就能够实现字符串的拼接,只须要将变量放在大括号之中。

「(6) 解构赋值」

解构赋值语法是JavaScript的一种表达式,能够不便的从数组或者对象中疾速提取值赋给定义的变量

获取数组中的值

//从数组中获取值并赋值到变量中,变量的程序与数组中对象程序对应。var foo = ["one", "two", "three", "four"];var [one, two, three] = foo;console.log(one); // "one"console.log(two); // "two"console.log(three); // "three"
//如果你要疏忽某些值,你能够依照上面的写法获取你想要的值var [first, , , last] = foo;console.log(first); // "one"console.log(last); // "four"
//你也能够这样写var a, b; //先申明变量[a, b] = [1, 2];console.log(a); // 1console.log(b); // 2
//如果没有从数组中的获取到值,你能够为变量设置一个默认值。var a, b;[a=5, b=7] = [1];console.log(a); // 1console.log(b); // 7
//通过解构赋值能够不便的替换两个变量的值。var a = 1;var b = 3;[a, b] = [b, a];console.log(a); // 3console.log(b); // 1

获取对象中的值

const student = {  name:'Ming',  age:'18',  city:'Shanghai'  };const {name,age,city} = student;console.log(name); // "Ming"console.log(age); // "18"console.log(city); // "Shanghai"

「(7) 延展操作符」

在ECMAScript 2018中延展操作符减少了对对象的反对,用于对像和数组的拆解

var obj1 = { foo: 'bar', x: 42 };var obj2 = { foo: 'baz', y: 13 };var clonedObj = { ...obj1 };// 克隆后的对象: { foo: "bar", x: 42 }var mergedObj = { ...obj1, ...obj2 };// 合并后的对象: { foo: "baz", x: 42, y: 13 } 雷同属性会进行笼罩
//咱们能够这样合并数组:var arr1=['a','b','c'];var arr2=[...arr1,'d','e']; //['a','b','c','d','e']
//开展运算符也能够用在push函数中,能够不必再用apply()函数来合并两个数组:var arr1=['a','b','c'];var arr2=['d','e'];arr1.push(...arr2); //['a','b','c','d','e']
//用于解构赋值let [arg1,arg2,...arg3] = [1, 2, 3, 4];arg1 //1arg2 //2arg3 //['3','4']
//开展运算符既然能合并数组,天然也能解构数组,不过要留神,解构赋值中【开展运算符】只能用在【最初】:let [arg1,...arg2,arg3] = [1, 2, 3, 4]; //报错

「(8) 对象属性简写」

在ES6中容许咱们在设置一个对象的属性的时候不指定属性名。

不应用ES6

const name='Ming',age='18',city='Shanghai';const student = {    name:name,    age:age,    city:city};console.log(student);//{name: "Ming", age: "18", city: "Shanghai"}//对象中必须蕴含属性和值,显得十分冗余。

应用ES6

const name='Ming',age='18',city='Shanghai'; const student = {    name,    age,    city};console.log(student);//{name: "Ming", age: "18", city: "Shanghai"}//对象中间接写变量,十分简洁。

「(9) Promise」 什么是 Promise

Promise 是异步编程的一种解决方案,比传统的异步解决方案回调函数事件更正当、更弱小。现已被 ES6 纳入进标准中。

上面通过几个案例来加深promise的理解:

(1) Promise 构造函数是同步执行的.then是异步的

const promise = new Promise((resolve, reject) => {  console.log(1)  resolve()  console.log(2)})promise.then(() => {  console.log(3)})console.log(4)//运行后果:1 2 4 3

解析:Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的。

(2) promise 状态一旦扭转则不能再变

const promise = new Promise((resolve, reject) => {  resolve('success1')  reject('error')  resolve('success2')})promise  .then((res) => {    console.log('then: ', res)  })  .catch((err) => {    console.log('catch: ', err)  })//运行后果:then: success1

解析:构造函数中的 resolve 或 reject 只有第一次执行无效屡次调用没有任何作用,promise 状态一旦扭转则不能再变。

(3) .then 或者 .catch 都会返回一个新的 promise

Promise.resolve(1)  .then((res) => {    console.log(res)    return 2  })  .catch((err) => {    return 3  })  .then((res) => {    console.log(res)  })//运行后果:1 2

解析:promise 能够链式调用。提起链式调用咱们通常会想到通过 return this 实现,不过 Promise 并不是这样实现的。promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用。

(4) Promise 构造函数只执行一次

const promise = new Promise((resolve, reject) => {  setTimeout(() => {    console.log('once')    resolve('success')  }, 1000)})const start = Date.now()promise.then((res) => {  console.log(res, Date.now() - start)})promise.then((res) => {  console.log(res, Date.now() - start)})//运行后果:once          success 1005          success 1007

解析:promise 的 .then 或者 .catch 能够被调用屡次,但这里 Promise 构造函数只执行一次。或者说 promise 外部状态一经扭转(第一次调用.then就扭转了),并且有了一个值,那么后续每次调用 .then 或者 .catch 都会间接拿到该值。

(5) .then 或者 .catch 都会返回一个新的 promise

Promise.resolve()  .then(() => {    return new Error('error!!!')  })  .then((res) => {    console.log('then: ', res)  })  .catch((err) => {    console.log('catch: ', err)  })//运行后果:then: Error: error!!!    at Promise.resolve.then (...)    at ...

解析:.then 或者 .catch 中 return 一个 error 对象并不会抛出谬误,所以不会被后续的 .catch 捕捉,须要改成其中一种:

  1. return Promise.reject(new Error('error!!!'))
  2. throw new Error('error!!!')

因为返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return new Error('error!!!') 等价于 return Promise.resolve(new Error('error!!!'))。

(6) .then 或 .catch 返回的值不能是 promise 自身

const promise = Promise.resolve()  .then(() => {    return promise  })promise.catch(console.error)//运行后果:TypeError: Chaining cycle detected for promise #<Promise>    at <anonymous>    at process._tickCallback (internal/process/next_tick.js:188:7)    at Function.Module.runMain (module.js:667:11)    at startup (bootstrap_node.js:187:16)    at bootstrap_node.js:607:3

解析:.then 或 .catch 返回的值不能是 promise 自身,否则会造成死循环。

(7) .then函数返回值类型与参数传递

Promise.resolve(2) // resolve(2) 函数返回一个Promise<number>对象.then(x=>{   console.log( x ); // 输入2, 示意x类型为number且值为2,也就是下面resolve参数值   return "hello world"; // 回调函数返回字符串类型,then函数返回Promise<string>}) // then函数返回Promise<string>类型.then(x=>{   console.log( x ); // 输入hello world,也就是上一个then回调函数返回值,表明上一个then的返回值就是下一个then的参数}) // then函数回调函数中没有返回值,则为Promise<void>.then(x=>{ // 后面的then的回调函数没有返回值所以这个x是undefined   console.log( x ); // undefined}) // Promise<void>.then(()=>{ // 后面没有返回值,这里回调函数能够不加返回值   return Promise.resolve("hello world"); // 返回一个Promise<string>类型}) // 这里then的返回值是Promise<string>.then(x=>{ // 尽管下面的then中回调函数返回的是Promise<string>类型然而这里x并不是Promise<string>类型而是string类型   console.log(x); // hello world   return Promise.resolve(2); // 返回一个Promise<number>类型对象}) // 返回Promise<number>类型

(8) .then 或者 .catch 的参数冀望是函数,传入非函数则会产生值穿透。

Promise.resolve(1)  .then(2)  .then(Promise.resolve(3))  .then(console.log)  //1

解析:.then 或者 .catch 的参数冀望是函数,传入非函数则会产生值穿透。

(9) .catch 相当于.then的简写(省略了.then的第二个参数)

Promise.resolve()  .then(function success (res) {    throw new Error('error')  }, function fail1 (e) {    console.error('fail1: ', e)  })  .catch(function fail2 (e) {    console.error('fail2: ', e)  })//运行后果:fail2: Error: error          at success (...)          at ...

解析:.then 能够接管两个参数,第一个是解决胜利的函数,第二个是处理错误的函数。.catch也相当于是一个.then,只不过把.then的第二个参数省略了,然而它们用法上有一点须要留神:.then 的第二个处理错误的函数捕捉不了第一个解决胜利的函数抛出的谬误,而后续的 .catch 能够捕捉之前的谬误。

(10) 微工作宏工作执行程序

process.nextTick(() => {  console.log('nextTick')})Promise.resolve()  .then(() => {    console.log('then')  })setImmediate(() => {  console.log('setImmediate')})console.log('end')//运行后果:end          nextTick          then          setImmediate

解析:process.nextTick 和 promise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

「(10) let 和 const」

查看1-1js变量申明

17 事件流

事件流是网页元素接管事件的程序,"DOM2级事件"规定的事件流包含三个阶段:事件捕捉阶段处于指标阶段事件冒泡阶段

  • 首先产生的事件捕捉,为截获事件提供机会。
  • 而后是理论的指标承受事件。
  • 最初一个阶段是工夫冒泡阶段,能够在这个阶段对事件做出响应。

尽管捕捉阶段在标准中规定不容许响应事件,然而实际上还是会执行,所以有两次机会获取到指标对象。

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>事件冒泡</title></head><body>    <div>        <p id="parEle">我是父元素                <span id="sonEle">我是子元素</span>        </p>    </div></body></html><script type="text/javascript">var sonEle = document.getElementById('sonEle');var parEle = document.getElementById('parEle');parEle.addEventListener('click', function () {    alert('父级 冒泡');}, false);parEle.addEventListener('click', function () {    alert('父级 捕捉');}, true);sonEle.addEventListener('click', function () {    alert('子级冒泡');}, false);sonEle.addEventListener('click', function () {    alert('子级捕捉');}, true);</script>

当容器元素及嵌套元素,即在捕捉阶段又在冒泡阶段调用事件处理程序时:事件按DOM事件流的程序执行事件处理程序:

父级捕捉=》子级捕捉=》子级冒泡=》父级冒泡

  • 点击【我是父元素】,顺次弹出('父级 捕捉'=》'父级 冒泡')
  • 点击【我是父元素】,顺次弹出('父级 捕捉'=》'子级 捕捉'=》'子级 冒泡'=》'父级 冒泡')

奇淫技巧:三个阶段能够这么记 捕母猫(捕捉,指标,冒泡)

18 new

结构调用:

  • 发明一个全新的对象
  • 这个对象会被执行 [[Prototype]] 连贯,将这个新对象的 [[Prototype]] 链接到这个构造函数.prototype 所指向的对象
  • 这个新对象会绑定到函数调用的 this
  • 如果函数没有返回其余对象,那么 new 表达式中的函数调用会主动返回这个新对象

19 手写promise

20 js脚本加载问题,async、defer

20-1 失常模式

这种状况下 JS 会阻塞浏览器,浏览器必须期待 index.js 加载和执行结束能力去做其它事件。

<script src="index.js"></script>

20-2 async(异步) 模式

async 模式下,JS 不会阻塞浏览器做任何其它的事件。它的加载是异步的,当它加载完结,JS 脚本会立刻执行。

<script async src="index.js"></script>

20-3 defer(延缓) 模式

defer 模式下,JS 的加载是异步的,执行是被推延的。等整个文档解析实现、DOMContentLoaded 事件行将被触发时,被标记了 defer 的 JS 文件才会开始顺次执行。

<script defer src="index.js"></script>

从利用的角度来说,个别当咱们的脚本与 DOM 元素和其它脚本之间的依赖关系不强时,咱们会选用 async;当脚本依赖于 DOM 元素和其它脚本的执行后果时,咱们会选用 defer。

21 性能优化

什么是性能优化,为什么要优化,咱们优化的对象是什么,思考分明这几个问题,咱们能力找到对应的解决办法。

什么是性能优化?

在不影响零碎运行正确性的前提下,使之运行地更快实现特定性能所需的工夫更短。简而言之,就是让咱们的程序更快的运行。

为什么要性能优化?

性能是留住用户很重要的一环,个别人们能忍耐一个网页的加载时长是少于5秒,性能优化也是程序高效运行的保障。

咱们优化的对象是什么?

优化对象是程序,以及程序所运行在的载体(如浏览器)

咱们曾经晓得了性能优化的对象是什么了,那么接下来就能够依据优化对象离开几个大类总结,对于前端来说,程序和载体无非以下5点:

  1. html
  2. css
  3. js
  4. 程序相干的工具
  5. 浏览器

这样咱们再开展去说就能说的分明,不会脱漏。

总结每个大类的时候,先从整个文档格局开始,再到内部资源,再到代码层面

1. html

html应该首先想到语义化标签,正确的语义化能够咱们的文档构造更加清晰。
js文件写在body标签之后,能够让避免阻塞页面的加载。

  • 语义化标签,构造清晰
  • js文件正确搁置,避免阻塞

2.css

  • css文件应该放在body标签顶部,避免页面二次渲染而抖动。
  • 当小图片多的时候能够应用雪碧图,缩小页面申请。
  • 小图标能够用base64格局,缩小页面申请。
  • 公共的css抽离,代码复用
  • 多个css合并,缩小HTTP申请
  • 选择器优化嵌套,尽量避免层级过深,缩短查找过程
  • 充分利用css继承属性,缩小代码量
  • 缩小页面的重绘,能够先用一个变量操作所有款式后,最初一步把这个变量关联到dom上

3.js

  • 抽离公共的js,代码复用
  • 抽离公共的组件,代码复用
  • 定时器记得革除,较少内存的耗用
  • v-if和v-show正确应用,较少页面dom的构建
  • 节流、防抖,避免意外的触发
  • 长列表滚动到可视区域动静加载(大数据渲染)
  • computed 和 watch 辨别应用场景,缩小性能耗费
  • v-for 遍历必须为 item 增加 key,且防止同时应用 v-if

4.webpack

  • 去除代码正文,压缩程序
  • Webpack 对图片进行压缩-------先引入npm install image-webpack-loader --save-dev,而后在 webpack.config.js 中配置
  • 缩小 ES6 转为 ES5 的冗余代码
  • 提取公共代码
  • 模板预编译
  • 优化 SourceMap
  • 构建后果输入剖析
  • 应用webpack-bundle-analyzer查看我的项目所有包及体积大小

5. 浏览器

  • 首屏加载loading,优化体验
  • 应用缓存,缩小反复申请
  • 启用gzip压缩,缩小申请
  • 应用cnd,缩短申请链
  • 应用图片懒加载,组件懒加载,路由懒加载,缩小申请
  • 第三方插件的按需引入
  • 服务端渲染 SSR or 预渲染
  • 应用 Chrome Performance 查找性能瓶颈,针对性的优化

掘金主页
csdn主页