JavaScript知識點整理

寫在前面

因爲本人仍是一個前端菜鳥,因此是很是有可能出錯誤的,熱烈歡迎指正!javascript

JavaScript

JavaScript是一門什麼樣的語言?有哪些特色?

運行在瀏覽器的V8引擎中的腳本語言,不要編譯就能夠由解釋器直接運行的,此外變量鬆散定義,屬於弱類型語言。html

說幾條JavaScript的基本規範?

  • 不要在同一行聲明多個變量
  • 用===替代==
  • switch語句要有default分支
  • 構造函數首字母大寫,常量用大寫字母
  • 使用對象字面量替代new Function(){}

頁面渲染原理

  • 瀏覽器解析HTML生成一個DOMTree
  • 接着解析CSS會產生CSSRuleTree
  • 解析完成後,瀏覽器引擎會經過DOMTree和CSSRuleTree來構造RenderingTree。
  • 瀏覽器調用

預編譯

  • 首先掃描var關鍵字,提高到頂端;
  • 而後掃描function定義,提到var以前
  • 而後再順序執行

defer和async

  • <script>元素中設置defer屬性,至關於告訴瀏覽器馬上下載,延遲執行。會等頁面解析完後,按指定順序執行
  • async屬性會告訴瀏覽器馬上下載,一旦下載完就會開始執行,且不能保證按照指定它們的前後順序執行。

數據類型

原始類型有哪幾種?null是對象嗎?

原始類型有:boolean,string,number,null,undefined,symbol 6種。前端

null不是對象,typeof null會輸出object,是由於之前32爲系統會將000開頭的表明爲對象,但null表示爲全零因此被錯誤判斷成objectjava

原始類型和引用類型的區別

1.原始類型存儲的是值,引用類型存儲的是指針。 2.原始數據類型直接存儲在棧中,引用數據類型存儲在堆中。node

使用typeof能夠獲得哪些類型?

undefined,string,number,boolean,object,function面試

typeof只能區分除null的原始類型,引用類型只能區分function。算法

什麼是提高?什麼是暫時性死區?var,let及const區別

  • 函數提高優先於變量提高,函數提高會將整個函數挪到做用域頂部,變量提高只將聲明提到做用域頂部
  • var存在提高,能夠在聲明前使用,let,const由於暫時性死區,不能在聲明前使用
  • var在全局做用域下聲明變量會致使變量掛載到window上,其餘二者不會
  • let和const做用基本一致,但const聲明的變量不能再次賦值
  • let和const不容許重複聲明

原始類型

爲何0.1+0.2!=0.3?如何解決這個問題?

在計算機中,數字以二進制形式存儲。在JavaScript中數字採用IEEE754的雙精度標準進行存儲,由於存儲空間有限,當出現沒法整除的小數時會取一個近似值。編程

在0.1+0.2中0.1和0.2都是近似表示的,因此相加時兩個近似值進行計算致使最後結果爲0.3000,0000,0000,0004,此時對於JS來講不夠近似於0.3,因此出現了0.1+0.2!=0.3json

解決方法:parseFloat((0.1+0.2).toFixed(10)) === 0.3 // true後端

null和undefined的區別

undefine: 表示變量被聲明瞭但沒有賦值。

null:變量被定義賦值了,可是爲空,沒有任何屬性方法和值

Symbol的使用場景

做爲屬性名的使用

var mySymbol=Symbol();
var a={};
a[mySymbol]='hello'
複製代碼

操做符

什麼時候使用===,什麼時候使用==

==會進行類型轉換後再比較,===不會,儘可能都用===.

如下兩種狀況能夠用==

  • 判斷對象屬性是否存在
var obj={}
if(obj.a == null){
    // 至關於obj.a===null || obj.a===undefined
}
複製代碼
  • 判斷函數參數是否存在
function fn(a, b){
    if(b == null){
        // 至關於b===null || b===undefined
    }
}
複製代碼

Object.is()與===,==的區別?

Object.is()能夠說是升級版,Object.is(NaN,NaN)會返回true,Object.is(-0,+0)返回false

引用類型

Array類型

如何判斷一個變量是數組?

1.判斷是否具備數組某些方法

if(arr.splice){}

2.instanceof(某些IE版本不正確)

arr instanceof Array

3.Array.isArray()

4.Object.prototype.toString.call(arr); // '[object Array]'

5.constructor方法

arr.constructor === Array

將類數組轉化爲數組

let arrayLike = {
    '0' : 'a',
    '1' : 'b',
    '2' : 'c',
    length : 3
};
let arr1 = Array.prototype.slice.call(arrayLike);
let arr2 = [].slice.call(arrayLike);
let arr3 = Array.from(arrayLike);
複製代碼

數組的方法

