1. 實用js寫紅綠燈的效果?
<ul id="traffic" class="">
<li id="green"></li>
<li id="yellow"></li>
<li id="red"></li>
</ul>
ul {
position: absolute;
width: 200px;
height: 200px;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
/*畫3個圓代表紅綠燈*/
ul >li {
width: 40px;
height: 40px;
border-radius:50%;
opacity: 0.2;
display: inline-block;
}
/*執行時改變透明度*/
ul.red >#red,
ul.green >#green,
ul.yellow >#yellow{
opacity: 1.0;
}
/*紅綠燈的三個顏色*/
#red {background: red;}
#yellow {background: yellow;}
#green {background: green;}
function timeout(timer){
return function(){
return new Promise(function(resolve,reject){
setTimeout(resolve,timer)
})
}
}
var green = timeout(3000);
var yellow = timeout(4000);
var red = timeout(5000);
var traffic = document.getElementById("traffic");
(function restart(){
'use strict' //嚴格模式
console.log("綠燈"+new Date().getSeconds()) //綠燈執行三秒
traffic.className = 'green';
green()
.then(function(){
console.log("黃燈"+new Date().getSeconds()) //黃燈執行四秒
traffic.className = 'yellow';
return yellow();
})
.then(function(){
console.log("紅燈"+new Date().getSeconds()) //紅燈執行五秒
traffic.className = 'red';
return red();
}).then(function(){
restart()
})
})();
2. axios是否需要promise封裝?
需要
import axios from 'axios'
const http = ({
url,method,params,headers
}) => {
return new Promise ( (resolve,reject) => {
axios({
url,
method,
params,
headers
})
.then( res => {
resolve(res)
})
.catch( error => {
throw error
})
})
}
export default http
3. Promise內部發生錯誤,如果同時.then方法有第二個引數,也有.catch會呼叫哪個
.catch
4. 宏任務 微任務
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4')
settimeout肯定是異步的, 我也知道有一個event佇列,你settimeout沒設定時間應該直接就進入這個佇列了吧,然后就是Promise的回掉函式進入event佇列, 當時我二話不說給了個答案 2,4,1,3.并且很自信,然后面試官就問你不想想了?我說不想了,然后后半段他全程開始皺眉頭了,我也涼涼,最后他讓我回去看一下宏任務和微任務,
首先說一下普通的異步函式的執行程序吧
同步和異步任務分別進入不同的執行"場所",同步的進入主執行緒,異步的進入Event Table并注冊函式,當指定的事情完成時,Event Table會將這個函式移入Event Queue,主執行緒內的任務執行完畢為空,會去Event Queue讀取對應的函式,進入主執行緒執行,上述程序會不斷重復,也就是常說的Event Loop(事件回圈),
那么如此看來我給的答案還是對的,但是js異步有一個機制,就是遇到宏任務,先執行宏任務,將宏任務放入eventqueue,然后在執行微任務,將微任務放入eventqueue最騷的是,這兩個queue不是一個queue,當你往外拿的時候先從微任務里拿這個回掉函式,然后再從宏任務的queue上拿宏任務的回掉函式, 我當時看到這我就服了還有這種騷操作,
而宏任務一般是:包括整體代碼script,setTimeout,setInterval,
微任務:Promise,process.nextTick,
記住就行了,
然后回到開頭的代碼,因為settimeout是宏任務,雖然先執行的他,但是他被放到了宏任務的eventqueue里面,然后代碼繼續往下檢查看有沒有微任務,檢測到Promise的then函式把他放入了微任務序列,等到主線行程的所有代碼執行結束后,先從微任務queue里拿回掉函式,然后微任務queue空了后再從宏任務的queue拿函式,
所以正確的執行結果當然是:2,4,3,1,
https://juejin.im/post/59e85eebf265da430d571f89
5. Js 原型和原型鏈
原型鏈的設計是js的精髓所在,比較抽象,需要從內部設計原理去理解這種設計思想,在紙上畫畫其中的關系會幫助理解,
prototype物件
prototype物件的引入:所有實體物件需要共享的屬性和方法,都放在這個物件中,那些不需要共享的屬性和方法,就放在建構式中,以此來模擬類,
function Animal(name) {
this.name = name
}
Animal.prototype.getName = function() {
console.log(this.name)
}
var animal1 = new Animal('Kate')
var animal2 = new Animal('Lucy')
//物件animal1 和 animal2共享方法getName
animal1.getName()
animal2.getName()
原型鏈
在javascript中,每個物件都有一個指向它的原型(prototype)物件的內部鏈接,每個原型物件又有自己的原型,直到某個物件的原型為null為止,組成這條鏈的最后一環,
*proto寫入es6標準
當一個物件被創建時,它的__protp__屬性和內部屬性[[prototype]]指向相同的物件(也就是它的建構式的prototype屬性),改變__proto__屬性的值同時也會改變內部屬性[[prototype]]的值,除非該物件是不可擴展的,
在ES5中,所有建構式的__proto__都指向Function.prototype
**在ES6中,建構式的__proto__指向它的父類建構式
obj.__proto__ === obj.[[prototype]]
// ES5
Cat.__proto__ === Function.prototype
// ES6
Cat.__proto__ === Animal
建構式繼承
有四種方式可以實作建構式的繼承
1.呼叫apply方法
function Animal() {
this.species = '動物'
}
Animal.prototype.getName = function() {
console.log('我是動物')
}
function Cat() {
Animal.apply(this, arguments)
}
var cat = new Cat()
cat.species // 動物
cat.getName() // undefined
這種方法可以繼承父類建構式的屬性,但是無法繼承prototype屬性,即父類中共享的方法和屬性
2.改寫prototype物件
Cat.prototype = new Animal()
Cat.prototype.constructor = Cat
這是最常用的方法來模擬單繼承,缺點是始終要保留Animal的物件,如果Animal物件比較大時,會消耗部分記憶體(其實很少),并且沒有實作多繼承
3.直接繼承prototype
Cat.prototype = Animal.prototype
Cat.prototype.constructor = Cat
缺點是當修改了Cat.prototype上的方法時會影響Animal.prototype
4.利用空物件作中介
var F = function(){}
F.prototype = Animal.prototype
Cat.prototype = new F()
Cat.prototype.constructor = Cat
6. js的事件回圈機制是什么
行程、執行緒
- 行程是系統分配的獨立資源,是 CPU 資源分配的基本單位,行程是由一個或者多個執行緒組成的,
- 執行緒是行程的執行流,是CPU調度和分派的基本單位,同個行程之中的多個執行緒之間是共享該行程的資源的,
瀏覽器內核
-
瀏覽器是多行程的,瀏覽器每一個 tab 標簽都代表一個獨立的行程(也不一定,因為多個空白 tab 標簽會合并成一個行程),瀏覽器內核(瀏覽器渲染行程)屬于瀏覽器多行程中的一種,
-
瀏覽器內核有多種執行緒在作業,
-
GUI 渲染執行緒:
- 負責渲染頁面,決議 HTML,CSS 構成 DOM 樹等,當頁面重繪或者由于某種操作引起回流都會調起該執行緒,
- 和 JS 引擎執行緒是互斥的,當 JS 引擎執行緒在作業的時候,GUI 渲染執行緒會被掛起,GUI 更新被放入在 JS 任務佇列中,等待 JS 引擎執行緒空閑的時候繼續執行,
-
JS 引擎執行緒:
- 單執行緒作業,負責決議運行 JavaScript 腳本,
- 和 GUI 渲染執行緒互斥,JS 運行耗時過長就會導致頁面阻塞,
-
事件觸發執行緒:
- 當事件符合觸發條件被觸發時,該執行緒會把對應的事件回呼函式添加到任務佇列的隊尾,等待 JS 引擎處理,
-
定時器觸發執行緒:
- 瀏覽器定時計數器并不是由 JS 引擎計數的,阻塞會導致計時不準確,
- 開啟定時器觸發執行緒來計時并觸發計時,計時完成后會被添加到任務佇列中,等待 JS 引擎處理,
-
http 請求執行緒:
-
http 請求的時候會開啟一條請求執行緒,
-
請求完成有結果了之后,將請求的回呼函式添加到任務佇列中,等待 JS 引擎處理,
-
JavaScript 引擎是單執行緒
JavaScript 引擎是單執行緒,也就是說每次只能執行一項任務,其他任務都得按照順序排隊等待被執行,只有當前的任務執行完成之后才會往下執行下一個任務,
HTML5 中提出了 Web-Worker API,主要是為了解決頁面阻塞問題,但是并沒有改變 JavaScript 是單執行緒的本質,了解 Web-Worker,
JavaScript 事件回圈機制
JavaScript 事件回圈機制分為瀏覽器和 Node 事件回圈機制,兩者的實作技術不一樣,瀏覽器 Event Loop 是 HTML 中定義的規范,Node Event Loop 是由 libuv 庫實作,這里主要講的是瀏覽器部分,
Javascript 有一個 main thread 主執行緒和 call-stack 呼叫堆疊(執行堆疊),所有的任務都會被放到呼叫堆疊等待主執行緒執行,
-
JS 呼叫堆疊
JS 呼叫堆疊是一種后進先出的資料結構,當函式被呼叫時,會被添加到堆疊中的頂部,執行完成之后就從堆疊頂部移出該函式,直到堆疊內被清空,
-
同步任務、異步任務
JavaScript 單執行緒中的任務分為同步任務和異步任務,同步任務會在呼叫堆疊中按照順序排隊等待主執行緒執行,異步任務則會在異步有了結果后將注冊的回呼函式添加到任務佇列(訊息佇列)中等待主執行緒空閑的時候,也就是堆疊內被清空的時候,被讀取到堆疊中等待主執行緒執行,任務佇列是先進先出的資料結構,
-
Event Loop
呼叫堆疊中的同步任務都執行完畢,堆疊內被清空了,就代表主執行緒空閑了,這個時候就會去任務佇列中按照順序讀取一個任務放入到堆疊中執行,每次堆疊內被清空,都會去讀取任務佇列有沒有任務,有就讀取執行,一直回圈讀取-執行的操作,就形成了事件回圈,
-
-
-
7.前端跨域的方式
前端跨域的方案:
1、通過jsonp跨域
2、postMessage跨域
3、跨域資源共享(CORS)
4、nginx代理跨域
5、nodejs中間件代理跨域
6、WebSocket協議跨域
7.反向代理
<u>https://segmentfault.com/a/1190000011145364</u>
8.Promise的理解,和promise都有哪些方法
Promise,就是一個物件,用來傳遞異步操作的訊息,避免了層層嵌套的回呼函式,它代表了某個未來才會知道結果的事件(通常是一個異步操作),并且這個事件提供統一的API,可供進一步處理,
(1)物件的狀態不受外界影響,有三種狀態:Pending(進行中)、Resolved(已完成,又稱Fulfilled)和Rejected(已失敗),
(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果,Promise物件的狀態改變,只有兩種可能:從Pending變為Resolved和從Pending變為Rejected,只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果
9.原型和原型鏈的理解
我們創造的每一個函式都有一個prototype(原型)屬性,這個屬性是一個指標,指向原型對 象,在默認情況下,所有的原型物件都會有一個constructor(建構式)屬性,這個屬性包含一個指向prototype屬相所在的指標,當呼叫建構式創建一個新實體之后,該實體內部將包含一個指標(內部屬性),指向建構式的原型物件,
<u>https://juejin.im/post/5ae95290518825672c00c0a4</u>
10:異步方式
1. Promise
2. Generator
3. Async-await
4. Node.js 中的nextTick()和setimmediate()
5. async庫
11:修復bug或添加新功能的常見作業流(git命令)是什么?空和未定義的javascript有什么區別?
git flow feature start f1 添加新特性,這個操作創建了一個基于develop的特性分支,并切換到這個分支之下,
git flow feature finish f1 完成新特性,這個操作會合并f1分支到develop分支,并洗掉特性分支,切換回develop分支,
git flow feature publish f1 發布新分支,發布新特性分支到遠程服務器,其它用戶也可以使用這分支,
修復bug:
git flow hotfix start VERSION [BASENAME] 創建hotfix分支,VERSION 引數標記著修正版本,[BASENAME]為finish release時填寫的版本號,
12:您使用什么框架來撰寫單元測驗,寫下一個案例來驗證呼叫的函式
一、問題描述:
在一個升序陣列中,使用折半查找得到要查詢的值的索引位置,如:
var a=[1,2,3,4,5,6,7,8,9];
search(a,3);//回傳2
search(a,1);//左邊界,回傳0
search(a,9);//右邊界,回傳8
search(a,0);//比最小的值還小,回傳"您查找的數值不存在"
search(a,10);//比最大的值還大,回傳"您查找的數值不存在"
注:折半查找必須在有序陣列中才有效,無序的陣列不能實作查找功能,比如:在[10,5,6,7,8,9,20]中查找10,中間索引位置的值為7,比較得出7比10小,因而應該在右子陣列中查找,實際上不可能找到10;
二、我的實作
function search(arr,num) {
var l=arr.length;
var left=0;
var right=l-1;
var center=Math.floor((left+right)/2);
while(left<=l-1&&right>=0){
if (arr[center]==num) return center;
if (left==right) return "您查找的數不存在";
if (arr[center]>num) {
right=center-1;
center=Math.floor((left+right)/2);
}else if (arr[center]<num) {
left=center+1;
center=Math.floor((left+right)/2);
}
}
}
var a=[1,2,3,4,5,6,7,8,9];
console.log(search(a,-2));
說明:
1、基本思路:
每次比較,如果陣列中間索引位置的值比要查找的值大,就轉而在陣列中間位置之前的子陣列中查找;相反,如果陣列中間索引位置的值比要查找的值大,就轉而在陣列中間位置之后的子陣列中查找;如果陣列中間索引位置的值恰好等于要查找的值,就回傳該索引位置,
2、left定義查找范圍的起始位置,right定義查找范圍的結束位置,center定義查找范圍的中間位置,
3、while中的邏輯說明:
(1)由于不知道具體查找查找多少次,while是比較好的選擇;
(2)回圈結束條件:
a、一旦當right小于0時,就不再查找,再糾纏也不會有結果,例如:在a=[1,2,3,4,5,6,7,8,9]中查找0,當查找范圍變為left=0,right=0,center=0時,進入while陳述句,由于arr[center]>0,故執行
right=center-1;center=Math.floor((left+right)/2);
得到right=-1此時應不再進入回圈;
b、一旦當left>l-1時,就不再查找,同樣再糾纏也不會有結果,例如:在a=[1,2,3,4,5,6,7,8,9]中查找10,當查找范圍變為left=8,right=8,center=8時,進入while陳述句,由于arr[center]<10,故執行
left=center;center=Math.floor((left+right)/2);
得到left=9,此時應不再進入回圈;
4、始終是通過center匹配到要查找的值;
5、Math.floor處理了查找范圍長度為偶數的情況;
6、當left==right了,而arr[center]==num卻沒執行,可以得出結論查找不到的;
7、當arr[center]==num時,整個函式都結束了,后面陳述句是不會執行的,
13.撰寫一個regex運算式以查找內容,內容以2個數字開頭,以結尾
var reg = /1[0-9]$/
14.push 添加陣列后, 是怎么回應的
push() 方法可向陣列的末尾添加一個或多個元素,并回傳新的長度
注釋:該方法會改變陣列的長度,
語法:
arrayObject.push(newelement1,newelement2,…,newelementX)
引數描述
newelement1 必需,要添加到陣列的第一個元素,
newelement2 可選,要添加到陣列的第二個元素,
newelementX 可選,可添加多個元素,
push() 方法可把它的引數順序添加到 arrayObject 的尾部,它直接修改 arrayObject,而不是創建一個新的陣列,push() 方法和 pop() 方法使用陣列提供的先進后出堆疊的功能,
15.js基本資料型別
Undefined、Null、Bollean、Number、String
16.js中和=的區別是什么
前者會自動轉換型別
后者不會
17.for和for in 區別
語法結構上不同,
for 一般用來遍歷陣列的,是比較簡單的操作
for in 一般用來遍歷物件,雖然for in 也能遍歷陣列,但是會存在
以下幾個問題:
1、index索引為字串型數字,不能直接進行幾何運算
2、遍歷順序有可能不是按照實際陣列的內部順序
3、使用for in會遍歷陣列所有的可列舉屬性,包括原型,例如上栗
的原型方法method和name屬性
這也是為什么用for不用for in的區別,如果是遍歷普通陣列的話,
用for是最好的選擇,但是如果是物件,用for in就好了,
18.js中和=的區別是什么
運算元1 == 運算元2, 運算元1 === 運算元2
雙等號==:
(1)如果兩個值型別相同,再進行三個等號(===)的比較
(2)如果兩個值型別不同,也有可能相等,需根據以下規則進行型別轉換在比較:
1)如果一個是null,一個是undefined,那么相等
2)如果一個是字串,一個是數值,把字串轉換成數值之后再進行比較
三等號===:
(1)如果型別不同,就一定不相等
(2)如果兩個都是數值,并且是同一個值,那么相等;如果其中至少一個是NaN,那么不相 等,(判斷一個值是否是NaN,只能使用isNaN( ) 來判斷)
(3)如果兩個都是字串,每個位置的字符都一樣,那么相等,否則不相等,
(4)如果兩個值都是true,或是false,那么相等
(5)如果兩個值都參考同一個物件或是函式,那么相等,否則不相等
(6)如果兩個值都是null,或是undefined,那么相等
19:for和for in 區別
for in:
1.for...in 陳述句用于對陣列或者物件的屬性進行回圈操作,
2.for ... in 回圈中的代碼每執行一次,就會對陣列的元素或者物件的屬性進行一次操作,
3.for...in陳述句以任意順序遍歷一個物件的可列舉屬性,對于每個不同的屬性,陳述句都會被執行,
for :
1.for回圈是對陣列的元素進行回圈,而不能參考于非陣列物件,
20:陣列去重的方法
第一種:
function uniq(array){
var temp = []; //一個新的臨時陣列
for(var i = 0; i < array.length; i++){
if(temp.indexOf(array[i]) == -1){
temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));
第二種:物件鍵值法去重
function uniq(array){
var temp = {}, r = [], len = array.length, val, type;
for (var i = 0; i < len; i++) {
val = array[i];
type = typeof val;
if (!temp[val]) {
temp[val] = [type];
r.push(val);
} else if (temp[val].indexOf(type) < 0) {
temp[val].push(type);
r.push(val);
}
}
return r;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第三種:排序后相鄰去除法
function uniq(array){
array.sort();
var temp=[array[0]];
for(var i = 1; i < array.length; i++){
if( array[i] !== temp[temp.length-1]){
temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第四種:陣列下標法
function uniq(array){
var temp = [];
for(var i = 0; i < array.length; i++) {
//如果當前陣列的第i項在當前陣列中第一次出現的位置是i,才存入陣列;否則代表是重復的
if(array.indexOf(array[i]) == i){
temp.push(array[i])
}
}
return temp;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
第五種:優化遍歷陣列法
function uniq(array){
var temp = [];
var index = [];
var l = array.length;
for(var i = 0; i < l; i++) {
for(var j = i + 1; j < l; j++){
if (array[i] === array[j]){
i++;
j = i;
}
}
temp.push(array[i]);
index.push(i);
}
console.log(index);
return temp;
}
var aa = [1,2,2,3,5,3,6,5];
console.log(uniq(aa));
21:排序的方法
第一種:冒泡排序:
var arr = [1,4,-8,-3,6,12,9,8];
function bubbleSort(arr){
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length-i-1; j++) {
if(arr[j] > arr[j+1]){
var c = arr[j];
arr[j] = arr[j+1];
arr[j+1] = c;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
快速排序:
var arr = [1,4,-8,-3,6,12,9,8];
function quicksort(arr){
if(arr.length <= 1){
return arr;
}
var middleIndex = Math.floor(arr.length/2);
var middleNum = arr.splice(middleIndex,1);
var left = [], right = [];
for (var i = 0; i < arr.length; i++) {
if(arr[i] < middleNum){
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quicksort(left).concat(middleNum, quicksort(right));
}
console.log(quicksort(arr));
選擇排序:
var arr = [1,4,-8,-3,6,12,9,8];
function selectSort(arr){
for(var i=0;i<arr.length;i++){
//設定當前范圍最小值和索引
var min = arr[i];
var minIndex = i;
//在該范圍選出最小值
for(var j=i+1;j<arr.length;j++){
if(min>arr[j]){
min = arr[j];
minIndex = j;
}
}
//將最小值插入,并將原來位置的最小值洗掉
arr.splice(i,0,min);
arr.splice(minIndex+1,1);
}
return arr;
}
console.log(selectSort(arr));
插入排序:
var array = [1,4,-8,-3,6,12,9,8];
function selectSort(arr){
for(var i=0;i<arr.length;i++){
//設定當前范圍最小值和索引
var min = arr[i];
var minIndex = i;
//在該范圍選出最小值
for(var j=i+1;j<arr.length;j++){
if(min>arr[j]){
min = arr[j];
minIndex = j;
}
}
//將最小值插入,并將原來位置的最小值洗掉
arr.splice(i,0,min);
arr.splice(minIndex+1,1);
}
}
selectSort(array);
document.write(array);
22:冒泡排序
var arr = [1,4,-8,-3,6,12,9,8];
function bubbleSort(arr){
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr.length-i-1; j++) {
if(arr[j] > arr[j+1]){
var c = arr[j];
arr[j] = arr[j+1];
arr[j+1] = c;
}
}
}
return arr;
}
console.log(bubbleSort(arr));
23:原型鏈的理解:
在談原型鏈之前,我們首先要了解自定義函式與 Function 之間是什么關系,而建構式、原型和實體之間又存在什么千絲萬縷的關系呢?其實,所有的函式都是 Function 的實體,在建構式上都有一個原型屬性 prototype,該屬性也是一個物件;那么在原型物件上有一個 constructor 屬性,該屬性指向的就是建構式;而實體物件上有一個 _proto_ 屬性,該屬性也指向原型物件,并且該屬性不是標準屬性,不可以用在編程中,該屬性用于瀏覽器內部使用,
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QqhVpWo7-1630416218424)(en-resource://database/450:1)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8tp8hnPS-1630416218430)(en-resource://database/452:1)]
1、原型鏈
1)建構式、原型和實體的關系
①建構式都有一個屬性prototype,這個屬性是一個物件(Object的實體)
②原型物件prototype里面有一個constructor屬性,該屬性指向原型物件所屬的建構式
③實體物件都有一個_proto_屬性,該屬性也指向建構式的原型物件,它是一個非標準屬性,
不可以用于編程,它是用于瀏覽器自己使用的
2)prototype與_proto_的關系
①prototype是建構式的屬性
②_proto_是實體物件的屬性 ——這兩者都指向同一個物件
【總結】
i)函式也是物件,物件不一定是函式;
ii)物件的本質:無序的鍵值對集合;鍵值對當中的值可以是任意資料型別的值
iii)物件就是一個容器,這個容器當中放的是(屬性和方法)
3)屬性搜索
①在訪問物件的某個成員的時候會先在物件中找是否存在
②如果當前物件中沒有就在建構式的原型物件中找
③如果原型物件中沒有找到就到原型物件的原型上找
④知道Object的原型物件的原型是null為止
2、Function——
所有函式都是Function的實體
`①本地物件:獨立于宿主環境(瀏覽器)的物件——包括Object、Array、Date、RegExp、 Function、Error、Number、String、Boolean
②內置物件——包括Math、Global(window,在js中就是全域變數),使用的時候不需要 new
③宿主物件——包括自定義物件、DOM、BOM
24:改變this指向的方法
第一種: new關鍵字改變this指向
function Fn(){
this.user = "追夢子";
}
var a = new Fn();
console.log(a.user); //追夢子
用變數a創建了一個Fn的實體(相當于復制了一份Fn到物件a里面),此時僅僅只是創建,并沒有執行,而呼叫這個函式Fn的是物件a,那么this指向的自然是物件a,那么為什么物件a中會有user,因為你已經復制了一份Fn函式到物件a中,用了new關鍵字就等同于復制了一份第二種: call()
第二種: call()
var a = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
}
}
var b = a.fn;
b.call(a); //若不用call,則b()執行后this指的是Window物件
把b添加到第一個引數的環境中,簡單來說,this就會指向那個物件,
第三種:apply()
var a = {
user:"追夢子",
fn:function(){
console.log(this.user); //追夢子
}
}
var b = a.fn;
b.apply(a);
第四種:bind()
var a = {
user:"追夢子",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a); //代碼沒有被列印
我們發現代碼沒有被列印,對,這就是bind和call、apply方法的不同,實際上bind方法回傳的是一個修改過后的函式,
25:es6新特性
1. 變數宣告
let 與 const:
可以把let看成var,只是它定義的變數被限定在了特定范圍內才能使用,而離開這個范圍則無效,const則很直觀,用來定義常量,即無法被更改值的變數,
for (let i=0;i<2;i++)console.log(i);//輸出: 0,1
console.log(i);//輸出:undefined,嚴格模式下會報錯
2.類的支持
ES6中添加了對類的支持,引入了class關鍵字(其實class在JavaScript中一直是保留字,目的就是考慮到可能在以后的新版本中會用到,現在終于派上用場了),JS本身就是面向物件的,ES6中提供的類實際上只是JS原型模式的包裝,現在提供原生的class支持后,物件的創建,繼承更加直觀了,并且父類方法的呼叫,實體化,靜態方法和建構式等概念都更加形象化,
//類的定義
class Animal {
//ES6中新型構造器
constructor(name) {
this.name = name;
}
//實體方法
sayName() {
console.log('My name is '+this.name);
}
}
//類的繼承
class Programmer extends Animal {
constructor(name) {
//直接呼叫父類構造器進行初始化
super(name);
}
program() {
console.log("I'm coding...");
}
}
//測驗我們的類
var animal=new Animal('dummy'),
wayou=new Programmer('wayou');
animal.sayName();//輸出 ‘My name is dummy’
wayou.sayName();//輸出 ‘My name is wayou’
wayou.program();//輸出 ‘I'm coding...’
3.字串模板
字串模板相對簡單易懂些,ES6中允許使用反引號 ` 來創建字串,此種方法創建的字串里面可以包含由美元符號加花括號包裹的變數${vraible},如果你使用過像C#等后端強型別語言的話,對此功能應該不會陌生,
//產生一個亂數
var num=Math.random();
//將這個數字輸出到console
console.log(`your num is ${num}`);
4.解構:
自動決議陣列或物件中的值,比如若一個函式要回傳多個值,常規的做法是回傳一個物件,將每個值做為這個物件的屬性回傳,但在ES6中,利用解構這一特性,可以直接回傳一個陣列,然后陣列中的值會自動被決議到對應接收該值的變數中,
var [x,y]=getVal(),//函式回傳值的解構
[name,,age]=['wayou','male','secrect'];//陣列解構
function getVal() {
return [ 1, 2 ];
}
console.log('x:'+x+', y:'+y);//輸出:x:1, y:2
console.log('name:'+name+', age:'+age);//輸出: name:wayou, age:secrect
5.Promise:
Promises是處理異步操作的一種模式,之前在很多三方庫中有實作,比如jQuery的deferred 物件,當你發起一個異步請求,并系結了.when(), .done()等事件處理程式時,其實就是在應用promise模式,
//創建promise
var promise = new Promise(function(resolve, reject) {
// 進行一些異步或耗時操作
if ( /*如果成功 */ ) {
resolve("Stuff worked!");
} else {
reject(Error("It broke"));
}
});
//系結處理程式
promise.then(function(result) {
//promise成功的話會執行這里
console.log(result); // "Stuff worked!"
}, function(err) {
//promise失敗會執行這里
console.log(err); // Error: "It broke"
});
26.promise的理解
? ES6提供的解決異步處理方法
? 有兩個優點
? 1.promise物件的狀態不受外界影響
? -pending 初始狀態
? -fulfilled 成功狀態
? -rejected 失敗狀態
? 2.promise的狀態一旦改變,就不會再變,狀態不可逆,只能由pending變成pending變成fulfilled或者由pending變成rejected
? 三個缺點
? 1.無法取消promise,一旦新建它就會立即執行,無法中途取消
? 2.如果不設定回呼函式,promise內部拋出的錯誤,不會反映到外部
? 3.當處于pending狀態時,無法得知目前進展到哪一個階段
? 用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
?
27.同源策略
? 同源策略是瀏覽器的一個安全功能,不同源的客戶端腳本在沒有明確授權的情況下,不能讀寫對方資源,同源指的是協議,域名和埠號均相同則屬于同源
28.前端跨域的方式
? 使用jsonp跨域,因為script標簽引入的js是不受同源策略的限制,通過script標簽引入一個js或者是一個其他后綴形式(如php,jsp等)的檔案,此時檔案回傳一個JS函式的呼叫
? 通過cors跨域,實作cors通信的關鍵是服務器,只要服務器實作cors介面,就可以跨域
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://segmentfault.com/u/trigkit4/",true);
xhr.send();
</script>
? 反向代理跨域,反向代理指的是在前端的服務器環境中, 短暫的開啟一個后端服務器, 由后端服務器進行資料請求, 然后在將結果回傳給前端
29.AMD,CMD模塊化
模塊化的開發方式可以提高代碼復用率,方便進行代碼的管理,通常一個檔案就是一個模塊,有自己的作用域,只向外暴露特定的變數和函式,目前流行的js模塊化規范有CommonJS、AMD、CMD以及ES6的模塊系統
1、AMD推崇依賴前置,在定義模塊的時候就要宣告其依賴的模塊
2、CMD推崇就近依賴,只有在用到某個模塊的時候再去require
這種區別各有優劣,只是語法上的差距,而且requireJS和SeaJS都支持對方的寫法
AMD和CMD最大的區別是對依賴模塊的執行時機處理不同,注意不是加載的時機或者方式不同
30.報表繪圖類的框架
? highcharts http://www.highcharts.com/
? jscharts http://www.jscharts.com/
? AdminLTE http://adminlte.la998.com/
31.庫和插件的源代碼
? 庫和框架都是一種有別于軟體、面向程式開發者的產品形式,
庫是將代碼集合成的一個產品,供程式員呼叫,面向物件的代碼組織形式而成的庫也叫
類別庫,
框架則是為解決一個(一類)問題而開發的產品,框架用戶一般只需要使用框架提供的類
或函式,即可實作全部功能,
32.函式柯里化是什么 ?
柯里化(英語:Currying),又稱為部分求值,是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,并且回傳一個新的函式的技術,新函式接受余下引數并回傳運算結果,
具體內容見 https://juejin.im/entry/58b316d78d6d810058678579
33.哪些情況下會造成記憶體泄漏 ?
1)意外的全域變數引起的記憶體泄露
?```javascript
function leak () {
leak="xxx"; //leak成為一個全域變數,不會被回收 相當于 window.leak = 'XXX'
}
?```
2. 閉包可以維持函式內區域變數,使其得不到釋放,
3. 沒有清理的DOM元素參考
4. 被遺忘的定時器或者回呼
34.性能優化 ?
1. 定義區域變數.查找區域變數比全域變數要快,
2. 不濫用閉包,
3. 合并js檔案,減少http請求
4. 避免使用for-in回圈
5. 盡量不用with,eval陳述句,try-catch的catch子句要謹慎使用
35.作業中閉包的使用案例?使用過什么閉包工具庫嘛
1. 閉包經典使用場景一:通過回圈給頁面上多個dom節點系結事件
?```javascript
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<button>Button0</button>
<button>Button1</button>
<button>Button2</button>
<button>Button3</button>
<button>Button4</button>
</body>
</html>
for(var i = 0, len = btns.length; i < len; i++) {
(function(i) {
btns[i].onclick = function() {
alert(i);
}
}(i))
}
?```
2. 封裝變數 閉包可以將一些不希望暴露在全域的變數封裝成“私有變數”,
3. 閉包使用場景三:延續區域變數的壽命
閉包工具庫:???
36.301 302如何重定向 ?
301 redirect: 301 代表永久性轉移(Permanently Moved)
302 redirect: 302 代表暫時性轉移(Temporarily Moved )
詳細來說,301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器回傳的這個狀態碼后會自動跳轉到一個新的URL地址,這個地址可以從回應的Location首部中獲取(用戶看到的效果就是他輸入的地址A瞬間變成了另一個地址B)——這是它們的共同點,他們的不同在于,301表示舊地址A的資源已經被永久地移除(這個資源不可訪問了),搜索引擎在抓取新內容的同時也將舊的網址交換為重定向之后的網址;302表示舊地址A的資源還在(仍然可以訪問),這個重定向只是臨時地從舊地址A跳轉到地址B,搜索引擎會抓取新的內容而保存舊的網址,
37.暫時性死區 ?
舉例:
?```javascript
console.log (a) //由于變數提升,輸出undefined
var a
?```
?```javascript
console.log(a) //報錯 ReferenceError: a is not defined
let a
?```
ES6規定,let/const 命令會使區塊形成封閉的作用域,若在宣告之前使用變數,就會報錯,
總之,在代碼塊內,使用 let 命令宣告變數之前,該變數都是不可用的,
這在語法上,稱為 “暫時性死區”( temporal dead zone,簡稱 TDZ),
38.堆和堆疊
- 堆疊(stack):為自動分配的記憶體空間,他由系統自動釋放,存放在堆疊記憶體中的簡單資料段,資料大小確定,記憶體空間大小可以分配,是直接按值存放的,所以可以直接訪問,基本的資料型別放在堆疊中
- 堆(heap):則是動態分配的記憶體,大小不定也不會自動釋放,參考型別(object)是存放在堆記憶體中的,變數實際上是一個存放在堆疊記憶體的指標,這個指標指向堆記憶體中的地址,每個空間大小不一樣,要根據情況進行特定的分配,
39.箭頭函式的this指向
- 箭頭函式默認不會使用自己的this,而是會和外層的this保持一致,最外層的this就是window物件,在多層對像嵌套里箭頭函式里this是和最最外層保持一致的
40.深拷貝和淺拷貝的區別
- 淺拷貝:只復制指向某個物件的指標,而不復制物件本身,新舊物件共享一塊記憶體
- 深拷貝:復制并創建一個一模一樣的物件,不共享記憶體,修改新物件,舊物件保持不變,
41.手寫一個promise,采用es5的方面,promise的原始碼理解
-
var fn=function(resolve, reject){ console.log('begin to execute!'); var number=Math.random(); if(number<=0.5){ resolve('less than 0.5'); }else{ reject('greater than 0.5'); } } var p=new Promise(fn); p.then(function(data){ console.log('resolve: ', data); }, function(data){ console.log('reject: ', data); }) 對promise原始碼的理解: 當我們運行 var p=new Promise(fn) 這條陳述句的時候,fn函式就已經執行了,然而,p.then這個方法是在后面才定義了resolve和reject,那么為何fn函式能夠知道resolve和reject函式是什么呢?換句話說,resolve和reject函式是如何回到過去,出現在先執行的fn函式當中的呢?要解決這個問題,主要運用的就是setTimeout這個方法,來延遲fn當中resolve和reject的執行,我們知道js是單執行緒+訊息佇列,必須等主執行緒代碼執行完畢才能開始執行訊息佇列當中的代碼,因此,會首先執行then這個方法,給里面兩個引數賦值, 加入狀態:pending, resolved, rejected 在Promise規范當中,規定Promise只能從初始pending狀態變到resolved或者rejected狀態,是單向變化的,也就是說執行了resolve就不會再執行reject,反之亦然,并在必要的地方進行判斷,防止重復執行, function MyPromise(fn) { this.value; this.status = 'pending'; this.resolveFunc = function() {}; this.rejectFunc = function() {}; fn(this.resolve.bind(this), this.reject.bind(this)); } MyPromise.prototype.resolve = function(val) { var self = this; if (this.status == 'pending') { //判斷狀態 this.status = 'resolved'; this.value=val; setTimeout(function() { self.resolveFunc(self.value); }, 0); } } MyPromise.prototype.reject = function(val) { //判斷狀態 var self = this; if (this.status == 'pending') { this.status = 'rejected'; this.value=val; setTimeout(function() { self.rejectFunc(self.value); }, 0); } } MyPromise.prototype.then = function(resolveFunc, rejectFunc) { this.resolveFunc = resolveFunc; this.rejectFunc = rejectFunc; } 鏈式呼叫: 要實作鏈式呼叫,then方法的回傳值也必須是一個Promise物件,這樣才能再次在后面呼叫then,
42.async與promise的區別:
- 在函式前有一個關鍵字
async,await關鍵字只能在使用async定義的函式中使用,任何一個async函式都會隱式回傳一個promise,并且promise resolve 的值就是 return 回傳的值 - 不能在函式開頭使用
await - Async 函式的實作最簡潔,最符合語意,幾乎沒有語意不相關的代碼,
- Promise 的寫法比回呼函式的寫法大大改進,但是一眼看上去,代碼完全都是 Promise 的 API(
then、catch等等),操作本身的語意反而不容易看出來,
43.async是promise暴露出來的語法糖?
- async函式就是generator函式的語法糖
44.箭頭函式與function的區別?
(1) 箭頭函式與function定義函式的寫法:
//function
function fn(a, b){
return a + b;
}
//arrow function
var foo = (a, b)=>{ return a + b };
(2) this的指向:
使用function定義的函式,this的指向隨著呼叫環境的變化而變化的,而箭頭函式中的this指向是固定不變的,一直指向的是定義函式的環境,
(3) 建構式
function是可以定義建構式的,而箭頭函式是不行的,
(4) 變數提升
由于js的記憶體機制,function的級別最高,而用箭頭函式定義函式的時候,需要var(let const定義的時候更不必說)關鍵詞,而var所定義的變數不能得到變數提升,故箭頭函式一定要定義于呼叫之前!
45.箭頭函式中沒有this物件,this是最近的this
(1) 由于箭頭函式不系結this, 它會捕獲其所在(即定義的位置)背景關系的this值, 作為自己的this值
(2)方法的箭頭函式this指向全域window物件,而普通函式則指向呼叫它的物件,箭頭函式沒有this
46.箭頭函式可以作為建構式嗎?
因為箭頭函式沒有自己的this,它的this其實是繼承了外層執行環境中的this,且this指向永遠不會隨在哪里呼叫、被誰呼叫而改變,所以箭頭函式不能作為建構式使用,或者說建構式不能定義成箭頭函式,否則用new呼叫時會報錯!
47.箭頭函式替代arguments的方法?
箭頭函式沒有自己的arguments物件,在箭頭函式中訪問arguments實際上獲得的是外層區域(函式)執行環境中的值,可以在箭頭函式中使用rest引數代替arguments物件,來訪問箭頭函式的引數串列
48.解構和負載的場景?
-
解構賦值,即對某種結構進行決議,然后將決議出來的值賦值給相關的變數,常見的有陣列、物件、字串的解構賦值等
-
陣列解構
只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值,let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3 -
物件結構
物件的解構與陣列有一個重要的不同,陣列的元素是按次序排列的,變數的取值由它的位置決定;而物件的屬性沒有次序,變數必須與屬性同名,才能取到正確的值,
let { bar, foo } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb" -
字串結構
字串被轉換成了一個類似陣列的物件
const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o"
-
-
負載
49.擴展運算子了解?
-
概念
擴展運算子(spread)是三個點(…),它好比 rest 引數的逆運算,將一個陣列轉為用逗號分隔的引數序列,擴展運算子與正常的函式引數可以結合使用,后面也可以放置運算式,但如果后面是一個空陣列,則不產生任何效果,
let arr = []; arr.push(...[1,2,3,4,5]); console.log(arr); //[1,2,3,4,5] console.log(1, ...[2, 3, 4], 5) //1 2 3 4 5 console.log(...(1 > 0 ? ['a'] : [])); //a console.log([...[], 1]); //[1] -
應用
1 替代函式的apply方法
由于擴展運算子可以展開陣列,所以不再需要apply方法,將陣列轉為函式的引數了,// ES5 的寫法 Math.max.apply(null, [14, 3, 77]) // ES6 的寫法 Math.max(...[14, 3, 77])2 復制陣列
// ES5 的寫法 const a1 = [1, 2]; const a2 = a1.concat(); // ES6 的寫法 const a1 = [1, 2]; onst a2 = [...a1]; //或 const [...a2] = a1;3 合并陣列
// ES5 的寫法 [1, 2].concat(more); arr1.concat(arr2, arr3); // ES6 的寫法 [1, 2, ...more]; [...arr1, ...arr2, ...arr3]4 與結構賦值結合
// ES5 的寫法 a = list[0], rest = list.slice(1) // ES6 的寫法 [a, ...rest] = list
50.深拷貝的方法?
答:對陣列進行深拷貝:
- for回圈
- slice方法
- concat方法
- ES6擴展運算子
對物件進行深拷貝: - for回圈
- 先轉換成json在轉換成物件
- ES6擴展運算子
51.擴展運算子的方法進行拷貝,是深拷貝還是淺拷貝?
答:深拷貝
52.set的用法:陣列去重,
答:有兩種方法:
方法一: Set + Array.from()
var set1 = Array.from(new Set([1,1,2,2,33,'33',44,'44'
]))
方法二: …[拓展運算子] + Set
var tt = [...new Set([5,5,6,6,8,])] // 5,6,8
53.es6判斷是否是陣列:isArray,以及其他判斷陣列的方法?typeof 檢測陣列回傳值
答:
判斷陣列方法:
-
1.用instanceof判斷
使用instanceof運算子可以分辨陣列和物件,可以判斷陣列是陣列,
-
2.用constructor判斷
實體化的陣列擁有一個constructor屬性,這個屬性指向生成這個陣列的方法,
當constructor屬性被修改之后,就無法用這個方法判斷陣列是陣列了 -
3.用Object的toString方法判斷
toString方法將會回傳"[object type]",其中的type代表的是物件的型別,根據type的值,我們就可以判斷這個疑似陣列的物件到底是不是陣列了
-
4.用Array物件的isArray方法判斷
isArray方法回傳true,當引數不為陣列的時候,isArray方法回傳false
typeof 檢測陣列回傳值
typeof是javascript原生提供的判斷資料型別的運算子,它會回傳一個表示引數的資料型別的字串
54.陣列去重的方法?
答:
- 利用ES6中的Set
- 利用for嵌套for,然后splice去重(ES5中最常用)
- 利用indexOf去重
- 利用sort()
- 利用includes
- 利用hasOwnProperty
- 利用filter
- 利用遞回去重
- 利用Map資料結構去重
- 利用reduce+includes
55.陣列中出現重復數字的重復統計?
思路:
//統計一個陣列中有多少個不重復的單詞:
// 不用reduce時:
var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){
var obj = {};
for(var i= 0, l = arr.length; i< l; i++){
var item = arr[i];
obj[item] = (obj[item] +1 ) || 1;
}
return obj;
}
console.log(getWordCnt());//{apple: 2, orange: 3, pear: 1}
// 用reduce時:
var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){
return arr.reduce(function(prev,next){
prev[next] = (prev[next] + 1) || 1;
return prev;
},{});
}
console.log(getWordCnt());//{apple: 2, orange: 3, pear: 1}
56.陣列常見的api
- 陣列api又叫應用程式介面,函式方法
- 常見api
- arr.push():資料添加 - 在陣列尾部添加元素
push方法一次可添加單個或多個元素到陣列末端,也可以添加陣列
陣列名.push("元素1","元素2",...) - arr.pop():資料更新 - 洗掉陣列的最后一個元素
pop方法的作用是移除陣列末尾的一個元素,把陣列長度減1,并且回傳它的洗掉值,如果陣列為空,則pop()不改變陣列,回傳undefind
陣列名.pop() - arr.concat():連接兩個或更多的陣列,并回傳結果,
- arr.join():字符連接
若不想要任何連接符,則括號中用空字符即可,
陣列名.join("連接符") - arr.reverse():顛倒陣列中元素的順序,
- arr.shift():洗掉資料 - 移除陣列頂端的元素
shift方法與pop相反,移除陣列的第一個元素并將其回傳,該方法執行后,陣列剩下的元素向前移動,下標索引號重新調整從0開始,
陣列名.shift() - arr.unshift():添加資料 - 在陣列頭部添加元素
nshift方法與push方法正好相反,是將元素插入陣列的首部,一次可以插入單個或多個元素,所有元素按順序插入,操作完成后回傳新陣列的參考
陣列名.unshift("元素1","元素2",...) - arr.slice():生成特定資料 - 獲取陣列中的一部分元素
slice方法的作用是抽取陣列的一段元素,抽取指定下標索引區間中的元素作為新陣列回傳
陣列名.slice(start,end)
注:splice是直接修改原陣列,而slice不會修改原陣列, - arr.sort():對陣列的元素進行排序
- arr.splice():更新移動資料 - 洗掉、替換或插入陣列元素
splice方法的作用是,從一個陣列中移除一個或多個元素,剩下的元素組成一個陣列,移除的元素組成另一個陣列并回傳它的參考,同時,原陣列可以在移除的開始位置處順帶插入一個或多個新元素,達到修改替換陣列元素的目的,這個操作效果通常稱為接合
陣列名.splice(start,deleteCount,item1,item2,...)
引數說明:
start:必選項,表示從陣列中剪切的起始位置下標索引號,
deteleCount:必選項,表示從陣列中切取的元素個數,
item:可選項,表示切取時插入原陣列切入點開始出的一個或多個元素, - arr.indexof():查找元素對應下標
- arr.forEach():陣列遍歷
- arr.filter():陣列遍歷,將回傳的值新建成一個新陣列
- arr.map():遍歷陣列,直接操作陣列,回傳一個新陣列
- arr.reduce():陣列歸并
- arr.push():資料添加 - 在陣列尾部添加元素
57.陣列中sort的用法,sort的回傳值?沒有傳比較函式的比較?
用法:對陣列的元素進行排序
arr.sort()直接操作原有陣列,回傳原有陣列,當沒有傳比較函式的話不會按大小排序,而是按順序排序
var arr = [22,12,3,43,56,47,4];
arr.sort();
console.log(arr); // [12, 22, 3, 4, 43, 47, 56]
arr.sort(function (m, n) {
if (m < n) return -1
else if (m > n) return 1
else return 0
});
console.log(arr); // [3, 4, 12, 22, 43, 47, 56]
58.canvas與webGL的區別
- 概要
Canvas 位圖,是需要自己畫點的白板;
WebGL 3D位圖,是基于 Canvas 的 3D 框架,
- 用途
Canvas 適用于位圖,高資料量高繪制頻率(幀率)的場景,如影片、游戲;
WebGL 主要用來做 3D 展示、影片、游戲,
- canvas缺點:
- 只能繪制2D影像,暫時不支持3D影像,
-canvas繪制圖形出并非可以直接操作的dom物件,如果要對其進行類似dom的操作,例如添加屬性等等,比較麻煩(這就是為什么必須使用類別庫),
- canvas優點:
- 由于canvas繪圖不會給每個點生成物件,所以繪制速度快,消耗記憶體少,(這點主要是相對于SVG,VML技術而言)
- 兼容性較好,除了IE6,其他瀏覽器都可以支持,(IE7,8需要載入擴展JS,終究還是能用的)
- webGL
是一項使用JavaScript實作3D繪圖的技術,瀏覽器無需插件支持,Web開發者直接使用js呼叫相關API就能借助系統顯卡(GPU)進行撰寫代碼從而呈現3D場景和物件,
59.BOM物件與DOM物件的區別?實作由BOM物件還是先有DOM物件
BOM:瀏覽器物件模型(Brower Object Model),是用于操作瀏覽器而出現的API,BOM物件則是Javascript對BOM介面的實作,
BOM提供了獨立于內容的、可以與瀏覽器視窗進行互動的物件結構,通過BOM物件可以訪問瀏覽器功能部件和屬性,
BOM中代表瀏覽器視窗的window物件是Javascript頂層物件,其他BOM物件均為window物件的子物件,被作為window物件的屬性來參考,
其他BOM物件都是在window物件中進行操作,
DOM 是檔案物件模型,比如 html 是樹結構的,操作 dom 就是操作這顆樹:
DOM:檔案物件模型(Document Object Model),是W3C定義的一套用于處理HTML和XML檔案內容的標準編程介面API,javascript實作DOM介面的物件對應的是document物件,JS通過該物件來對HTML/XML檔案進行增刪改查,DOM定義了HTML和XML的邏輯結構,將整個頁面劃分成由層次節點構成的檔案,以樹的形式來展現,如上面那張圖所示,
在BOM和DOM結構層次圖中,document物件屬于window物件,所以DOM也可以看作是BOM的一部分
0-9 ??
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/296903.html
標籤:其他
上一篇:不同方向程式員工資一覽!
