JavaScript核心知识

1.js数据类型

6种原始类型:string,number,boolean,null,undefined,symbol(es6新增);

8种常见引用类型:Object,String,Number,Boolean,Function,Array,Date,RegExp;

判断一个变量的类型使用typeof,typeof的返回值有string,number,boolean,undefined,symbol,object,function;

判断一个对象的原型链上是否包含指定的构造函数;

注意:

typeof null === 'object';  //js历史遗留BUG;

typeof Object=== 'function';typeof String=== 'function';typeof Array=== 'function'  //因为Object,String,Number,Boolean,Function,Array,Date,RegExp本身是一个构造函数;

typeof new Object()=== 'object';typeof new String()=== 'object';typeof new Array()=== 'object'  //凡是new加构造函数生成的变量都是引用类型,使用typeof测试为object,但是typeof new Function() === 'function';

typeof String()=== 'string';typeof Date()=== 'string';typeof Number()=== 'number';typeof Boolean()=== 'boolean';typeof Object()=== 'object';typeof Array()=== 'object';//不使用new直接使用构造函数生成的变量除了String,Number,Boolean,Date是原始类型外(使用instanceof时,String() instanceof String 为false),其他的都是引用类型(使用instanceof时,Array() instanceof Array 为true)。

2.new的作用

  如下代码为例:

扫描二维码关注公众号,回复: 9109860 查看本文章
function Person(age){
	this.age = age;
	console.log(this);
	return age;//如果此处返回的是基本数据类型,则不使用new时,直接返回name数据,使用时返回带有name属性的Person对象
}
Person.prototype.name = "20";
var person1 = new Person("16");
var person2 = Person("18");
 
console.log(person1);
console.log(person2);

  运行结果如下:

  

  结论:

    不使用new时:函数的this指向当前运行上下文,执行完对this的添加属性和方法后,返回函数的运行结果;

    使用new时:函数的this指向一个空对象,执行完对this的添加属性和方法后,返回this,忽略函数本身的return返回值;

    new的作用:

      1.创建一个空对象,并将this指向该对象

      2.将this.__proto__指向该构造函数的prototype

      3.调用构造函数为该对象添加属性和方法

      4.如果构造函数中没有return,返回this这个对象。如果return后面跟原始类型或this,返回this这个对象。如果return后面跟一个引用类型,则忽略掉this对象,返回这个引用类型。

 3.new的简单实现

  代码如下:

function _new(fn,...arg){
    let obj = {};
    obj.__proto__ = fn.prototype;
    fn.apply(obj,arg);
    return obj;
}

                        使用new生成的Person对象                                                   使用_new生成的Person对象

                                                                  

4.前端缓存

  4.1DNS缓存

    DNS查找:浏览器DNS缓存->系统DNS缓存->DNS服务器

  4.2CDN缓存(内容分发网络)

    源服务器的内容分发至分布在各地的代理服务器,客户从最近的一个代理服务器下载资源。

  4.3HTTP缓存

    cache-control:可控制是否缓存,如何缓存;

    Expires:缓存过期时间;

    if-none-Match/ETag:请求资源标识;

    if-Modified-Since/Last-Modified:资源修改时间(只能精确到秒);

    参考链接:https://www.jianshu.com/p/4f07740d68e4

  4.4Service Worker

    Service是一个注册在指定源和路径下的事件驱动Worker,后台独立运行的一个线程;由于不同系统以及不同浏览器的限制,目前可使用Service Sorker拦截处理全栈请求,替代HTTP缓存;

  4.5PageCache与Ajax缓存

    pagecache表示页面缓存,当使用页面缓存是,判断页面需要更新的模块,使用ajax下载模块,然后更新到相应的区域。

   4.6浏览器本地缓存

    cookie:key/value 单个cookie保存的数据不能超过4kb,每次访问都要传送cookie给服务器,可以设置过期时间,和域名绑定

    localstorage:key/value  一直存储于本地硬盘(浏览器中可以删除),一般数据最大5MB(各个浏览器不一样),和域名绑定

    sessionStorage:key/value 关闭页面或浏览器后被清除,最大5MB,和域名绑定

    indexDB:key/object,可以存储对象,浏览器中的数据库,异步,支持事务,和域名绑定,存储空间大

5.Promise的简单实现