// 會改變原數組
pop()       // 末尾刪除
push()      // 末尾新增
shift()     // 開頭刪除
unshift()   // 開頭新增
reverse()   // 數組反轉
sort()      // 排序
splice()    // 修改數組(刪除插入替換)
// 不會改變原數組
concat()    // 合併數組
slice()     // 選擇數組的一部分
indexOf()   // 順序查找指定元素下標
lastIndexOf()   // 倒序查找指定元素下標
複製代碼
// 迭代方法
// every()查詢數組是否每一項都知足條件
// some()查詢數組中是否有知足條件的項
// filter()過濾,返回true的項組成的數組
// map()對每一項運行給定函數,返回每次函數調用結果組成的數組
// forEach()對每一項運行給定函數,無返回值
var numbers = [1,2,3,4,5,4,3,2,1];
numbers.every(function(item,index,array){
    return item>2;
})  // false
numbers.some(function(item,index,array){
    return item>2;
})  // true
numbers.filter(function(item,index,array){
    return item>2;
})  // [3,4,5,4,3]
numbers.map(function(item,index,array){
    return item*2;
})  // [2,4,6,8,10,8,6,4,2]
numbers.forEach(function(item,index,array){
    // 執行某些操做
})  // 無返回值
複製代碼
// 歸併方法
// reduce()從第一項開始逐個遍歷到最後
// reduceRight()從最後一項開始向前遍歷到第一項
var values = [1,2,3,4,5];
values.reduce(function(prev,cur,index,array){
    return prev+cur;
}) // 15
// reduceRight()結果同樣,順序相反
複製代碼

原生sort使用的是哪些排序算法?

插入排序和快速排序結合的排序算法

['1','2','3'].map(parseInt)的答案是?

[1,NaN,NaN]

由於parentInt須要兩個參數(val,radix),radix表示解析進制,而map傳入三個(element,index,array)致使對應的radix不合法致使解析錯誤。

Date()類型

獲取2019-02-16格式的日期

function formatDate(dt){
    if(!dt){
        dt=new Date()
    }
    var year = dt.getFullYear();
    var month = dt.getMonth()+1;
    var day = dt.getDate();
    if(month<10){
        month= '0'+month;
    }
    if(day<0){
        day = '0'+day;
    }
    var formatDt = year+'-'+month+'-'+day
    return formatDt;
}
複製代碼

其餘一些方法

getHour()       // 返回時
getMinutes()    // 返回分
getSeconds()    // 返回秒
getDay()        // 返回星期天數
getTime()       // 返回毫秒數
複製代碼

Function類型

函數聲明和函數表達式

解析器會先讀取函數聲明,提高到最前。而函數表達式會等到執行到它所在的代碼才真正被解釋執行

// 函數聲明
function sum(a,b){
    return a+b;
}
// 函數表達式
var sum = function(a,b){
    return a+b;
}
複製代碼

談談對this對象的理解

this引用函數執行的環境對象,老是指向函數的直接調用者,在執行時才能肯定值

this的指向

1.默認綁定,在瀏覽器中爲window,在node中是global

2.隱式綁定 例:window.a()

3.顯式綁定

  • 硬綁定 call,apply,bind
  • api調用的上下文 filter,forEach等有一個可選參數,在執行callback時用於this值

4.new綁定

5.箭頭函數

這裏寫的不是很詳細,推薦看JavaScript深刻之史上最全--5種this綁定全面解析

bind,call和apply各自有什麼區別?

相同點

  • 都是用來改變函數的this指向
  • 第一個參數是this要執行的值
  • 均可以利用後續參數傳參

不一樣點

  • call和apply是對函數直接調用,bind返回一個函數,須要()進行調用
  • call傳參是一個個傳,bind與call同樣,apply第二個參數是數組

callee和caller的做用?

callee是arguments對象的一個屬性,指向arguments對象的函數即當前函數。遞歸可使用arguments.callee()。 箭頭函數中this做用域與函數外一致,且沒有arguments對象,因此箭頭函數沒有callee

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num*arguments.callee(num-1)
    }
}
複製代碼

caller是函數對象的一個屬性,指向調用當前函數的函數,好比A調用B,則B.caller指向A()。全局做用域下調用當前函數,caller的值爲null

基本包裝類型

Number

toFixed()按指定小數位返回數值的字符串表示

var num=10; num.toFixed(2); // '10.00'

String

// charAt()根據字符位置返回所在位置的字符串
// charCodeAt()根據字符位置返回所在位置字符串的字符編碼
var str = 'hello world';
str.charAt(1);      // 'e'
str.charCode(1);    // '101'

// fromCharCode() 接收字符編碼轉爲字符串
String.fromCharCode(104,101,108,108,111) //'hello'

// concat()將字符拼接起來獲得新字符串
var str="hello"
str.concat(' world'); // "hello world"

// indexOf()和lastIndexOf() 返回字符位置
// trim() 刪除空格
// toLowerCase() 轉小寫,toUpperCase() 轉大寫
// localeCompare()  根據字母表比較排序

複製代碼

slice,substr和substring的區別

slice和substring接收的是起始位置和結束位置,substr接收的是起始位置和所要返回的字符串長度

對於負數,slice會將負數與字符串長度相加,substr會將負的第一個參數加上字符串長度,第二個參數轉爲0,substring會將全部負數轉爲0

split()和join()的區別

join()將數組中的元素放入一個字符串中,split將字符串分割成數組

var arr=[1,2,3];
var str=arr.join('|'); // '1|2|3'
str.split('|'); // [1,2,3]
複製代碼

單體內置對象

Global對象

URI編碼方法

encodeURI和encodeURICcomponent encodeURI和decodeURICcomponent

eval的做用?

功能是將對於字符串解析出JS代碼執行。要避免使用eval,不安全且很是消耗性能

Math對象

  • min()和max()
var max=Math.max(3,54,32,16); // 54
複製代碼
  • 舍入方法 Math.ceil() // 向上舍入 Math.floor() // 向下舍入 Math.round() // 四捨五入
  • random()

原型和原型鏈

