根據大神們整理的前端自檢清單,本身整理一下答案,也方便本身學習。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請求將緩存過程分爲兩個部分,分別是強制緩存和協商緩存 。
強制緩存就是向瀏覽器緩存查找該請求結果,並根據該結果的緩存規則來決定是否使用該緩存結果的過程,強制緩存的狀況主要有三種(暫不分析協商緩存過程),以下:
不存在該緩存結果和緩存標識,強制緩存失效,則直接向服務器發起請求(跟第一次發起請求一致),以下圖:
存在該緩存結果和緩存標識,但該結果已失效,強制緩存失效,則使用協商緩存(暫不分析),以下圖
存在該緩存結果和緩存標識,且該結果還沒有失效,強制緩存生效,直接返回該結果,以下圖
當瀏覽器向服務器發起請求時,服務器會將緩存規則放入HTTP響應報文的HTTP頭中和請求結果一塊兒返回給瀏覽器,控制強制緩存的字段分別是Expires和Cache-Control,其中Cache-Control優先級比Expires高。 Expires Expires是HTTP/1.0控制網頁緩存的字段,其值爲服務器返回該請求結果緩存的到期時間,即再次發起該請求時,若是客戶端的時間小於Expires的值時,直接使用緩存結果。 Cache-Control 在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁緩存,主要取值爲: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…