function Promise(fn){
    let resolveCallbacks = [];
    let rejectCallbacks = [];
    this.then = (resloveCallback,rejectCallback) => {
        resolveCallbacks.push(resloveCallback);
        rejectCallbacks.push(rejectCallback);
        return new Promise();
    }
    function resolve(res){
        resolveCallbacks.forEach(callback=>{
            callback(res);
        })
    }
    function reject(res){
        rejectCallbacks.forEach(callback=>{
            callback(res);
        })
    }
    fn(resolve,reject);
}

6、JS各大数据类型及其属性、方法、应用;

  6个原始类型:string,number,boolean,null,undefined,symbol;

  8个对象类型:Object,Array,Function,String,Boolean,Number,Date,RegExp;

6.1 String

String构造函数方法

String.fromCharCode()  

 通过一串 Unicode 创建字符串。

String实例方法

String.prototype.charAt()

返回特定位置的字符。

String.prototype.charCodeAt()

返回表示给定索引的字符的Unicode的值。

String.prototype.codePointAt()

返回使用UTF-16编码的给定位置的值的非负整数。

String.prototype.concat()

连接两个字符串文本,并返回一个新的字符串。

String.prototype.includes()

判断一个字符串里是否包含其他字符串。

String.prototype.endsWith()

判断一个字符串的是否以给定字符串结尾,结果返回布尔值。

String.prototype.indexOf()

从字符串对象中返回首个被发现的给定值的索引值,如果没有找到则返回-1。

String.prototype.lastIndexOf()

从字符串对象中返回最后一个被发现的给定值的索引值,如果没有找到则返回-1。

String.prototype.localeCompare()

返回一个数字表示是否引用字符串在排序中位于比较字符串的前面,后面,或者二者相同。

String.prototype.match()

使用正则表达式与字符串相比较。

String.prototype.normalize()

返回调用字符串值的Unicode标准化形式。

String.prototype.padEnd()

在当前字符串尾部填充指定的字符串, 直到达到指定的长度。 返回一个新的字符串。

String.prototype.padStart()

在当前字符串头部填充指定的字符串, 直到达到指定的长度。 返回一个新的字符串。

String.prototype.repeat()

返回指定重复次数的由元素组成的字符串对象。

String.prototype.replace()

被用来在正则表达式和字符串直接比较,然后用新的子串来替换被匹配的子串。

String.prototype.search()

对正则表达式和指定字符串进行匹配搜索,返回第一个出现的匹配项的下标。

String.prototype.slice()

摘取一个字符串区域,返回一个新的字符串。

String.prototype.split()

通过分离字符串成字串,将字符串对象分割成字符串数组。

String.prototype.startsWith()

判断字符串的起始位置是否匹配其他字符串中的字符。

String.prototype.substr()

通过指定字符数返回在指定位置开始的字符串中的字符。

String.prototype.substring()

返回在字符串中指定两个下标之间的字符。

String.prototype.toLocaleLowerCase()

根据当前区域设置,将符串中的字符转换成小写。对于大多数语言来说,toLowerCase的返回值是一致的。

String.prototype.toLocaleUpperCase()

根据当前区域设置,将字符串中的字符转换成大写,对于大多数语言来说,toUpperCase的返回值是一致的。

String.prototype.toLowerCase()

将字符串转换成小写并返回。

String.prototype.toString()

返回用字符串表示的特定对象。重写 Object.prototype.toString 方法。

String.prototype.toUpperCase()

将字符串转换成大写并返回。

String.prototype.trim()

从字符串的开始和结尾去除空格。参照部分 ECMAScript 5 标准。

String.prototype.trimStart()

从字符串的左侧去除空格。

String.prototype.trimEnd()

从字符串的右侧去除空格。

String.prototype.valueOf()

返回特定对象的原始值。重写 Object.prototype.valueOf 方法。

6.2 Array

Array构造函数方法

Array.from()

从类数组对象或者可迭代对象中创建一个新的数组实例。

Array.isArray()

用来判断某个变量是否是一个数组对象。

Array.of()

根据一组参数来创建新的数组实例,支持任意的参数数量和类型。

Array实例方法

修改器方法

下面的这些方法会改变调用它们的对象自身的值:

Array.prototype.copyWithin() 

在数组内部,将一段元素序列拷贝到另一段元素序列上,覆盖原有的值。

Array.prototype.fill() 