原型

  • 全部引用類型(數組、對象、函數)都有一個__proto__隱式原型屬性,屬性值是一個普通對象。 此外,Object.prototype.__proto__指向null
  • 全部函數都有一個prototype顯式原型屬性,屬性值是一個普通對象。 Function.prototype.bind()沒有prototype屬性
  • 全部引用類型(數組、對象、函數)的__proto__執行它的構造函數的prototype屬性

構造函數

構造函數特色:函數名首字母大寫,它就相似一個模板,能夠new出多個實例

var a={} // var a=new Object()的語法糖
var a=[] // var a=new Array()的語法糖
function Foo(){} // var Foo=new Function()的語法糖
複製代碼

instanceof

instanceof判斷引用類型屬於哪一個構造函數

f instanceof Foo的判斷邏輯:f的__proto__一層層往上,可否找到Foo.prototype。

可是由於原型鏈上全部特殊對象的__proto__最終都會指向Object.prototype,因此instanceof判斷類型也不徹底準確

new操做符具體幹了什麼?

  • 建立一個空對象
  • 將對象的__proto指向構造函數的原型prototype
  • 執行構造函數中的代碼,傳遞參數,並將this指向這個對象
  • 返回對象
function _new(){
    let obj = {};
    let con=[].shift.call(arguments);
    obj.__proto__ = con.prototype;
    let res = con.apply(obj, arguments);
    return res instanceof Object ? res : obj;
}
複製代碼

經過new的方式建立對象和經過字面量建立的區別

更推薦字面量的方式建立對象,性能和可讀性都更好。使用var o=new Object()和var o={}的區別是前者會調用構造函數

hasOwnProperty

hasOwnProperty判斷該對象自己是否有指定屬性,不會到原型鏈上查找。

使用方法:object.hasOwnProperty(proName)

利用它能夠循環對象自身屬性

for(let item in f){
    if(f.hasOwnProperty(item)){
        console.log(item)
    }
}
複製代碼

原型鏈

訪問一個對象的屬性時,先在基本屬性中查找,若是沒有,再沿着__proto__這條鏈向上找,這就是原型鏈

面向對象

建立對象的幾種方式

  • 工廠模式
function createPerson(name, age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        console.log(this.name)
    }
    return o;
}
var person1 = createPerson('chen',21)
複製代碼
  • 構造函數模式

沒有顯示建立對象,直接將屬性方法賦給this,沒有return語句

function Person(name, age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        console.log(this.name)
    }
}
var person1 = new Person('chen',21)
複製代碼

缺點:每一個方法都要在每一個實例上從新定義一遍,沒法獲得複用

  • 原型模式
function Person(){}
Person.prototype.name="chen"
Person.prototype.age=21
Person.prototype.sayName=function(){
    console.log(this.name)
}
var person1 = new Person()
複製代碼

缺點:全部實例都取得相同屬性值

  • 混合構造函數原型模式 構造函數模式用於定義實例屬性,原型模式用於定義方法和共享的屬性。
function Person(name,age){
    this.name=name;
    this.age=age;
}
Person.prototype = {
    constructor: Person,
    sayName: function(){
        console.log(this.name)
    }
}
var person1=new Person('chen',21)
複製代碼

實現繼承

JavaScript經過原型鏈實現繼承

  • 原型鏈繼承 核心:將父類的實例做爲子類的繼承
function Parent(){
    this.name = 'parent'
}
Parent.prototype.sayName = function(){
    return this.name
}
function Child(){
}
// 繼承了Parent
Child.prototype = new Parent();
var child1=new Child();
child1.say();
複製代碼

缺點:對象實例共享全部繼承的屬性和方法

  • 借用構造函數 核心:在子構造函數內部調用父構造函數
function Parent(){
    this.arr=[1,2,3]
}
function Child(){
    // 繼承了Parent
    Parent.call(this)
}
var child1 = new Child();
child.arr.push(4); // [1,2,3,4]
var child2 = new Child();
child.arr;  // [1,2,3]
複製代碼
  • 組合繼承

使用原型鏈繼承共享的屬性和方法,經過借用構造函數繼承實例屬性

function Parent(name){
    this.name = name;
    this.arr = [1,2,3]
}
Parent.prototype.sayName = function(){
    console.log(this.name)
}
function Child(name,age){
    // 繼承屬性
    Parent.call(this, name)
    this.age=age
}
// 繼承方法
Child.prototype = new Parent()
Child.prototype.constructor = Child;
Child.prototype.sayAge = function(){
    console.log(this.age)
}
var child1=new Child('chen',21);
child1.arr.push(4); //[1,2,3,4]
child1.sayName()    // 'chen'
child1.sayAge()     // 21

var child2=new Child('miao', 12)
child2.arr          // [1,2,3]
child2.sayName()    // "miao"
child2.sayAge()     // 12
複製代碼

缺點:不管在什麼狀況都會調用兩次父構造函數,一次是建立子類型原型,另外一次是在子構造函數內部

  • 原型式繼承 核心:執行對給定對象的淺複製
var person = {
    name: 'chen',
    arr: [1,2,3]
}
var person1 = Object.create(person);
person1.name = 'run'
person1.arr.push(4)
var person2 = Object.create(person);
person2.name = 'miao'
person2.arr.push(5)
person.arr; // [1,2,3,4,5]
複製代碼
  • 寄生式繼承 核心:基於某個對象建立一個對象,而後加強對象,返回對象。
function create(original){
    // 經過調用函數建立一個新對象
    var clone = object(original); 
    // 以某種方式加強對象
    clone.sayHi = function(){
        console.log('hi')
    }
    return clone;
}
var person = {
    name: 'chen'
}
var person1 = create(person);
person1.sayHi();
複製代碼
  • 寄生組合式繼承
