根据大神们整理的前端自检清单,本身整理一下答案,也方便本身学习。javascript
js目前共定义了8种语言类型,其中包括:Undefined,Null,Boolean,Number,String,Object, Symbol,BigIntcss
JavaScript基本类型数据都是直接按值存储在栈中的(Undefined、Null、不是new出来的布尔、数字和字符串),每种类型的数据占用的内存空间的大小是肯定的,并由系统自动分配和自动释放。这样带来的好处就是,内存能够及时获得回收,相对于堆来讲 ,更加容易管理内存空间。html
JavaScript引用类型数据被存储于堆中 (如对象、数组、函数等,它们是经过拷贝和new出来的)。其实,说存储于堆中,也不太准确,由于,引用类型的数据的地址指针是存储于栈中的,当咱们想要访问引用类型的值的时候,须要先从栈中得到对象的地址指针,而后,在经过地址指针找到堆中的所须要的数据。前端
1.使用Symbol来替代常量vue
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
复制代码
2.使用Symbol来做为对象属性名(key)java
const PROP_NAME = Symbol()
const PROP_AGE = Symbol()
let obj = {
[PROP_NAME]: "一斤代码"
}
obj[PROP_AGE] = 18
复制代码
3.使用Symbol定义类的私有属性/方法node
// a.js
const PASSWORD = Symbol()
class Login {
constructor(username, password) {
this.username = username
this[PASSWORD] = password
}
checkPassword(pwd) {
return this[PASSWORD] === pwd
}
}
export default Login
// b.js
import Login from './a'
const login = new Login('admin', '123456')
login.checkPassword('123456') // true
login.PASSWORD // oh!no!
login[PASSWORD] // oh!no!
login["PASSWORD"] // oh!no!
复制代码
因为Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再建立一个如出一辙的Symbol出来(由于Symbol是惟一的),所以这个PASSWORD的Symbol只能被限制在a.js内部使用,因此使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。react
4.注册和获取全局Symbol window中建立的Symbol实例老是惟一的,而咱们须要的是在全部这些window环境下保持一个共享的Symbol。这种状况下,咱们就须要使用另外一个API来建立或获取Symbol,那就是Symbol.for(),它能够注册或获取一个window间全局的Symbol实例:webpack
let gs1 = Symbol.for('global_symbol_1') //注册一个全局Symbol
let gs2 = Symbol.for('global_symbol_1') //获取全局Symbol
gs1 === gs2 // true
复制代码
基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,经过按值访问 引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript不容许直接访问堆内存中的位置,所以操做对象时,实际操做对象的引用nginx
let a1 = 0; // 栈内存
let a2 = "this is string" // 栈内存
let a3 = null; // 栈内存
let b = { x: 10 }; // 变量b存在于栈中,{ x: 10 }做为对象存在于堆中
let c = [1, 2, 3]; // 变量c存在于栈中,[1, 2, 3]做为对象存在于堆中
复制代码
内置对象: Object是 JavaScript 中全部对象的父对象 数据封装类对象:Object、Array、Boolean、Number 和 String 其余对象:Function、Math、Date、RegExp、Error。 特殊的基本包装类型(String、Number、Boolean) arguments: 只存在于函数内部的一个类数组对象 装箱: 把基本数据类型转化为对应的引用数据类型的操做,装箱分为隐式装箱和显示装箱 隐式装箱
let a = 'sun'
let b = a.indexof('s') // 0 // 返回下标
复制代码
上面代码在后台实际的步骤为:
let a = new String('sun')
let b = a.indexof('s')
a = null
复制代码
实现机制:
1.建立String类型的一个实例; 2.在实例上调用指定的方法; 3.销毁这个实例;
显示装箱 经过内置对象能够对Boolean、Object、String等能够对基本类型显示装箱 let a = new String('sun')
拆箱: 拆箱和装箱相反,就是把引用类型转化为基本类型的数据,一般经过引用类型的valueof()和toString()方法实现
let name = new String('sun')
let age = new Number(24)
console.log(typeof name) // object
console.log(typeof age) // object
// 拆箱操做
console.log(typeof age.valueOf()); // number // 24 基本的数字类型
console.log(typeof name.valueOf()); // string // 'sun' 基本的字符类型
console.log(typeof age.toString()); // string // '24' 基本的字符类型
console.log(typeof name.toString()); // string // 'sun' 基本的字符类型
复制代码
(1) 做为函数的参数,表示该函数的参数不是对象。
(2) 做为对象原型链的终点。
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function等6种数据类型。
instanceof是用来判断A是否为B的实例时,表达式为:A instanceof B,若是 A是B的实例,则返回true; 不然返回false 在这里特别注意的是 instanceof检测的是原型
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,… 基本上全部对象的类型均可以经过这个方法获取到。 4 constructor 查看对象对应的构造函数 construvtor在对应对象的原型下面,是自动生成的,当咱们写一个构造函数的时候,程序自动添加,构造函数名.prototype.constructor = 构造函数名
0.1+0.2不等于0.3,是由于计算机进行计算时先转化成二进制,二浮点数用二进制表示时是无穷位的,IEEE754标准中用64位表示(1位用来表示符号位,11用来表示指数,52位表示尾数)会截断后面的位数,再转化成十进制,就有了偏差。
最大数字:
对于整数,前端出现问题的概率可能比较低,毕竟不多有业务须要须要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。
对于小数,前端出现问题的概率仍是不少的,尤为在一些电商网站涉及到金额等数据。解决方式:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)
最安全的数字-Math.pow(2, 53)-1,到+Math.pow(2, 53)-1
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
while (true) {
if (leftVaule === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
leftVaule = leftVaule.__proto__
}
}
复制代码
// 原型继承
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
this.subProty =false;
}
SubType.prototype = new SuperType();
var instance = new SubType();
console.log(instance.getSuperValue())
// 借用构造函数
function SuperType(name) {
this.name = name;
}
function SubType(){
SuperType.call(this, 'demo');
this.age = 18;
}
var instance = new SubType();
console.log(instance.name);
console.log(instance.age);
// 组合继承
function SuperType(name){
this.name = name;
this.colors = ['red'];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age) {
SuperType.call(this,name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var instance = new SubType('demo',18);
instance.sayAge();
instance.sayName();
// 原型式继承
function object(o) {
function F(){};
F.prototype = o;
return new F();
}
var person = {
name: 'tom'
}
var anotherPerson = object(person)
console.log(anotherPerson.name)
// 寄生式继承
function createAnother(original){
var clone =Object.create(original);
clone.sayHi = function () {
console.log('hi');
}
return clone;
}
var person = {
name: 'tom'
}
var anotherPerson = createAnother(person);
console.log(anotherPerson.name)
anotherPerson.sayHi();
// 寄生组合式继承
function SuperType(name) {
this.name = name;
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
function inheritPrototype(subType,superType){
var prototype = Object.create(superType.prototype);
prototype.constructor =subType;
subType.prototype = prototype;
}
inheritPrototype(SubType,SuperType);
var person = new SubType('zhangsan',18);
person.sayName()
复制代码
function Person (name,age){
this.name = name;
this.age = age;
this.say = function () {
console.log("I am " + this.name)
}
}
function realizeNew(){
let obj = {};
let Con = [].shift.call(arguments);
obj.__proto__ = Con.prototype;
let result = Con.apply(obj,arguments);
return typeof result === 'object'? result : obj
}
var person1 =realizeNew(Person,'张三')
复制代码
词法做用域,函数的做用域在函数定义的时候就决定了(取决于函数定义的位置)
动态做用域,函数的做用域在函数调用的时候就决定了(取决于函数的调用) js采用的是词法做用域
做用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说做用域最大的用处就是隔离变量,不一样做用域下同名变量不会有冲突。 ES6 以前 JavaScript 没有块级做用域,只有全局做用域和函数做用域。ES6的到来,为咱们提供了‘块级做用域’,可经过新增命令let和const来体现。
function outFun2() {
var inVariable = "内层变量2";
}
outFun2();//要先执行这个函数,不然根本不知道里面是啥
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined
复制代码
做用域链 在 JavaScript 中使用变量时,JavaScript 引擎将尝试在当前做用域中查找变量的值。若是找不到变量,它将查找外部做用域并继续这样作,直到找到变量或到达全局做用域为止。
若是仍然找不到变量,它将在全局做用域内隐式声明变量(若是不是在严格模式下)或返回错误。 ####3.理解JavaScript的执行上下文栈,能够应用堆栈信息快速定位问题 执行上下文是当前 JavaScript 代码被解析和执行时所在环境的抽象概念。
执行上下文的类型 执行上下文总共有三种类型
全局执行上下文:只有一个,浏览器中的全局对象就是 window 对象,this 指向这个全局对象。
函数执行上下文:存在无数个,只有在函数被调用的时候才会被建立,每次调用函数都会建立一个新的执行上下文。
Eval 函数执行上下文: 指的是运行在 eval 函数中的代码,不多用并且不建议使用。
执行栈 执行栈,也叫调用栈,具备 LIFO(后进先出)结构,用于存储在代码执行期间建立的全部执行上下文。 首次运行JS代码时,会建立一个全局执行上下文并Push到当前的执行栈中。每当发生函数调用,引擎都会为该函数建立一个新的函数执行上下文并Push到当前执行栈的栈顶。 根据执行栈LIFO规则,当栈顶函数运行完成后,其对应的函数执行上下文将会从执行栈中Pop出,上下文控制权将移到当前执行栈的下一个执行上下文。
执行上下文的建立 执行上下文分两个阶段建立:
1)建立阶段;
2)执行阶段
建立阶段
一、肯定 this 的值,也被称为 This Binding。
二、LexicalEnvironment(词法环境) 组件被建立。
三、VariableEnvironment(变量环境) 组件被建立。
This Binding:
在全局执行上下文中,this 的值指向全局对象,在浏览器中,this 的值指向 window 对象。 在函数执行上下文中,this 的值取决于函数的调用方式。若是它被一个对象引用调用,那么 this 的值被设置为该对象,不然 this 的值被设置为全局对象或 undefined(严格模式下)
词法环境
在词法环境中,有两个组成部分:
(1)环境记录(environment record)
(2)对外部环境的引用 环境记录是存储变量和函数声明的实际位置。 对外部环境的引用意味着它能够访问其外部词法环境。 变量环境:
它也是一个词法环境,其 EnvironmentRecord 包含了由 VariableStatements 在此执行上下文建立的绑定。 如上所述,变量环境也是一个词法环境,所以它具备上面定义的词法环境的全部属性。 在 ES6 中,LexicalEnvironment 组件和 VariableEnvironment 组件的区别在于前者用于存储函数声明和变量( let 和 const )绑定,然后者仅用于存储变量( var )绑定。 在建立阶段,代码会被扫描并解析变量和函数声明,其中函数声明存储在环境中,而变量会被设置为 undefined(在 var 的状况下)或保持未初始化(在 let 和 const 的状况下) 这就是为何你能够在声明以前访问 var 定义的变量(尽管是 undefined ),但若是在声明以前访问 let 和 const 定义的变量就会提示引用错误的缘由。
这就是咱们所谓的变量提高。
this 既不指向函数自身,也不指函数的词法做用域,而是调用函数时的对象!
一)普通函数的调用,this指向的是Window
var name = '卡卡';
function cat(){
var name = '有鱼';
console.log(this.name);//卡卡
console.log(this);//Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
}
cat();
复制代码
(二)对象的方法,this指的是该对象
一、一层做用域链时,this指的该对象
var name = '卡卡';
var cat = {
name:'有鱼',
eat:function(){
console.log(this.name);//有鱼
}
}
cat.eat();
复制代码
二、多层做用域链时,this指的是距离方法最近的一层对象
var name = '卡卡';
var cat = {
name:'有鱼',
eat1:{
name:'年年',
eat2:function(){
console.log(this.name);//年年
}
}
}
cat.eat1.eat2();
复制代码
这里须要注意一个状况,若是cat.eat1.eat2这个结果赋值给一个变量eat3,则eat3()的值是多少呢?
var eat3 = cat.eat1.eat2;
eat3(); // 卡卡
复制代码
答案是[卡卡],这个是由于通过赋值操做时,并未发起函数调用,eat3()这个才是真正的调用,而发起这个调用的是根对象window,因此this指的就是window,this.name=卡卡
(三)构造函数的调用,this指的是实例化的新对象
var name = '卡卡';
function Cat(){
this.name = '有鱼';
this.type = '英短蓝猫';
}
var cat1 = new Cat();
console.log(cat1);// 实例化新对象 Cat {name: "有鱼", type: "英短蓝猫"}
console.log(cat1.name);// 有鱼
复制代码
(四)apply和call调用时,this指向参数中的对象
var name = '有鱼';
function eat(){
console.log(this.name);
}
var cat = {
name:'年年',
}
var dog = {
name:'高飞',
}
eat.call(cat);// 年年
eat.call(dog);// 高飞
复制代码
(五)匿名函数调用,指向的是全局对象
var name = '卡卡';
var cat = {
name:'有鱼',
eat:(function(){
console.log(this.name);//卡卡
})()
}
cat.eat;
复制代码
(六)定时器中调用,指向的是全局变量
var name = '卡卡';
var cat = setInterval(function(){
var name = '有鱼';
console.log(this.name);// 卡卡
clearInterval(cat);
},500);
复制代码
总结: ①普通函数的调用,this指向的是window
②对象方法的调用,this指的是该对象,且是最近的对象
③构造函数的调用,this指的是实例化的新对象
④apply和call调用,this指向参数中的对象
⑤匿名函数的调用,this指向的是全局对象window
⑥定时器中的调用,this指向的是全局变量window
包就是可以读取其余函数内部变量的函数 它的最大用处有两个,一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。
function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000
复制代码
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证实了,函数f1中的局部变量n一直保存在内存中,并无在f1调用后被自动清除。 为何会这样呢?缘由就在于f1是f2的父函数,而f2被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。 这段代码中另外一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,所以nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数自己也是一个闭包,因此nAdd至关因而一个setter,能够在函数外部对函数内部的局部变量进行操做。
一、内存泄露:是指申请的内存执行完后没有及时的清理或者销毁,占用空闲内存,内存泄露过多的话,就会致使后面的程序申请不到内存。所以内存泄露会致使内部内存溢出 二、堆栈溢出:是指内存空间已经被申请完,没有足够的内存提供了
常见的手段是将一个变量置为null,该变量就会被下一轮垃圾回收机制回收。 常见的内存泄露的缘由:
微任务包括: 原生Promise(有些实现的promise将then方法放到了宏任务中),Object.observe(已废弃), MutationObserver, MessageChannel;只有promise调用then的时候,then里面的函数才会被推入微任务中 宏任务包括:setTimeout, setInterval, setImmediate, I/O;
function execute(tasks){
return tasks.reduce((previousPromise, currentPromise)=>previousPromise.then(resultList=>{
return new Promise(resolve=>{
currentPromise().then(result=>{
resolve(resultList.concat(result))
}).catch(()=>{
resolve(resultList.concat(null))
})
})
},Promise.resolve([])))
}
const execute = (tasks = []) => {
const resultList = [];
for(task of tasks){
try{
resultList.push(await tasks())
}catch(err){
resultList.push(null);
}
}
return resultList;
}
复制代码
Node 10之前: 执行完一个阶段的全部任务 执行完nextTick队列里面的内容 而后执行完微任务队列的内容 Node 11之后: 和浏览器的行为统一了,都是每执行一个宏任务就执行完微任务队列。
ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现
function mySetInterval(fn,mil){
function interval(){
setTimeout(interval,mil);
fn();
}
setTimeout(interval,mil)
}
mySetInterval(function(){console.log(1)},1000)
复制代码
1. W3C 标准盒模型: 属性width,height只包含内容content,不包含border和padding。 2. IE 盒模型: 属性width,height包含border和padding,指的是content+padding+border。 css的盒模型由content(内容)、padding(内边距)、border(边框)、margin(外边距)组成。但盒子的大小由content+padding+border这几部分决定,把margin算进去的那是盒子占据的位置,而不是盒子的大小! 咱们在编写页面代码时应尽可能使用标准的W3C模型(需在页面中声明DOCTYPE类型),这样能够避免多个浏览器对同一页面的不兼容。 由于若不声明DOCTYPE类型,IE浏览器会将盒子模型解释为IE盒子模型,FireFox等会将其解释为W3C盒子模型;若在页面中声明了DOCTYPE类型,全部的浏览器都会把盒模型解释为W3C盒模型。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.parent1{
width: 200px;
height: 200px;
background: red;
position: relative;
}
.parent2{
display: table-cell;
vertical-align: middle;
text-align: center;
}
.parent5{
display: flex;
align-items: center;
justify-content: center;
}
.child1{
width: 100px;
height: 100px;
position: absolute;
left: 50%;
top:50%;
margin-left: -50px;
margin-top: -50px;
line-height: 100px;
text-align: center;
}
.child2{
position: absolute;
left: 50%;
top:50%;
transform: translate(-50%,-50%);
}
.child3{
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
width: 100px;
height: 100px;
line-height: 100px;
text-align: center;
}
.child4{
display: inline-block;
width: 100px;
height: 50px;
overflow: scroll;
}
.child6{
width: 100px;
height: 100px;
display: inline-block;
text-align: center;
vertical-align: middle;
line-height: 100px;
}
.parent6{
text-align: center;
}
.parent6::after{
content: '';
height: 100%;
vertical-align: middle;
display: inline-block;
}
</style>
</head>
<body>
<p>方案一:知道宽度的状况下 absolute+margin负值</p>
<div class="parent1">
<div class="child1">child1</div>
</div>
<p>方案二:不知道宽度的状况下 absolute+transform</p>
<div class="parent1">
<div class="child2">child2</div>
</div>
<p>方案三:不知道宽度的状况下 absolute+margin:auto</p>
<div class="parent1">
<div class="child3">child3</div>
</div>
<p>方案四:多行文本垂直居中 table-cell vertical-align:middle</p>
<div class="parent1 parent2">
<div class="child4">多行文本垂直居中 table-cell vertical-align:middle多行文本垂直居中 table-cell vertical-align:middle多行文本垂直居中 table-cell vertical-align:middle</div>
</div>
<p>方案五:display:flex</p>
<div class="parent1 parent5">
<div class="child5">flex</div>
</div>
<p>方案六:伪元素</p>
<div class="parent1 parent6">
<div class="child6">伪元素</div>
</div>
</body>
</html>
复制代码
js是一门解释型语言(英语:Interpreted language),是一种编程语言。这种类型的编程语言,会将代码一句一句直接运行,不须要像编译语言(Compiled language)同样,通过编译器先行编译为机器码,以后再运行。这种编程语言须要利用解释器,在运行期,动态将代码逐句解释(interpret)为机器码,或是已经预先编译为机器码的的子程序,以后再运行。
###设计模式
Function.prototype.myCall = function (context = window,...args) {
context.fn = this;
console.log(args)
let result = context.fn(...args);
delete context.fn;
return result;
}
Function.prototype.myApply = function (context = window, arr) {
context.fn = this;
let result = !arr ? context.fn() : context.fn(...arr);
delete context.fn;
return result;
}
Function.prototype.myBind = function (context = window , ...arg) {
context.fn = this;
let bound = function () {
let args = [...args].concat(...arguments);
context.fn = this instanceof context.fn ? this : context;
let result = context.fn(...args);
delete context.fn;
return result;
}
bound.prototype = new this();
return bound;
}
复制代码
function handlePromise(promise2, x, resolve, reject){
if (promise2 === x) { //promise2是否等于x,也就是判断是否将本身自己返回
return reject(new TypeError('circular reference')); //若是是抛出错误
}
if(x !==null && (typeof x==='object' || typeof x ==='function')) {
let called; //called控制resolve或reject 只执行一次,屡次调用没有任何做用。
try{
let then = x.then;
if(typeof then === 'function') {
then.call(x,y=>{
if(called) return;
called = true;
handlePromise(promise2,y,resolve,reject)
},r => {
if(called) return;
called = true;
reject(r);
})
} else {
reject(x);
}
}catch(err) {
if (called) return;
called = true;
reject(err);
}
} else {
reject(x);
}
}
class Promise {
constructor(executor){
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if(this.status === 'pending') {
this.value = value;
this.status = 'resolved';
this.onResolvedCallbacks.foEach(fn => fn(this.value));
}
}
let reject = (reason) => {
if(this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
this.onRejectedCallbacks.foEach(fn => fn(this.reason));
}
}
try{
executor(resolve,reject)
}catch(err){
reject(err);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y;
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
let promise2; // 返回的新的promise
if (this.status === 'pending') {
this.onResolvedCallbacks.push()
promise2 = new Promise((resolve,reject)=>{
this.onResolvedCallbacks.push(()=>{
setTimeout(()=>{
try{
let x = onFulfilled(this.value);
handlePromise(promise2,x,resolve,reject)
}catch(err){
reject(err);
}
},0)
})
this.onRejectedCallbacks.push(()=>{
setTimeout(()=>{
try{
let x = onRejected(this.reason);
handlePromise(promise2,x,resolve,reject)
}catch(err){
reject(err);
}
},0)
})
})
}
if (this.status === 'resolved') {
promise2 = new Promise((resolve,reject) =>{
setTimeout(()=>{
try{
let x =onFulfilled(this.value);
handlePromise(promise2,x,resolve,reject)
}catch(err){
reject(err);
}
},0)
})
}
if(this.status === 'rejected') {
onRejected(this.reason);
promise2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
try{
let x = onRejected(this.reason);
handlePromise(promise2,x,resolve,reject)
}catch(err){
reject(err);
}
},0)
})
}
return promise2;
}
catch(onRejected){ //在此处添加原型上的方法catch
return this.then(null,onRejected);
}
}
Promise.all = function (promiseArrs) { //在Promise类上添加一个all方法,接受一个传进来的promise数组
return new Promise((resolve, reject) => { //返回一个新的Promise
let arr = []; //定义一个空数组存放结果
let i = 0;
function handleData(index, data) { //处理数据函数
arr[index] = data;
i++;
if (i === promiseArrs.length) { //当i等于传递的数组的长度时
resolve(arr); //执行resolve,并将结果放入
}
}
for (let i = 0; i < promiseArrs.length; i++) { //循环遍历数组
promiseArrs[i].then((data) => {
handleData(i, data); //将结果和索引传入handleData函数
}, reject)
}
})
}
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject);
}
})
}
Promise.resolve = function (val) {
return new Promise((resolve, reject) => resolve(val));
}
Promise.reject = function (val) {
return new Promise((resolve, reject) => reject(val));
}
module.exports = Promise;
复制代码
class EventEmitter {
constructor(){
this.events = {}
}
on(eventName,callback){
if(this.events[eventName]){
this.events[eventName].push(callback)
} else{
this.events[eventName] = [callback]
}
}
emit(eventName,...rest){
if(this.events[eventName]){
this.events[eventName].forEach(fn=>fn.apply(this,rest))
}
}
}
const event =new EventEmitter();
const handle = (...pyload) => console.log(pyload)
event.on('click',handle)
event.emit('click',1,2,3)
复制代码
window.JSON = {
parse: function (str) {
return eval('(' + str + ')') //防止{}会返回undefined
},
stringify: function (str) {
if (typeof str === 'number') {
return Number(str);
}
if (typeof str === 'string') {
return str
};
var s = '';
console.log(Object.prototype.toString.call(str))
switch (Object.prototype.toString.call(str)) {
case '[object Array]':
s += '[';
for (var i = 0; i < str.length - 1; i++) {
if (typeof str === 'string') {
s += '"' + str[i] + '",'
} else {
s += str[i] + ','
}
}
if (typeof str[str.length - 1] == 'string') {
s += '"' + str[i] + '"'
} else {
if (str[str.length - 1] == null) {
str[str.length - 1] = null;
s += 'null';
} else {
s += (str[str.length - 1] ? str[str.length - 1] : '')
}
}
s += "]";
break;
case '[object Date]':
console.log(str.toJSON())
s+='"'+(str.toJSON?str.toJSON():str.toString())+'"';
break;
case '[object Function]':
s= 'undefined';
break
case '[object Object]':
s+='{'
for(var key in str) {
if(str[key] === undefined){
continue;
}
if(typeof str[key] === 'symbol' || typeof str[key] === 'function') {
continue;
}
if(typeof Object.prototype.toString.call(str[key]) === '[object RegExp]') {
continue;
}
s+=('"'+key+'":"'+str[key]+'",')
}
s = s.slice(0,s.length-1);
if(s===''){s+='{'}
s+='}'
break
}
return s;
}
}
复制代码
当Render Tree中部分或所有元素的尺寸、结构、或某些属性发生改变时,浏览器从新渲染部分或所有文档的过程称为回流。 会致使回流的操做:
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并从新绘制它,这个过程称为重绘。 CSS
JavaScript
什么叫再也不继续使用的变量?
再也不使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程当中存在,当函数运行结束,没有其余引用(闭包),那么该变量会被标记回收。
全局变量的生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成垃圾回收。
工做原理:
当变量进入环境时(例如在函数中声明一个变量),将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工做流程:
到2008年为止,IE、Chorme、Fireofx、Safari、Opera 都使用标记清除式的垃圾收集策略,只不过垃圾收集的时间间隔互有不一样
避免内存泄漏的方法
const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"
复制代码
复制代码这种状况下,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap 保存的这个键值对,也会自动消失。
####10.浏览器采用的缓存方案,如何选择和控制合适的缓存方案 缓存过程分析 浏览器与服务器通讯的方式为应答模式,便是:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程以下图:
浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中
以上两点结论就是浏览器缓存机制的关键,他确保了每一个请求的缓存存入与读取,只要咱们再理解浏览器缓存的使用规则,那么全部的问题就迎刃而解了,本文也将围绕着这点进行详细分析。为了方便你们理解,这里咱们根据是否须要向服务器从新发起HTTP请求将缓存过程分为两个部分,分别是强制缓存和协商缓存 。
强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强制缓存的状况主要有三种(暂不分析协商缓存过程),以下:
不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致),以下图:
存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存(暂不分析),以下图
存在该缓存结果和缓存标识,且该结果还没有失效,强制缓存生效,直接返回该结果,以下图
public:全部内容都将被缓存(客户端和代理服务器均可缓存)
private:全部内容只有客户端能够缓存,Cache-Control的默认取值
no-cache:客户端缓存内容,可是是否使用缓存则须要通过协商缓存来验证决定
no-store:全部内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效 到了HTTP/1.1,Expire已经被Cache-Control替代,缘由在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间作对比,那么若是客户端与服务端的时间由于某些缘由(例如时区不一样;客户端和服务端有一方的时间不许确)发生偏差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无心义,
因为Cache-Control的优先级比expires,那么直接根据Cache-Control的值进行缓存,意思就是说在600秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。
注:在没法肯定客户端的时间是否与服务端的时间同步的状况下,Cache-Control相比于expires是更好的选择,因此同时存在时,只有Cache-Control生效。
浏览器的缓存存放在哪里,如何在浏览器中判断强制缓存是否生效?
内存缓存(from memory cache):内存缓存具备两个特色,分别是快速读取和时效性: 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程必定的内存资源,以方便下次运行使用时的快速读取。 时效性:一旦该进程关闭,则该进程的内存则会清空。
硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存须要对该缓存存放的硬盘文件进行I/O操做,而后从新解析该缓存内容,读取复杂,速度比内存缓存慢。
在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,因此每次渲染页面都须要从硬盘读取缓存(from disk cache)。
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有如下两种状况:
###数据流管理
yarn是通过从新设计的崭新的npm客户端,它能让开发人员并行处理全部必须的操做,并添加了一些其余改进。 运行速度获得了显著的提高,整个安装时间也变得更少 像npm同样,yarn使用本地缓存。与npm不一样的是,yarn无需互联网链接就能安装本地缓存的依赖项,它提供了离线模式。这个功能在2012年的npm项目中就被提出来过,但一直没有实现。 容许合并项目中使用到的全部的包的许可证
和编译器相似,babel 的转译过程也分为三个阶段,这三步具体是:
1.解析 Parse 将代码解析生成抽象语法树( 即AST ),也就是计算机理解咱们代码的方式(扩展:通常来讲每一个 js 引擎都有本身的 AST,好比熟知的 v8,chrome 浏览器会把 js 源码转换为抽象语法树,再进一步转换为字节码或机器代码),而 babel 则是经过 babylon 实现的 。简单来讲就是一个对于 JS 代码的一个编译过程,进行了词法分析与语法分析的过程。
2.转换 Transform 对于 AST 进行变换一系列的操做,babel 接受获得 AST 并经过 babel-traverse 对其进行遍历,在此过程当中进行添加、更新及移除等操做。
3.生成 Generate 将变换后的 AST 再转换为 JS 代码, 使用到的模块是 babel-generator。 而 babel-core 模块则是将三者结合使得对外提供的API作了一个简化。
跨站脚本攻击是指经过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。 XSS 的原理是恶意攻击者往 Web 页面里插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中 Web 里面的脚本代码会被执行,从而能够达到攻击者盗取用户信息或其余侵犯用户安全隐私的目的。
非持久型 XSS 漏洞攻击有如下几点特征:
为了防止出现非持久型 XSS 漏洞,须要确保这么几件事情:
2.持久型 XSS(存储型 XSS) 持久型 XSS 漏洞,通常存在于 Form 表单提交等交互功能,如文章留言,提交文本信息等,黑客利用的 XSS 漏洞,将内容经正常功能提交进入数据库持久保存,当前端页面得到后端从数据库中读出的注入代码时,刚好将其渲染执行
攻击成功须要同时知足如下几个条件:
持久型 XSS 有如下几个特色:
如何防护 1) CSP CSP 本质上就是创建白名单,开发者明确告诉浏览器哪些外部资源能够加载和执行。咱们只须要配置规则,如何拦截是由浏览器本身实现的。咱们能够经过这种方式来尽可能减小 XSS 攻击。
一般能够经过两种方式来开启 CSP:
2) 转义字符 用户的输入永远不可信任的,最广泛的作法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义
3) HttpOnly Cookie 这是预防XSS攻击窃取用户cookie最有效的防护手段。Web应用程序在设置cookie时,将其属性设为HttpOnly,就能够避免该网页的cookie被客户端恶意JavaScript窃取,保护用户cookie信息。
1.CSRF攻击的原理 完成 CSRF 攻击必需要有三个条件:
2.如何防护 防范 CSRF 攻击能够遵循如下几种规则:
2) Referer Check HTTP Referer是header的一部分,当浏览器向web服务器发送请求时,通常会带上Referer信息告诉服务器是从哪一个页面连接过来的,服务器籍此能够得到一些信息用于处理。能够经过检查请求的来源来防护CSRF攻击。正常请求的referer具备必定规律,如在提交表单的referer一定是在该页面发起的请求。因此经过检查http包头referer的值是否是这个页面,来判断是否是CSRF攻击。
但在某些状况下如从https跳转到http,浏览器处于安全考虑,不会发送referer,服务器就没法进行check了。若与该网站同域的其余网站有XSS漏洞,那么攻击者能够在其余网站注入恶意脚本,受害者进入了此类同域的网址,也会遭受攻击。出于以上缘由,没法彻底依赖Referer Check做为防护CSRF的主要手段。可是能够经过Referer Check来监控CSRF攻击的发生。
3) Anti CSRF Token 目前比较完善的解决方案是加入Anti-CSRF-Token。即发送请求时在HTTP 请求中以参数的形式加入一个随机产生的token,并在服务器创建一个拦截器来验证这个token。服务器读取浏览器当前域cookie中这个token值,会进行校验该请求当中的token和cookie当中的token值是否都存在且相等,才认为这是合法的请求。不然认为此次请求是违法的,拒绝该次服务。
这种方法相比Referer检查要安全不少,token能够在用户登录后产生并放于session或cookie中,而后在每次请求时服务器把token从session或cookie中拿出,与本次请求中的token 进行比对。因为token的存在,攻击者没法再构造出一个完整的URL实施CSRF攻击。但在处理多个页面共存问题时,当某个页面消耗掉token后,其余页面的表单保存的仍是被消耗掉的那个token,其余页面的表单提交时会出现token错误。
点击劫持的原理 用户在登录 A 网站的系统后,被攻击者诱惑打开第三方网站,而第三方网站经过 iframe 引入了 A 网站的页面内容,用户在第三方网站中点击某个按钮(被装饰的按钮),其实是点击了 A 网站的按钮。
如何防护 1)X-FRAME-OPTIONS X-FRAME-OPTIONS是一个 HTTP 响应头,在现代浏览器有一个很好的支持。这个 HTTP 响应头 就是为了防护用 iframe 嵌套的点击劫持攻击。
该响应头有三个值可选,分别是
2)JavaScript 防护 对于某些远古浏览器来讲,并不能支持上面的这种方式,那咱们只有经过 JS 的方式来防护点击劫持了。
if(top.location != self.location){
top.location = self.location;
}
复制代码
[📚]JavaScript
高级程序设计(必看):book.douban.com/subject/105…
[📚]高性能JavaScript
:book.douban.com/subject/536…
现代JavaScript
教程:zh.javascript.info/
阮一峰的ECMAScript 6
教程:es6.ruanyifeng.com/
ECMAScript 6
标准:www.ecma-international.org/ecma-262/6.…
HTML meta
标签总结与属性使用介绍:segmentfault.com/a/119000000…
CSS
编码指导:github.com/chadluo/CSS…
大前端开发者须要了解的基础编译原理和语言知识:fullstack.blog/2017/06/24/…
图解HTTP
:book.douban.com/subject/258…
[📚]JavaScript
设计模式与开发实践:book.douban.com/subject/263…
正则表达式30分钟入门教程:link.juejin.im/?target=htt…
数据结构与算法之美:time.geekbang.org/column/intr…
用动画的形式呈现解LeetCode
题目的思路:github.com/MisterBooo/…
JavaScript
数据结构和算法:github.com/ConardLi/aw…
30-seconds-of-code
(里面有不少js
代码很是巧妙,我正在将它翻译成中文):github.com/ConardLi/30…
《重学前端》中的浏览器原理章节:time.geekbang.org/column/arti…
图解浏览器的基本工做原理:zhuanlan.zhihu.com/p/47407398
七天学会NodeJS
:github.com/nqdeng/7-da…
Node.js
模块加载与运行原理:efe.baidu.com/blog/nodejs…
TypeScript Handbook
:zhongsp.gitbooks.io/typescript-…
React.js
小书:huziketang.mangojuice.top/books/react…
React
深刻系列:juejin.im/post/5cad39…
Webpack React
小书:fakefish.github.io/react-webpa…
Vue.js
技术揭秘:github.com/ustbhuangyi…
Vuex
-在Vue
中管理状态:sabe.io/tutorials/g…
你须要Mobx
仍是Redux
?:juejin.im/post/5a7fd7…
Underscore
源码分析:yoyoyohamapi.gitbooks.io/undersercor…
微信小程序开发资源汇总:github.com/justjavac/a…
腾讯移动Web
前端知识库:github.com/AlloyTeam/M…
一口(很长的)气了解babel
:zhuanlan.zhihu.com/p/43249121
Webpack
傻瓜式指南:zhuanlan.zhihu.com/p/20367175
Webpack
原理:segmentfault.com/a/119000001…
廖雪峰的git
教程:www.liaoxuefeng.com/wiki/001373…
前端开发者必备的Nginx
知识:juejin.im/post/5c85a6…
使用Jenkins进行持续集成:www.liaoxuefeng.com/article/001…
常见六大Web
安全攻防解析:github.com/ljianshu/Bl…
深刻理解前端性能监控:juejin.im/post/5caaac…
[📚]高性能网站建设指南:book.douban.com/subject/313…
新人如何快速融入技术实力强的前端团队:juejin.im/post/5cb860…
印记中文(各类中文开发文档):www.docschina.org/
前端学习方法:github.com/helloqingfe…
如何在工做内外得到持续的技术成长:juejin.im/post/5cbd74…
优秀的前端博客汇总:github.com/foru17/fron…
另外推荐我一直在关注的几位大佬的我的博客:
冴羽的博客:github.com/mqyqingfeng…
左耳朵耗子:coolshell.cn/
互联网术语大全:www.jianshu.com/p/9a7ca206c…
互联网沟通、问答、学习的艺术:zhuanlan.zhihu.com/p/41431775
常常加班至深夜,怎样才能保持身体健康:www.zhihu.com/question/21…