将数组中指定区间的所有元素的值,都替换成某个固定的值。

Array.prototype.pop()

删除数组的最后一个元素,并返回这个元素。

Array.prototype.push()

在数组的末尾增加一个或多个元素,并返回数组的新长度。

Array.prototype.reverse()

颠倒数组中元素的排列顺序,即原先的第一个变为最后一个,原先的最后一个变为第一个。

Array.prototype.shift()

删除数组的第一个元素,并返回这个元素。

Array.prototype.sort()

对数组元素进行排序,并返回当前数组。

Array.prototype.splice()

在任意的位置给数组添加或删除任意个元素。

Array.prototype.unshift()

在数组的开头增加一个或多个元素,并返回数组的新长度。

访问方法

下面的这些方法绝对不会改变调用它们的对象的值,只会返回一个新的数组或者返回一个其它的期望值。

Array.prototype.concat()

返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组。

Array.prototype.includes() 

判断当前数组是否包含某指定的值,如果是返回 true,否则返回 false

Array.prototype.join()

连接所有数组元素组成一个字符串。

Array.prototype.slice()

抽取当前数组中的一段元素组合成一个新数组。

Array.prototype.toSource() 

返回一个表示当前数组字面量的字符串。遮蔽了原型链上的 Object.prototype.toSource() 方法。

Array.prototype.toString()

返回一个由所有数组元素组合而成的字符串。遮蔽了原型链上的 Object.prototype.toString() 方法。

Array.prototype.toLocaleString()

返回一个由所有数组元素组合而成的本地化后的字符串。遮蔽了原型链上的 Object.prototype.toLocaleString() 方法。

Array.prototype.indexOf()

返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1。

Array.prototype.lastIndexOf()

返回数组中最后一个(从右边数第一个)与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1。

迭代方法

在下面的众多遍历方法中,有很多方法都需要指定一个回调函数作为参数。在每一个数组元素都分别执行完回调函数之前,数组的length属性会被缓存在某个地方,所以,如果你在回调函数中为当前数组添加了新的元素,那么那些新添加的元素是不会被遍历到的。此外,如果在回调函数中对当前数组进行了其它修改,比如改变某个元素的值或者删掉某个元素,那么随后的遍历操作可能会受到未预期的影响。总之,不要尝试在遍历过程中对原数组进行任何修改,虽然规范对这样的操作进行了详细的定义,但为了可读性和可维护性,请不要这样做。

Array.prototype.forEach()

为数组中的每个元素执行一次回调函数。

Array.prototype.entries() 

返回一个数组迭代器对象,该迭代器会包含所有数组元素的键值对。

Array.prototype.every()

如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。

Array.prototype.some()

如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。

Array.prototype.filter()

将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。

Array.prototype.find() 

找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined

Array.prototype.findIndex() 

找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1

Array.prototype.keys() 

返回一个数组迭代器对象,该迭代器会包含所有数组元素的键。

Array.prototype.map()

返回一个由回调函数的返回值组成的新数组。

Array.prototype.reduce()

从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。

Array.prototype.reduceRight()

从右到左为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。

Array.prototype.values() 

返回一个数组迭代器对象,该迭代器会包含所有数组元素的值。

6.3 Object

Object 构造函数的方法

Object.assign()

通过复制一个或多个对象来创建一个新的对象。

Object.create()

使用指定的原型对象和属性创建一个新对象。

Object.defineProperty()

给对象添加一个属性并指定该属性的配置。

Object.defineProperties()

给对象添加多个属性并分别指定它们的配置。

Object.entries()

返回给定对象自身可枚举属性的 [key, value] 数组。

Object.freeze()

冻结对象:其他代码不能删除或更改任何属性。

Object.getOwnPropertyDescriptor()

返回对象指定的属性配置。

Object.getOwnPropertyNames()

返回一个数组,它包含了指定对象所有的可枚举或不可枚举的属性名。

Object.getOwnPropertySymbols()

返回一个数组,它包含了指定对象自身所有的符号属性。

Object.getPrototypeOf()

返回指定对象的原型对象。

Object.is()

比较两个值是否相同。所有 NaN 值都相等(这与==和===不同)。

Object.isExtensible()

判断对象是否可扩展。

Object.isFrozen()

判断对象是否已经冻结。

Object.isSealed()

判断对象是否已经密封。