function Parent(name){
    this.name = name;
    this.arr = [1,2,3]
}
// 將共享的屬性/方法放到原型上
Parent.prototype.sayName = function(){
    console.log(this.name)
}
// 借用構造函數加強子類實例屬性(支持傳參和避免篡改)
function Child(name,age){
    // 繼承屬性
    Parent.call(this, name)
    this.age=age
}
function inheritPrototype(Child, Parent){
    var prototype=Object.create(Parent.prototype);
    prototype.constructor=Child;
    Child.prototype=prototype;
}
// 將父類原型指向子類
inheritPrototype(Child, Parent);
Child.prototype.sayAge=function(){
    console.log(this.age)
}

var child1=new Child('chen',21);
child1.arr.push(4); //[1,2,3,4] 繼承自父類實例屬性
child1.sayName()    // 'chen'   繼承自父類原型方法
child1.sayAge()     // 21       繼承自子類原型方法

var child2=new Child('miao', 12)
child2.arr          // [1,2,3]
child2.sayName()    // "miao" 
child2.sayAge()     // 12
複製代碼

class如何實現繼承,class本質是什麼?

class只是語法糖,本質是函數

class Parent{
    constructor(value){
        this.val=value
    }
    getValue(){
        console.log(this.val)
    }
}
class Child extends Parent{
    constructor(value){
        super(value)
        this.val = value
    }
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
複製代碼

核心:使用extends代表繼承自哪一個父類,而且在子類構造函數中必須使用super,能夠看作是Parent.call(this,value)

什麼是面向對象編程及面向過程編程,它們的異同和優缺點

面向過程就是分析出解決問題所需的步驟,而後用函數把這些步驟一步步實現,使用的時候一個個依次調用

面向對象是把構成問題的事務分解成各個對象,獎勵對象的目的不是爲了完成一個步驟,而是爲了描述某個事物在整個解決問題的步驟中的行爲。

優勢:易維護,可讀性高,易擴展,繼承性高,下降重複工做量,縮短了開發走起

閉包

閉包指有權訪問另外一個函數內部變量的函數,當在函數內部定義了其餘函數,也就建立了閉包

談談你對閉包的理解

使用閉包能夠模仿塊級做用域

優勢:能夠避免全局變量的污染,實現封裝和緩存;

缺點:閉包會常駐內存,增大內存使用量,使用不當很容易形成內存泄漏。解決方法:在退出函數以前,將不適用的局部變量設爲null。

閉包最大的兩個用處:1.能夠讀取函數內部的變量;2.使這些變量始終保存在內存中;3.封裝對象的私有屬性和私有方法

說說你對做用域鏈的理解

做用域鏈的做用是保證執行環境裏有權訪問的變量和函數是有序的,做用域鏈的變量只能享受訪問,訪問到window對象即被終止。

簡單來講,做用域就是變量和函數的可訪問範圍

如何建立塊級做用域

  • ES6 使用let和const
  • 閉包

BOM

BOM對象有哪些?

  • window JS最頂層對象
  • location 瀏覽器當前URL信息
  • navigator 瀏覽器自己信息
  • screen 客戶端屏幕信息
  • history 瀏覽器訪問歷史信息

window對象的方法

alert(),prompt(),confirm(),open(),close(),print(),focus(),blur(),moveBy(),moveTo(),resizeBy(),resizeTo(),scrollBy(),scrollTo(),setInterval(),setTimeout(),clearInterval(),clearTimeout()

history對象

history.go(-1);     // 後退一頁
history.go(1);      // 前進一頁
history.back();     // 後退一頁
history.forward();  // 前進一頁
複製代碼

窗口位置

  • 跨瀏覽器取得窗口左邊和上邊的位置
var leftPos=(typeof window.screenLeft == 'number') ? window.screenLeft : window.screenX;
var topPos=(typeof window.screenTop == 'number') ? window.screenTop : window.screenY;
複製代碼
  • 將窗口移動到指定位置

moveTo():接收新位置的x,y座標值

moveBy():接收在水平垂直方向上移動的像素數

窗口大小

outerWidth和outerHeight返回瀏覽器窗口自己的尺寸,innerWidth和innerHeight返回容器視圖區的大小

  • 調整窗口大小 resizeTo()和resizeBy()

如何檢測瀏覽器的類型

使用navigator.userAgent

var ua = navigator.userAgent;
var isChrome = ua.indexOf('Chrome')
複製代碼

拆解url的各部分

使用location的屬性

  • href 完整url地址
  • protocol 協議
  • host 主機名+端口號
  • hostname 主機名
  • port 端口號
  • pathname 相對路徑
  • hash #錨點
  • search ?查詢字符串
history.go(1)
複製代碼

DOM

爲何操做DOM慢?

由於DOM屬於渲染引擎的東西,JS又是JS引擎的東西,當咱們經過JS操做DOM的時候,涉及到兩個線程間的通訊,並且操做DOM可能會帶來重繪迴流的狀況,因此就致使了性能問題。

插入幾萬個DOM,如何實現頁面不卡頓?

  • 1.分批次部分渲染DOM,經過requestAnimationFrame去循環插入DOM
  • 2.虛擬滾動,只渲染可視區域內的內容

什麼狀況阻塞渲染

  • 1.HTML和CSS都會阻塞渲染,若是想渲染快就應該下降一開始渲染的文件大小,扁平層級,優化選擇器
  • 2.瀏覽器解析到script標籤時會暫停構建DOM,

解決方法

  • ①將script標籤放到body底部
  • ②給script標籤加上defer屬性,該JS文件會並行下載但等到HTML解析完後順序執行
  • ③script標籤加上async屬性,表示JS文件下載和解析不會阻塞渲染

重繪Repaint和迴流Reflow

重繪是當節點改變樣式而不影響佈局,迴流是當佈局或幾何屬性須要改變

迴流一定會發生重繪,迴流的成本比重繪高

性能問題:1.改變window大小 2.改變字體 3.添加或刪除樣式 4.文字改變 5.定位或浮動 6.盒模型

減小重繪和迴流

  • 1.使用transform替代top
  • 2.使用visibility換成display:none,前者只引發重繪,後者引起迴流
  • 3.不要把節點的屬性值放在一個循環裏當成循環裏的變量
  • 4.不要使用table佈局
  • 5.動畫實現的動畫速度越快,迴流次數越多,也可以使用requestAnimationFrame
  • 6.CSS選擇符從右往左匹配,避免節點層級過多
  • 7.將頻繁重繪或迴流的節點設置爲圖層 ①will-change屬性 ②video, iframe標籤

建立節點

  • createElement 建立一個元素
  • createTextNode 建立一個文本節點
  • createDocumentFragment 建立一個文檔片斷
  • cloneNode 返回調用該方法的節點的一個副本

注意事項:

  • 以上只是建立一個孤立的節點,須要經過appendChild添加到文檔中
  • cloneNode要注意被複制的節點是否包含子節點以及事件綁定等問題
  • 使用createDocumentFragment來解決添加大量節點時的性能問題

頁面修改API

  • appendChild() 添加子節點
  • insertBefore() 添加節點到參考節點以前 parentNode.insertBefore(newNode, refNode) parentNode表示父節點,newNode表示要添加的節點,refNode表示參照節點
  • removeChild() 刪除子節點
  • replaceChild() 替換 parent.replaceChild(newChild,oldChild)

節點查詢API

  • document.getElementById
  • document.getElementsByTagName
  • document.getElementsByName
  • document.getElementsByClassName
  • document.querySelector // 返回第一個匹配的元素
  • document.querySelectorAll

節點關係型API

  • parentNode // 父節點
  • parentElement // 父節點,必須是element
  • childNodes // 子元素列表,包含文本,註釋節點
  • children // 子元素列表
  • firstChild // 第一個子節點
  • lastChild // 最後一個子節點
  • hasChildNodes // 判斷當前節點是否有子節點
  • previousSibling // 前一個兄弟節點
  • previousElementSibling // 前一個兄弟元素節點
  • nextSibling // 後一個兄弟節點
  • nextElementSibling // 後一個兄弟元素節點

元素屬性API

  • setAttribute 設置屬性 element.setAttribute(name, value)
  • getAttribute 返回屬性值
  • removeAttribute 刪除屬性

元素樣式API

  • window.getComputedStyle 返回元素計算後的樣式
  • getBoundingClientRect 返回元素大小以及相對於瀏覽器可視窗口的位置
  • 直接修改元素的樣式
ele.style.color = 'red'
ele.style.setProperty('font-size', '16px')
ele.style.removeProperty('color')
複製代碼
  • 動態添加樣式規則
var style = document.createElement('style');
style.innerHTML='body{color:red;} #top{color:white;}';
document.head.appendChild(style);
複製代碼

attribute和property的區別是什麼?

attribute是dom元素在文檔中做爲HTML標籤擁有的屬性

prototype是dom元素在JS中做爲對象擁有的屬性

JS如何設置獲取盒模型對於的寬和高?

  • 獲取內聯元素的寬高 dom.style.width/height
  • 只適用在IE中獲取元素寬高 dom.currentStyle.width/height
  • 獲取元素寬高,兼容性較好 window.getCompontedStyle(dom).width/height
  • 根據元素在視窗中的絕對位置獲取寬高 dom.getBoundingClientRect().width/height
  • 最經常使用,兼容性最好 dom.offsetWidth/offsetHeight

offsetWidth/offsetHeight,clientWidth/clientHeight與srcollWidth/scrollHeight的區別

  • offsetWidth/offsetHeight返回包含content+padding+border,效果與e.getBoundingClientRect()相同
  • clientWidth/clientHeight返回包含content+padding,若是有滾動條,也不包含滾動條
  • scrollWidth/scrollHeight返回包含content+paddin+溢出內容的尺寸

document.write和innerHTML的區別

  • document.write只能重繪整個頁面
  • innerHTML能夠重繪頁面的一部分

DOM事件

DOM事件的級別

  • DOM0 element.onclick=function(){}
  • DOM2 element.addEventListener('click',function(){},false)
  • DOM3 element.addEventListener('keyup',function(){},false)

DOM0級事件就是將一個函數賦值給一個事件處理屬性,缺點在於一個處理程序沒法同時綁定多個處理函數。

DOM2級事件運行給一個程序添加多個處理函數,定義了addEventListener和removeEventListener兩個方法,分別用於綁定和解綁事件,方法包含三個參數分別是綁定的事件處理的屬性名稱,處理函數,是否在捕獲時執行事件

IE8如下使用attachEvent和detachEvent實現,不須要傳入第三個參數,由於IE8如下只支持冒泡型事件

btn.attachEvent('onclick', showFn);
btn.detachEvent('onclick', showFn);
複製代碼

DOM3級事件是在DOM2級事件的基礎上添加不少事件類型如load,scroll,blur,focus,dbclick,mouseup,mousewheel,textInput,keydown,keypress,同時也容許使用者自定義一些事件。

如何使用事件?