Object.keys()

返回一个包含所有给定对象自身可枚举属性名称的数组。

Object.preventExtensions()

防止对象的任何扩展。

Object.seal()

防止其他代码删除对象的属性。

Object.setPrototypeOf()

设置对象的原型(即内部 [[Prototype]] 属性)。

Object.values()

返回给定对象自身可枚举值的数组。

7、JS异步编程

  7.1 Promise对象

  解决回调地狱问题,将回调形式改写成链式调用;

new Promise((resolved,rejected)=>{
    let i = 0;
    setTimeout(()=>{
        resolved(++i);
    },1000)
}).then(res=>{
    return new Promise((resolved,rejected)=>{
    setTimeout(()=>{
        console.log(res)
        resolved(++res);
    },1000)
});}).then(res=>{
    return new Promise((resolved,rejected)=>{
    setTimeout(()=>{
        console.log(res)
        resolved(++res);
    },1000)
});}).then(res=>{
    return new Promise((resolved,rejected)=>{
    setTimeout(()=>{
        console.log(res)
        resolved(++res);
    },1000)
});}).then(res=>{
    setTimeout(()=>{
        console.log(res)
    },1000)})

//输出 1 2 3 4

  7.2 generator函数

  generator函数可以把函数的执行流暂时挂起,等待合适的时机手动调用next()方法继续执行yield关键字后面的语句,可使用next(data)将data作为本次yield语句的返回值;generator函数返回一个可迭代的对象;

function *generator() {
    let i = 0;
    i = yield new Promise((resolve)=>{
        setTimeout(() => {
            resolve(++i);
        },1000);
    });
    console.log(i);
    i = yield new Promise((resolve)=>{
        setTimeout(() => {
            resolve(++i);
        },1000);
    });
    console.log(i)
    i = yield new Promise((resolve)=>{
        setTimeout(() => {
            resolve(++i);
        },1000);
    });
    console.log(i)
    i = yield new Promise((resolve)=>{
        setTimeout(() => {
            resolve(++i);
        },1000);
    });
    console.log(i)
}


let run = (generator,res)=>{
  var result = generator.next(res);
  if(result.done) return;
  result.value.then((res)=>{
    run(generator,res);
  });
}

run(generator());

  8.3 async函数

  async函数是generator函数的语法糖,自带并自动调用类似于run的函数,执行完整个generator函数;

async function async1(){
    let i = 0;
    i = await new Promise((resolved)=>{
        setTimeout(()=>{
            resolved(++i)
        },1000)
    });
    console.log(i)
    i = await new Promise((resolved)=>{
        setTimeout(()=>{
            resolved(++i)
        },1000)
    });
    console.log(i)
    i = await new Promise((resolved)=>{
        setTimeout(()=>{
            resolved(++i)
        },1000)
    });
    console.log(i)
    i = await new Promise((resolved)=>{
        setTimeout(()=>{
            resolved(++i)
        },1000)
    });
    console.log(i)
}
async1()

9、原型和原型链

  原型:每一个构造函数都有一个显示原型对象prototype,prototype对象的所有属性和方法都会被构造函数new出来的实例继承;每一个对象都有一个隐式原型对象__proto__,__proto__指向new出这个对象的构造函数;

  原型链:每一个对象都有一个隐式原型对象__proto__指向他的原型对象prototype(new出这个对象的构造函数的prototype),prototype是一个对象,他也有一个隐式原型对象__proto__指向他自己的原型对象prototype;这样一层一层的指向形成的一条链式指向叫做原型链;

  总结三点:原文链接https://blog.csdn.net/u012443286/article/details/78823955

  1.每个构造函数都有一个原型对象 

  2.原型对象都包含一个指向构造函数的指针

  3.实例都包含一个指向原型对象的内部指针

10、对象的封装与继承

  参考原文1:https://segmentfault.com/a/1190000013795390

  参考原文2:https://www.jianshu.com/p/2aaaacd8bad3

  

  class类:

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes

    特性1:class中定义的方法中的this如果没有指定this的值,this值将为undefined。class方法中的this只能指向class内部,不能指向外部;

   例:

class Animal { 
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined

Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined

  特性2:class定义的类不会存在变量提升;

  特性3:使用 extends 继承父类;