  • HTML事件處理程序
<div onclick="clicked()"></div>
複製代碼

優缺點:簡單,但與HTML代碼緊密耦合,更改不方便

  • DOM0級處理程序
document.onclick = function(){}; // 指定事件
docuemtn.onclick = null;         // 刪除事件
複製代碼

優缺點:簡單且跨瀏覽器

  • DOM2級處理程序
addEventListener('click', function(){},布爾值) // 指定事件
removeListener('click', function(){}, 布爾值) // 移除事件
複製代碼

優缺點:能夠添加多個監聽函數,若是指定處理函數是匿名函數,則沒法刪除

  • IE事件處理程序
attachEvent('onclick', function(){}) // 指定事件
detachEvent('click', function(){}) // 移除事件
複製代碼

優缺點:能夠添加多個監聽函數,若是指定處理函數是匿名函數,則沒法刪除

IE與標準DOM事件模型之間存在的差異

  • 參數的差異:

attachEvent()第一個參數比addEventListener()的事件名多一個「on」;且沒有第三個參數,由於IE事件模型只支持冒泡事件流

  • 事件處理函數做用域:

IE中事件處理程序處於全局做用域,其內的this會指向window,而DOM事件模型是做用於元素,其內的this執行所屬元素

  • 事件對象event的屬性方法的差異

阻止冒泡

IE:cancelBubble=true

DOM: stopPropagation()

阻止元素默認事件

IE:returnValue=false

DOM:preventDefault()

事件目標

IE: srcElement DOM: target

IE與標準有哪些兼容性寫法?

var ev = ev || window.event;
document.docuemntElement.clientWidth || document.body.clientWidth
var target = ev.srcElement || ev.target
複製代碼

DOM事件模型(冒泡和捕獲)

  • 事件冒泡

addEventListener第三個參數爲false,事件在冒泡時候執行,事件目標target一級一級往上冒泡

如何阻止事件冒泡

child.addEventListener('click', function(e){
    console.log('目標事件')
    e.stopPropagation();
}, false)
複製代碼
  • 事件捕獲

事件捕獲是自上而下執行的,將addEventListener第三個參數爲true

DOM事件流

DOM2級事件規定的事件流包括三個階段:事件捕獲階段,處於目標階段和事件冒泡階段。首先發送的是事件捕獲,爲截獲事件提供了機會,而後是實際的目標接收到事件,最後一個階段是冒泡階段,能夠在這個階段對事件作出響應

捕獲DOM事件捕獲的具體流程

window->docuemnt->html->body->...

一開始接收事件的window,window接收完之後給到document,第三個纔是html標籤,再就是body,而後在一級一級往下傳。與之至關的就是冒泡,從當前元素到window的過程

Event對象的常見應用

  • event.preventDefault(): 阻止默認事件
  • event.stopPropagation(): 阻止冒泡
  • event.stopImmediatePropagatio(): 阻止剩餘事件處理函數的執行,並防止當前事件在DOM上冒泡
  • event.currentTarget: 事件委託時,當前綁定的事件元素
  • event.target: 事件委託時,區分當前哪一個元素被點擊

自定義事件

var event = new Event('custome');
ev.addEventListener('custome', function(){
    console.log('custome');
});
ev.dispatchEvent(event);
複製代碼

mouseover和mouseenter的區別

mouseover:當鼠標移入元素或其子元素都會觸發事件,因此有一個重複觸發,冒泡的過程。對應的移除事件是mouseout

mouseenter: 當鼠標移入元素自己(不包含子元素)會觸發事件,不會冒泡,對應的移除事件是mouseleave

請解釋什麼是事件代理?

事件代理(事件委託)把本來須要綁定的事件委託給父元素,讓父元素擔當事件監聽的職務。原理是DOM元素的事件冒泡。

好處:能夠提升性能,大量節省內存佔用,減小事件註冊,能夠實現新增子對象時無需再次對其綁定

document.load和document.ready的區別

document.onload是在樣式結構加載完才執行,window.onload()不只要樣式結構加載完還要執行完全部樣式圖片資源文件所有加載完後纔會觸發

document.ready原生中無此方法,jQuery中有$().ready(),ready事件不要求頁面全加載完,只須要加載完DOM結構便可觸發。

JSON

如何理解JSON?

JSON是JS的一個內置對象,也是一種輕量級的數據交換格式。 經常使用兩個方法:

  • parse // 字符串轉對象
  • stringify // 對象轉字符串

XML和JSON的區別?

  • 數據體積方面:JSON相對於XML來說,數據的體積小,傳遞的速度更快些。
  • 數據交互方面:JSON與JavaScript的交互更加方便,更容易解析處理,更好的數據交互
  • 數據描述方面:JSON對數據的描述性比XML較差
  • 傳輸速度方面:JSON的速度要遠遠快於XML

Ajax

如何建立Ajax?

// 建立XMLHTTPRequest對象
var xhr = new XMLHttpRequest();
// 建立一個新的http請求
xhr.open("get", url, true)
// 設置響應HTTP請求狀態變化的函數
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
        if(xhr.status == 200){
            // 獲取異步調用返回的數據
            alert(xhr.responseText)
        }
    }
}
// 發送HTTP請求
xhr.send(null);
複製代碼

狀態碼readyState說明:0:未初始化,未調用send();1:已調用send(),正在發生請求;2:send()方法執行完畢,已經接收到所有響應內容;3:正在解析響應內容;4:解析完成,能夠在客戶端調用了

Ajax有哪些優缺點?

優勢:1.經過異步模式,提高用戶體驗;2.優化了瀏覽器和服務器之間的傳輸,減小沒必要要的數據往返,減小了帶寬佔用;3.Ajax在客戶端運行,承擔了一部分原本由服務器承擔的工做,減小了大用戶量下的服務器負載;4.Ajax能夠實現局部刷新

缺點:1.Ajax暴露了與服務器交互的的細節;2.對搜索引擎的支持較弱;3.不容易調試

GET和POST在瀏覽器實現的區別

  • GET後退刷新無害,可收藏爲書籤,能被緩存,參數會保留在瀏覽器歷史中,只容許ASCII字符,安全性較差,數據顯示在URL中
  • POST後退刷新會從新提交數據,不可被收藏爲標籤,不能被緩存,參數不會被保存在瀏覽器歷史中,對數據類型無限制,安全性較好,數據不會顯示在URL中

GET請求傳參的誤區

誤區:咱們常認爲GET請求參數的大小存在限制,而POST無限制

實際上HTTP協議沒有限制GET/POST的請求長度限制,GET的最大長度限制是由於瀏覽器和WEB服務器限制了URL的長度。

GET和POST的區別

GET和POST方法沒有實質區別,只是報文格式不一樣。

跨域

能夠跨域的三個標籤<img><link><script>

什麼是跨域?

跨域指經過JS在不一樣的域之間進行數據傳入或通訊。 協議,域名,端口有一個不一樣就是不一樣域

瀏覽器爲何要使用同源策略?

同源策略是爲了防止CSRF攻擊,它是利用用戶的登陸態發起惡意請求。若是沒有同源策略,網站能夠被任意來源的Ajax訪問到內容。

如何解決跨域問題?

  • JSONP 原理是利用<script>標籤沒有跨域限制的漏洞
function jsonp(url, callback, success){
    let script = docuemnt.createElement('scipt');
    script.src = url;
    script.async = true;
    script.type = "text/javascript";
    window[callback] = function(data){
        success && success(data)
    }
    document.body.appendChild(script)
}
jsonp('http://xxx.com', 'callback', function(value){
    console.log(value);  
})
複製代碼

JSONP使用簡單,兼容性不錯但僅限GET請求

  • CORS CORS須要先後端同時支持,服務器端設置Access-Control-Origin開啓CORS,該屬性代表哪些域名能夠訪問資源。分爲簡單請求和複雜請求,複雜請求多一個預檢請求
  • document.domain 只適用於二級域名相同,例如a.test.comb.test.com。在頁面添加document.domain = 'test.com'表示二級域名相同便可跨域
  • postMessage 一般用於獲取嵌入頁面的第三方頁面數據,一個頁面發送消息,另外一個頁面判斷來源並接收消息
// 發送消息
window.parent.postMessage('message', 'http://www.test.com')
// 接收消息
let mc=new MessageChannel()
mc.addEventListener('message', event => {
    let origin = event.origin || event.originalEvent.origin;
    if(origin === 'http://www.test.com'){
        console.log('驗證經過')
    }
})
複製代碼
  • window.name

存儲

請描述cookie,sessionStorage和localStorage的區別

答:

  • 容量:cookie只有4kb,localStorage和sessionStorage最大容量5M
  • 服務器通訊:cookie用於客戶端與服務器端的通訊
  • 有效時間:cookie在設置的有效時間過時前都存在,sessionStorage關閉當前頁面就清理,localStorage除非主動刪除不然一直存在
  • api易用性:cookie的api須要本身封裝才能使用,localStorage.setItem(key,value);localStorage.getItem(key)

有幾種方式能夠實現存儲功能?

cookie,sessionStorage,localStorage,indexDB。

indexDB不限存儲大小,不與服務器端通訊,除非主動刪除不然一直存在。

什麼是Service Worker?

Service Worker是運行在瀏覽器背後的獨立線程,通常能夠用來實現緩存功能,傳輸協議必須爲https

Server Worker的工做?

  • 與緩存進行交互,當用戶請求緩存中的東西時,Service Worker能馬上從緩存中獲取數據不經過外部http調用
  • 發送推送通知
  • 運行背景同步:在離線的狀況下用瀏覽器發送一個文件,Service Worker能夠保存此任務,等網絡鏈接恢復後將文件上傳至外部服務器

ES6

箭頭函數

箭頭函數與普通函數的區別:

  • 箭頭函數沒有arguments(用剩餘運算符替代)
  • 箭頭函數沒有prototype,不能做爲構造函數(不能用new關鍵字調用)
  • 箭頭函數沒有本身的this,引用的是外層執行上下文的this

擴展運算符

  • 可將類數組轉爲真正的數組
let nodeList = document.querySelectorAll('div')
let arr=[...nodeList]
複製代碼
  • 合併多個數組
let arr1=[1,2,3]
let arr2=[4,5,6]
let arr3=[...arr1, ...arr2]
複製代碼

for...of循環

for-of與for-in的區別

  • for-of遍歷獲取的是對象的鍵值,for-in獲取的是鍵名
  • for-in會遍歷對象的整個原型鏈,性能差,for...of只遍歷當前對象不會遍歷原型鏈
  • 對於數組的遍歷,for-in會遍歷全部可枚舉屬性(包括原型鏈),for...of只返回數組下標所對應的屬性值