  特性4:如果构造函数constructor中没有return,返回this这个对象。如果return后面跟原始类型或this,返回this这个对象。如果return后面跟一个引用类型,则忽略掉this对象,返回这个引用类型。

  特性5:super关键字用于访问和调用一个对象的父对象上的函数。//

11、对象的深拷贝

  方案1:只能实现属性中没有undefinedfunctionsymbol的深拷贝

  方法:使用JSON.stringify/JSON.parse

  局限性:undefinedfunctionsymbol 会在转换过程中会被忽略;


const originObj = {

  name:'axuebin',

  sex:undefined,

  sayHello:function(){

    console.log('Hello World');

  }

}

console.log(originObj); // {name: "axuebin", sex: undefined, sayHello: ƒ}

const cloneObj = JSON.parse(JSON.stringify(originObj));

console.log(cloneObj); // {name: "axuebin"}

  方法2:只能实现属性中没有对象或数组的深拷贝

  数组的深拷贝:let _newArray = oldArray.slice();  let _newArray2 =  old.concat();

  对象的深拷贝:let _newArray = Object.ossign({},oldObject);

  局限性:假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值;

  方法3:递归实现任何对象的深拷贝

function deepClone(obj){    //可深度复制所有原始类型、数组、Object对象、函数、Date、
  if(obj instanceof Object){
    let new_obj = null;
    switch(obj.constructor){
        case Array:
            new_obj = [];
            for (let key of Object.keys(obj)){
              new_obj[key] = deepClone(obj[key]);
            }
            break;
        case Object:
            new_obj = {};
            for (let key of Object.keys(obj)){
              new_obj[key] = deepClone(obj[key]);
            }
            break;
        case Function:
            new_obj = eval('(' + obj.toString() + ')');
            break;
        case Date:
            new_obj = new Date(obj.getTime());
            break;
        default :
            new_obj = obj;
    }
    return new_obj;
  }else {
    return obj;
  }
}

12、闭包

  闭包是对一个作用域(通常是函数作用域)中的变量引用的集合(对象或者函数);

  MDN闭包链接:MDN闭包链接

  例:使用闭包实现模块化管理变量;

var Counter = (function() {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }   
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */

13、JS事件

  13.1 事件模型

    1 DOM0模型

    使用onclick在元素上绑定事件,或者使用ducument.getElementById('#id').onclick = function(){}绑定事件;

    2 DOM2模型(W3C制定的标准模型)

    使用document.getElementById('#id').addEventList(type,listener,options)绑定事件;其中type表示事件类型,例:click;其中listener表示触发的行为,例:function(){};其中options表示触发的阶段,例:false(表示冒泡阶段触发);

    3 IE事件模型

    

  13.2 事件委托

    将多个子节点的事件委托给祖先节点处理;

  例:将多个button子节点的事件委托给父节点'box'来处理;

window.onload = function(){
            var oBox = document.getElementById("box");
            oBox.onclick = function (ev) {
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLocaleLowerCase() == 'input'){
                    switch(target.id){
                        case 'add' :
                            alert('添加');
                            break;
                        case 'remove' :
                            alert('删除');
                            break;
                        case 'move' :
                            alert('移动');
                            break;
                        case 'select' :
                            alert('选择');
                            break;
                    }
                }
            }
            
        }

  13.3 事件阻止

1.event.stopPropagation()方法

这是阻止事件的冒泡方法,不让事件向documen上层蔓延,但是默认事件任然会执行,当你调用这个方法的时候,如果点击一个连接,这个连接仍然会被打开。

2.event.preventDefault()方法

这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素,此方法可以控制一些按钮在点击时只触发时间而不会引起表单的提交。

3.return false;

这个方法比较暴力,他会同事阻止事件冒泡也会阻止默认事件;写上此代码,连接不会被打开,事件也不会传递到上一层的父元素;可以理解为return false就等于同时调用了event.stopPropagation()和event.preventDefault()

14、AMD、CommonJS与ES6模块

  异同:

    CommonJS:同步加载适用于服务器端、模块的导入导出 exports<->require;

    AMD:异步加载适用于客户端、模块的导入导出 define<->require、依赖前置;

    ES6模块:ES6只规定了模块的语法,并没有规定模块的加载方式(可自定义加载方式),模块的导入导出 export<->import;

  ES6模块特点:

    1.不管是否在代码头部添加 use scrict ,都启用严格模式;