for...of的原理

利用了遍歷對象內部的iterator接口,將for...of循環分解爲最原始的for循環

對promise的理解,分別有什麼優缺點?什麼是Promise鏈?Promise構造函數執行和then函數執行有什麼區別?

  • Promise有三種狀態:pending等待中,resolved完成,rejected拒絕 且一旦從等待狀態變爲其餘狀態就不能再次改變
  • 構造Promise時,構造函數內部的代碼是當即執行的。
  • Promise實現了鏈式調用即每次調用then以後返回的都是一個全新的Promise,若是在then中使用return,那麼return的值會被Promise.resolve()包裝

你理解的Generator是什麼?

Generator能夠控制函數的執行

function *foo(x){
let y=2*(yield(x+1))
let z=yield(y/3)
return (x+y+z)
}
let it=foo(5)
it.next()		// {value:6,done:false}
it.next(12)
it.next(13)
複製代碼

Generator函數調用會返回一個迭代器,第一次next,函數停在yield(x+1)因此value=5+1=6; 第二次next,傳入參數至關於上一個yield的值,即let y=2*12=24; let z=24/3=8;第三次next,傳入參數至關於上一個yield的值,即let z=13,y=24,x=5,相加返回42

async,await相比於promise的優點

  • 優點在於處理then的調用鏈,可以更清晰準確的寫出代碼
  • 缺點:await將異步代碼改形成同步,若是多個異步代碼之間沒有依賴性卻使用了await會致使性能下降

對async,await的理解,內部原理

async就是將函數返回值使用Promise.resolve()包裹一下,和then中處理返回值同樣,而且async只能搭配await使用 await其實就是generator加上Promise的語法糖,且內部實現了自動執行generator

setTimeout,setInterval,requestAnimationFrame各有什麼特色?

setTimeout延後執行,setInterval每隔一段時間執行一次回調函數,以上兩種都不能保證在預期時間執行任務 requestAnimationFrame自帶函數節流功能,且延時效果是精確的

本身實現一個Promise

const PENDGIN='pending'
const RESOLVED='resolved'
const REJECTED='rejected'
function myPromise(fn){
	const that=this
	that.state=PENDING
	that.value=null
	that.resolvedCallbacks=[]
	that.rejectedCallbacks=[]
	function resolve(value){
		if(value instanceof myPromise){
			return value.then(resolve, reject)
		}
		setTimeout(()=>{
			if(that.state===PENDING){
				that.state=RESOLVED;
				that.value=value;
				that.resolvedCallbacks.map(cb => cb(that.value))
			}
		},0)
	}
	function reject(error){
		setTimeout(()=>{
			if(that.state===PENDING){
				that.state=REJECTED
				that.value=value
				that.rejectedCallbacks.map(cb => cb(that.value))
			}
		},0)
	}
	try{
		fn(resolve, reject)
	}catch(e){
		reject(e)
	}
}
myPromise.prototype.then=function(onFulfilled, onRejected){
	const that=this
	onFulfilled=typeof onFulfilled==='function'?onFulfilled: v=>v
	onRejected=typeof onRejected==='function'?onRejected: r=>{throw r}
	if(that.state===PENDING){
		that.resolvedCallbacks.push(onFulfilled)
		that.rejectedCallbacks.push(onRejected)
	}
	if(that.state===RESOLVED){
		onFulfilled(that.value)
	}
	if(that.state===REJECTED){
		onRejected(that.value)
	}
}
new myPromise((resolve, reject) => {
	setTimeout(()=>{
		resolve(1)
	},0)
}).then(value=>{
	console.log(value)
})
複製代碼

線程和進程的區別

進程描述了CPU在運行指令及加載和保存上下文所需的事件,線程是更小的單位,秒速了執行一段指令所需的時間

JS單線程帶來的好處

由於js能夠修改DOM,若是JS執行的過程UI線程還在工做會致使不能不能安全的渲染UI。 JS單線程運行,能夠達到節省內存,節約上下文切換時間,沒有鎖的問題

什麼是執行棧

執行棧存放函數調用的棧結構,遵循先進後出的原則。 從底部開始放進去執行棧,而後從棧頂取出執行

微任務與宏任務

不一樣的任務源會被分配到不一樣task隊列中,任務源能夠分爲微任務(microtask)和宏任務(macrotask) 微任務包括process.nextTick, promise, MutationObserver 宏任務包括script,setTimeout,setInterval,setImmediate,I/O,UI rendering

EventLoop執行順序

1.首先執行同步代碼,這屬於宏任務 2.當執行完全部同步代碼後,執行棧爲空,查詢是否有異步代碼須要執行 3.執行全部微任務 4.當執行完全部微任務後,若有必要會渲染頁面 5.而後開始下一輪EventLoop,執行宏任務中的異步代碼,也就是setTimeout中的回調函數

Proxy

Vue3.0其中一個核心功能就是使用Proxy替代Object.defineProperty Proxy能夠在目標對象前架設一個攔截器,通常Reflect搭配,前者攔截對象,後者返回攔截的結果

Object.defineProperty的不足

1.沒法探測到對象根屬性的添加和刪除 2.沒法探測到對數組基於下標的修改 3.沒法探測到.length修改的監測

參考文獻:

JavaScript高級程序設計(第3版)

掘金小冊 yck-前端面試之道

近一萬字的ES6語法知識點補充

相關文章
相關標籤/搜索