    2.模块内的变量为局部变量,不影响全局变量;

    3.每个模块只加载一次,之后的加载直接从内存中获取,即单例模式加载模块;

    4.import命令会提升到模块顶部,首先执行;

  ES6模块的基本用法:

    1.导出的变量和函数必须要有名称(export default 命令另外考虑);

    2.as可模块进行导出或导入时对模块变量重命名;

      例:

/*-----export [test.js]-----*/
let myName = "Tom";
export { myName as exportName }                  //myName变量导出时以变量名exportName导出
 
/*-----import [xxx.js]-----*/
import { exportName } from "./test.js";
console.log(exportName);// Tom
使用 as 重新定义导出的接口名称,隐藏模块内部的变量
/*-----export [test1.js]-----*/
let myName = "Tom";
export { myName }
/*-----export [test2.js]-----*/
let myName = "Jerry";
export { myName }
/*-----import [xxx.js]-----*/
import { myName as name1 } from "./test1.js";
import { myName as name2 } from "./test2.js";    //myName变量导入时以变量名name2导入
console.log(name1);// Tom
console.log(name2);// Jerry

  3. import命令的特点

    3.1只读:import导入的变量不可进行改写,但可对其属性进行改写;

    3.2单例模式:import再次导入同一模块时,直接从内存中导入;

  4.export default 命令:只能有一个,在导入时可使用任何变量名接收,不需要使用 { }。

  5.require与import的异同

    require默认导出模块内的全部变量,import * as mod from 'model.js'导出全部变量(包含默认变量),import {a, b} from 'model.js'导出a和b变量,import mod from 'model.js'导出默认变量,import mod, {a, b} from 'model.js'导出默认变量和a,b变量。

    

    

15、JS编译原理

  15.1 js的编译过程

     1.词法分析:将字符流转换成记号流

     2.语法分析:将记号流抽象成AST语法树

     3.预编译:通过编译器对所有的变量和函数进行声明生成作用域;

     4.解释执行:在作用域及作用域链中查找所操作的变量及函数,然后进行操作;

 15.2 JIT编译优化

    在js引擎执行代码的时候增加一个监视器(也叫分析器),如多同一段代码被运行了几次就会被标记为'warm',如果被运行了很多次就会被标记为'hot';

    基线编译器:如果一段代码被标记了'warm',JIT就把这段代码送到基线编译器去编译,将编译后的结果替换掉那段代码;

    优化编译器:如果一段代码被标记了'hot',JIT就把这段代码送到优化编译器去编译,生成一个更快速和高效的代码版替换掉那段代码;

    去优化:如果在执行的过程中,这段相同的代码不再相同了,执行过程将会回到基线编译器或解释器,这一过程叫做去优化。

16、JS的AST抽象语法树

  JS的AST抽象语法树是在对JS代码进行此法分析后,进行语法分析生成的一个抽象语法树。

  npm上的recast工具可以将js代码转化为AST语法树,并可进行修改,再转换成js代码,也可以直接构建代码。

17、async与defer的区别

先来试个一句话解释仨,当浏览器碰到 script 脚本的时候:

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

    没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

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

    有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。异步加载,加载完成立即执行指定的脚本,不考虑HTML是否解析完成。

  3. <script defer src="myscript.js"></script>

    有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。异步加载,加载完成并在HTML解析后,按定义的顺序执行指定的脚本。

然后从实用角度来说呢,首先把所有脚本都丢到 </body> 之前是最佳实践,因为对于旧浏览器来说这是唯一的优化选择,此法可保证非脚本的其他一切元素能够以最快的速度得到加载和解析。

接着,我们来看一张图咯:

                    请输入图片描述

蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

此图告诉我们以下几个要点:

  1. defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
  2. 它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的
  3. 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
  4. async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行
  5. 仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics

18、()小括号

var b = 10;
(function b() {
  b = 20;
  console.log(b)
})()

针对这题,在知乎上看到别人的回答说:

  1. 函数表达式与函数声明不同,函数名只在该函数内部有效,并且此绑定是常量绑定。
  2. 对于一个常量进行赋值,在 strict 模式下会报错,非 strict 模式下静默失败。
  3. IIFE中的函数是函数表达式,而不是函数声明。
发布了20 篇原创文章 · 获赞 20 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/csj41352/article/details/102536467