let、const、var的差別
(1)塊級作用域: 塊作用域由
{ }
包括,let和const具有塊級作用域,var不存在塊級作用域。塊級作用域解決了ES5中的兩個問題:
- 内層變量可能覆寫外層變量
- 用來計數的循環變量洩露為全局變量
(2)變量提升: var存在變量提升,let和const不存在變量提升,即在變量隻能在聲明之後使用,否在會報錯。
(3)給全局添加屬性: 浏覽器的全局對象是window,Node的全局對象是global。var聲明的變量為全局變量,并且會将該變量添加為全局對象的屬性,但是let和const不會。
(4)重複聲明: var聲明變量時,可以重複聲明變量,後聲明的同名變量會覆寫之前聲明的周遊。const和let不允許重複聲明變量。
(5)暫時性死區: 在使用let、const指令聲明變量之前,該變量都是不可用的。這在文法上,稱為暫時性死區。使用var聲明的變量不存在暫時性死區。
(6)初始值設定: 在變量聲明時,var 和 let 可以不用設定初始值。而const聲明變量必須設定初始值。
(7)指針指向: let和const都是ES6新增的用于建立變量的文法。 let建立的變量是可以更改指針指向(可以重新指派)。但const聲明的變量是不允許改變指針的指向。
差別 | var | let | const |
---|---|---|---|
是否有塊級作用域 | × | ✔️ | ✔️ |
是否存在變量提升 | ✔️ | × | × |
是否添加全局屬性 | ✔️ | × | × |
能否重複聲明變量 | ✔️ | × | × |
是否存在暫時性死區 | × | ✔️ | ✔️ |
是否必須設定初始值 | × | × | ✔️ |
能否改變指針指向 | ✔️ | ✔️ | × |
深淺拷貝
淺拷貝:隻考慮對象類型。
function shallowCopy(obj) {
if (typeof obj !== 'object') return
let newObj = obj instanceof Array ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key]
}
}
return newObj
}
複制代碼
複制
簡單版深拷貝:隻考慮普通對象屬性,不考慮内置對象和函數。
function deepClone(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
}
}
return newObj;
}
複制代碼
複制
複雜版深克隆:基于簡單版的基礎上,還考慮了内置對象比如 Date、RegExp 等對象和函數以及解決了循環引用的問題。
const isObject = (target) => (typeof target === "object" || typeof target === "function") && target !== null;
function deepClone(target, map = new WeakMap()) {
if (map.get(target)) {
return target;
}
// 擷取目前值的構造函數:擷取它的類型
let constructor = target.constructor;
// 檢測目前對象target是否與正則、日期格式對象比對
if (/^(RegExp|Date)$/i.test(constructor.name)) {
// 建立一個新的特殊對象(正則類/日期類)的執行個體
return new constructor(target);
}
if (isObject(target)) {
map.set(target, true); // 為循環引用的對象做标記
const cloneTarget = Array.isArray(target) ? [] : {};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop], map);
}
}
return cloneTarget;
} else {
return target;
}
}
複制代碼
複制
什麼是中間人攻擊?如何防範中間人攻擊?
中間⼈ (Man-in-the-middle attack, MITM) 是指攻擊者與通訊的兩端分别建立獨⽴的聯系, 并交換其所收到的資料, 使通訊的兩端認為他們正在通過⼀個私密的連接配接與對⽅直接對話, 但事實上整個會話都被攻擊者完全控制。在中間⼈攻擊中,攻擊者可以攔截通訊雙⽅的通話并插⼊新的内容。
攻擊過程如下:
- 用戶端發送請求到服務端,請求被中間⼈截獲
- 伺服器向用戶端發送公鑰
- 中間⼈截獲公鑰,保留在⾃⼰⼿上。然後⾃⼰⽣成⼀個僞造的公鑰,發給用戶端
- 用戶端收到僞造的公鑰後,⽣成加密hash值發給伺服器
- 中間⼈獲得加密hash值,⽤⾃⼰的私鑰解密獲得真秘鑰,同時⽣成假的加密hash值,發給伺服器
- 伺服器⽤私鑰解密獲得假密鑰,然後加密資料傳輸給用戶端
對浏覽器的緩存機制的了解
浏覽器緩存的全過程:
- 浏覽器第一次加載資源,伺服器傳回 200,浏覽器從伺服器下載下傳資源檔案,并緩存資源檔案與 response header,以供下次加載時對比使用;
- 下一次加載資源時,由于強制緩存優先級較高,先比較目前時間與上一次傳回 200 時的時間差,如果沒有超過 cache-control 設定的 max-age,則沒有過期,并命中強緩存,直接從本地讀取資源。如果浏覽器不支援HTTP1.1,則使用 expires 頭判斷是否過期;
- 如果資源已過期,則表明強制緩存沒有被命中,則開始協商緩存,向伺服器發送帶有 If-None-Match 和 If-Modified-Since 的請求;
- 伺服器收到請求後,優先根據 Etag 的值判斷被請求的檔案有沒有做修改,Etag 值一緻則沒有修改,命中協商緩存,傳回 304;如果不一緻則有改動,直接傳回新的資源檔案帶上新的 Etag 值并傳回 200;
- 如果伺服器收到的請求沒有 Etag 值,則将 If-Modified-Since 和被請求檔案的最後修改時間做比對,一緻則命中協商緩存,傳回 304;不一緻則傳回新的 last-modified 和檔案并傳回 200;
很多網站的資源後面都加了版本号,這樣做的目的是:每次更新了 JS 或 CSS 檔案後,為了防止浏覽器進行緩存,強制改變版本号,用戶端浏覽器就會重新下載下傳新的 JS 或 CSS 檔案 ,以保證使用者能夠及時獲得網站的最新更新。
代碼輸出結果
f = function() {return true;};
g = function() {return false;};
(function() {
if (g() && [] == ![]) {
f = function f() {return false;};
function g() {return true;}
}
})();
console.log(f());
複制代碼
複制
輸出結果: false
這裡首先定義了兩個變量f和g,我們知道變量是可以重新指派的。後面是一個匿名自執行函數,在 if 條件中調用了函數 g(),由于在匿名函數中,又重新定義了函數g,就覆寫了外部定義的變量g,是以,這裡調用的是内部函數 g 方法,傳回為 true。第一個條件通過,進入第二個條件。
第二個條件是[] == ![],先看 ![] ,在 JavaScript 中,當用于布爾運算時,比如在這裡,對象的非空引用被視為 true,空引用 null 則被視為 false。由于這裡不是一個 null, 而是一個沒有元素的數組,是以 [] 被視為 true, 而 ![] 的結果就是 false 了。當一個布爾值參與到條件運算的時候,true 會被看作 1, 而 false 會被看作 0。現在條件變成了 [] == 0 的問題了,當一個對象參與條件比較的時候,它會被求值,求值的結果是數組成為一個字元串,[] 的結果就是 '' ,而 '' 會被當作 0 ,是以,條件成立。
兩個條件都成立,是以會執行條件中的代碼, f 在定義是沒有使用var,是以他是一個全局變量。是以,這裡會通過閉包通路到外部的變量 f, 重新指派,現在執行 f 函數傳回值已經成為 false 了。而 g 則不會有這個問題,這裡是一個函數内定義的 g,不會影響到外部的 g 函數。是以最後的結果就是 false。
有哪些可能引起前端安全的問題?
- 跨站腳本 (Cross-Site Scripting, XSS): ⼀種代碼注⼊⽅式, 為了與 CSS 區分是以被稱作 XSS。早期常⻅于⽹絡論壇, 起因是⽹站沒有對⽤戶的輸⼊進⾏嚴格的限制, 使得攻擊者可以将腳本上傳到帖⼦讓其他⼈浏覽到有惡意腳本的⻚⾯, 其注⼊⽅式很簡單包括但不限于 JavaScript / CSS / Flash 等;
- iframe的濫⽤: iframe中的内容是由第三⽅來提供的,預設情況下他們不受控制,他們可以在iframe中運⾏JavaScirpt腳本、Flash插件、彈出對話框等等,這可能會破壞前端⽤戶體驗;
- 跨站點請求僞造(Cross-Site Request Forgeries,CSRF): 指攻擊者通過設定好的陷阱,強制對已完成認證的⽤戶進⾏⾮預期的個⼈資訊或設定資訊等某些狀态更新,屬于被動攻擊
- 惡意第三⽅庫: ⽆論是後端伺服器應⽤還是前端應⽤開發,絕⼤多數時候都是在借助開發架構和各種類庫進⾏快速開發,⼀旦第三⽅庫被植⼊惡意代碼很容易引起安全問題。
一個 tcp 連接配接能發幾個 http 請求?
如果是 HTTP 1.0 版本協定,一般情況下,不支援長連接配接,是以在每次請求發送完畢之後,TCP 連接配接即會斷開,是以一個 TCP 發送一個 HTTP 請求,但是有一種情況可以将一條 TCP 連接配接保持在活躍狀态,那就是通過 Connection 和 Keep-Alive 首部,在請求頭帶上 Connection: Keep-Alive,并且可以通過 Keep-Alive 通用首部中指定的,用逗号分隔的選項調節 keep-alive 的行為,如果用戶端和服務端都支援,那麼其實也可以發送多條,不過此方式也有限制,可以關注《HTTP 權威指南》4.5.5 節對于 Keep-Alive 連接配接的限制和規則。
而如果是 HTTP 1.1 版本協定,支援了長連接配接,是以隻要 TCP 連接配接不斷開,便可以一直發送 HTTP 請求,持續不斷,沒有上限; 同樣,如果是 HTTP 2.0 版本協定,支援多用複用,一個 TCP 連接配接是可以并發多個 HTTP 請求的,同樣也是支援長連接配接,是以隻要不斷開 TCP 的連接配接,HTTP 請求數也是可以沒有上限地持續發送
li 與 li 之間有看不見的空白間隔是什麼原因引起的?如何解決?
浏覽器會把inline内聯元素間的空白字元(空格、換行、Tab等)渲染成一個空格。為了美觀,通常是一個
<li>
放在一行,這導緻
<li>
換行後産生換行字元,它變成一個空格,占用了一個字元的寬度。
解決辦法:
(1)為
<li>
設定float:left。不足:有些容器是不能設定浮動,如左右切換的焦點圖等。
(2)将所有
<li>
寫在同一行。不足:代碼不美觀。
(3)将
<ul>
内的字元尺寸直接設為0,即font-size:0。不足:
<ul>
中的其他字元尺寸也被設為0,需要額外重新設定其他字元尺寸,且在Safari浏覽器依然會出現空白間隔。
(4)消除
<ul>
的字元間隔letter-spacing:-8px,不足:這也設定了
<li>
内的字元間隔,是以需要将
<li>
内的字元間隔設為預設letter-spacing:normal。
介紹下 promise 的特性、優缺點,内部是如何實作的,動手實作 Promise
1)Promise基本特性
- 1、Promise有三種狀态:pending(進行中)、fulfilled(已成功)、rejected(已失敗)
- 2、Promise對象接受一個回調函數作為參數, 該回調函數接受兩個參數,分别是成功時的回調resolve和失敗時的回調reject;另外resolve的參數除了正常值以外, 還可能是一個Promise對象的執行個體;reject的參數通常是一個Error對象的執行個體。
- 3、then方法傳回一個新的Promise執行個體,并接收兩個參數onResolved(fulfilled狀态的回調);onRejected(rejected狀态的回調,該參數可選)
- 4、catch方法傳回一個新的Promise執行個體
- 5、finally方法不管Promise狀态如何都會執行,該方法的回調函數不接受任何參數
- 6、Promise.all()方法将多個多個Promise執行個體,包裝成一個新的Promise執行個體,該方法接受一個由Promise對象組成的數組作為參數(Promise.all()方法的參數可以不是數組,但必須具有Iterator接口,且傳回的每個成員都是Promise執行個體),注意參數中隻要有一個執行個體觸發catch方法,都會觸發Promise.all()方法傳回的新的執行個體的catch方法,如果參數中的某個執行個體本身調用了catch方法,将不會觸發Promise.all()方法傳回的新執行個體的catch方法
- 7、Promise.race()方法的參數與Promise.all方法一樣,參數中的執行個體隻要有一個率先改變狀态就會将該執行個體的狀态傳給Promise.race()方法,并将傳回值作為Promise.race()方法産生的Promise執行個體的傳回值
- 8、Promise.resolve()将現有對象轉為Promise對象,如果該方法的參數為一個Promise對象,Promise.resolve()将不做任何處理;如果參數thenable對象(即具有then方法),Promise.resolve()将該對象轉為Promise對象并立即執行then方法;如果參數是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法傳回一個新的Promise對象,狀态為fulfilled,其參數将會作為then方法中onResolved回調函數的參數,如果Promise.resolve方法不帶參數,會直接傳回一個fulfilled狀态的 Promise 對象。需要注意的是,立即resolve()的 Promise 對象,是在本輪“事件循環”(event loop)的結束時執行,而不是在下一輪“事件循環”的開始時。
- 9、Promise.reject()同樣傳回一個新的Promise對象,狀态為rejected,無論傳入任何參數都将作為reject()的參數
2)Promise優點
- ①統一異步 API
- Promise 的一個重要優點是它将逐漸被用作浏覽器的異步 API ,統一現在各種各樣的 API ,以及不相容的模式和手法。
- ②Promise 與事件對比
- 和事件相比較, Promise 更适合處理一次性的結果。在結果計算出來之前或之後注冊回調函數都是可以的,都可以拿到正确的值。 Promise 的這個優點很自然。但是,不能使用 Promise 處理多次觸發的事件。鍊式處理是 Promise 的又一優點,但是事件卻不能這樣鍊式處理。
- ③Promise 與回調對比
- 解決了回調地獄的問題,将異步操作以同步操作的流程表達出來。
- ④Promise 帶來的額外好處是包含了更好的錯誤處理方式(包含了異常處理),并且寫起來很輕松(因為可以重用一些同步的工具,比如 Array.prototype.map() )。
3)Promise缺點
- 1、無法取消Promise,一旦建立它就會立即執行,無法中途取消。
- 2、如果不設定回調函數,Promise内部抛出的錯誤,不會反應到外部。
- 3、當處于Pending狀态時,無法得知目前進展到哪一個階段(剛剛開始還是即将完成)。
- 4、Promise 真正執行回調的時候,定義 Promise 那部分實際上已經走完了,是以 Promise 的報錯堆棧上下文不太友好。
4)簡單代碼實作
最簡單的Promise實作有7個主要屬性, state(狀态), value(成功傳回值), reason(錯誤資訊), resolve方法, reject方法, then方法
class Promise{
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
}
};
try {
// 立即執行函數
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
let x = onFulfilled(this.value);
};
if (this.state === 'rejected') {
let x = onRejected(this.reason);
};
}
}
複制
5)面試夠用版
function myPromise(constructor){ let self=this;
self.status="pending" //定義狀态改變前的初始狀态
self.value=undefined;//定義狀态為resolved的時候的狀态
self.reason=undefined;//定義狀态為rejected的時候的狀态
function resolve(value){
//兩個==="pending",保證了了狀态的改變是不不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//兩個==="pending",保證了了狀态的改變是不不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕獲構造異常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved": onFullfilled(self.value); break;
case "rejected": onRejected(self.reason); break;
default:
}
}
// 測試
var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//輸出1
複制
6)大廠專供版
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
const resolvePromise = (promise, x, resolve, reject) => {
if (x === promise) {
// If promise and x refer to the same object, reject promise with a TypeError as the reason.
reject(new TypeError('循環引用'))
}
// if x is an object or function,
if (x !== null && typeof x === 'object' || typeof x === 'function') {
// If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
let called
try { // If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
let then = x.then // Let then be x.then
// If then is a function, call it with x as this
if (typeof then === 'function') {
// If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
// If/when rejectPromise is called with a reason r, reject promise with r.
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, r => {
if (called) return
called = true
reject(r)
})
} else {
// If then is not a function, fulfill promise with x.
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
// If x is not an object or function, fulfill promise with x
resolve(x)
}
}
function Promise(excutor) {
let that = this; // 緩存目前promise執行個體例對象
that.status = PENDING; // 初始狀态
that.value = undefined; // fulfilled狀态時 傳回的資訊
that.reason = undefined; // rejected狀态時 拒絕的原因
that.onFulfilledCallbacks = []; // 存儲fulfilled狀态對應的onFulfilled函數
that.onRejectedCallbacks = []; // 存儲rejected狀态對應的onRejected函數
function resolve(value) { // value成功态時接收的終值
if(value instanceof Promise) {
return value.then(resolve, reject);
}
// 實踐中要確定 onFulfilled 和 onRejected ⽅方法異步執⾏行行,且應該在 then ⽅方法被調⽤用的那⼀一輪事件循環之後的新執⾏行行棧中執⾏行行。
setTimeout(() => {
// 調⽤用resolve 回調對應onFulfilled函數
if (that.status === PENDING) {
// 隻能由pending狀态 => fulfilled狀态 (避免調⽤用多次resolve reject)
that.status = FULFILLED;
that.value = value;
that.onFulfilledCallbacks.forEach(cb => cb(that.value));
}
});
}
function reject(reason) { // reason失敗态時接收的拒因
setTimeout(() => {
// 調⽤用reject 回調對應onRejected函數
if (that.status === PENDING) {
// 隻能由pending狀态 => rejected狀态 (避免調⽤用多次resolve reject)
that.status = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach(cb => cb(that.reason));
}
});
}
// 捕獲在excutor執⾏行行器器中抛出的異常
// new Promise((resolve, reject) => {
// throw new Error('error in excutor')
// })
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
let newPromise;
// 處理理參數預設值 保證參數後續能夠繼續執⾏行行
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected = typeof onRejected === "function" ? onRejected : reason => {
throw reason;
};
if (that.status === FULFILLED) { // 成功态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try{
let x = onFulfilled(that.value);
resolvePromise(newPromise, x, resolve, reject); //新的promise resolve 上⼀一個onFulfilled的傳回值
} catch(e) {
reject(e); // 捕獲前⾯面onFulfilled中抛出的異常then(onFulfilled, onRejected);
}
});
})
}
if (that.status === REJECTED) { // 失敗态
return newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(that.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
if (that.status === PENDING) { // 等待态
// 當異步調⽤用resolve/rejected時 将onFulfilled/onRejected收集暫存到集合中
return newPromise = new Promise((resolve, reject) => {
that.onFulfilledCallbacks.push((value) => {
try {
let x = onFulfilled(value);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
that.onRejectedCallbacks.push((reason) => {
try {
let x = onRejected(reason);
resolvePromise(newPromise, x, resolve, reject);
} catch(e) {
reject(e);
}
});
});
}
};
複制
說一說SessionStorage和localStorage還有cookie
共同點:都是儲存在浏覽器端、且同源的
不同點:
1.cookie資料始終在同源的http請求中攜帶(即使不需要),即cookie在浏覽器和伺服器間來回傳遞。
cookie資料還有路徑(path)的概念,可以限制cookie隻屬于某個路徑下
sessionStorage和localStorage不會自動把資料發送給伺服器,僅在本地儲存。
2.存儲大小限制也不同,cookie資料不能超過4K,sessionStorage和localStorage可以達到5M
3.sessionStorage:僅在目前浏覽器視窗關閉之前有效;
localStorage:始終有效,視窗或浏覽器關閉也一直儲存,本地存儲,是以用作持久資料;
cookie:隻在設定的cookie過期時間之前有效,即使視窗關閉或浏覽器關閉
4.作用域不同
sessionStorage:不在不同的浏覽器視窗中共享,即使是同一個頁面;
localstorage:在所有同源視窗中都是共享的;也就是說隻要浏覽器不關閉,資料仍然存在
cookie: 也是在所有同源視窗中都是共享的.也就是說隻要浏覽器不關閉,資料仍然存在
複制代碼
複制
二分查找--時間複雜度 log2(n)
題目描述:如何确定一個數在一個有序數組中的位置
實作代碼如下:
function search(arr, target, start, end) {
let targetIndex = -1;
let mid = Math.floor((start + end) / 2);
if (arr[mid] === target) {
targetIndex = mid;
return targetIndex;
}
if (start >= end) {
return targetIndex;
}
if (arr[mid] < target) {
return search(arr, target, mid + 1, end);
} else {
return search(arr, target, start, mid - 1);
}
}
// const dataArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// const position = search(dataArr, 6, 0, dataArr.length - 1);
// if (position !== -1) {
// console.log(`目标元素在數組中的位置:${position}`);
// } else {
// console.log("目标元素不在數組中");
// }
複制代碼
複制
對 CSSSprites 的了解
CSSSprites(精靈圖),将一個頁面涉及到的所有圖檔都包含到一張大圖中去,然後利用CSS的 background-image,background-repeat,background-position屬性的組合進行背景定位。
優點:
- 利用
能很好地減少網頁的http請求,進而大大提高了頁面的性能,這是CSS Sprites
最大的優點;CSS Sprites
-
能減少圖檔的位元組,把3張圖檔合并成1張圖檔的位元組總是小于這3張圖檔的位元組總和。CSS Sprites
缺點:
- 在圖檔合并時,要把多張圖檔有序的、合理的合并成一張圖檔,還要留好足夠的空間,防止闆塊内出現不必要的背景。在寬屏及高分辨率下的自适應頁面,如果背景不夠寬,很容易出現背景斷裂;
-
在開發的時候相對來說有點麻煩,需要借助CSSSprites
或其他工具來對每個背景單元測量其準确的位置。photoshop
- 維護方面:
在維護的時候比較麻煩,頁面背景有少許改動時,就要改這張合并的圖檔,無需改的地方盡量不要動,這樣避免改動更多的CSS Sprites
,如果在原來的地方放不下,又隻能(最好)往下加圖檔,這樣圖檔的位元組就增加了,還要改動CSS
。CSS
兩欄布局的實作
一般兩欄布局指的是左邊一欄寬度固定,右邊一欄寬度自适應,兩欄布局的具體實作:
- 利用浮動,将左邊元素寬度設定為200px,并且設定向左浮動。将右邊元素的margin-left設定為200px,寬度設定為auto(預設為auto,撐滿整個父元素)。
.outer {
height: 100px;
}
.left {
float: left;
width: 200px;
background: tomato;
}
.right {
margin-left: 200px;
width: auto;
background: gold;
}
複制代碼
複制
- 利用浮動,左側元素設定固定大小,并左浮動,右側元素設定overflow: hidden; 這樣右邊就觸發了BFC,BFC的區域不會與浮動元素發生重疊,是以兩側就不會發生重疊。
.left{
width: 100px;
height: 200px;
background: red;
float: left;
}
.right{
height: 300px;
background: blue;
overflow: hidden;
}
複制代碼
複制
- 利用flex布局,将左邊元素設定為固定寬度200px,将右邊的元素設定為flex:1。
.outer {
display: flex;
height: 100px;
}
.left {
width: 200px;
background: tomato;
}
.right {
flex: 1;
background: gold;
}
複制代碼
複制
- 利用絕對定位,将父級元素設定為相對定位。左邊元素設定為absolute定位,并且寬度設定為200px。将右邊元素的margin-left的值設定為200px。
.outer {
position: relative;
height: 100px;
}
.left {
position: absolute;
width: 200px;
height: 100px;
background: tomato;
}
.right {
margin-left: 200px;
background: gold;
}
複制代碼
複制
- 利用絕對定位,将父級元素設定為相對定位。左邊元素寬度設定為200px,右邊元素設定為絕對定位,左邊定位為200px,其餘方向定位為0。
.outer {
position: relative;
height: 100px;
}
.left {
width: 200px;
background: tomato;
}
.right {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 200px;
background: gold;
}
複制代碼
複制
New操作符做了什麼事情?
1、首先建立了一個新對象
2、設定原型,将對象的原型設定為函數的prototype對象
3、讓函數的this指向這個對象,執行構造函數的代碼(為這個新對象添加屬性)
4、判斷函數的傳回值類型,如果是值類型,傳回建立的對象。如果是引用類型,就傳回這個引用類型的對象
複制代碼
複制
Canvas和SVG的差別
(1)SVG: SVG可縮放矢量圖形(Scalable Vector Graphics)是基于可擴充标記語言XML描述的2D圖形的語言,SVG基于XML就意味着SVG DOM中的每個元素都是可用的,可以為某個元素附加Javascript事件處理器。在 SVG 中,每個被繪制的圖形均被視為對象。如果 SVG 對象的屬性發生變化,那麼浏覽器能夠自動重制圖形。
其特點如下:
- 不依賴分辨率
- 支援事件處理器
- 最适合帶有大型渲染區域的應用程式(比如谷歌地圖)
- 複雜度高會減慢渲染速度(任何過度使用 DOM 的應用都不快)
- 不适合遊戲應用
(2)Canvas: Canvas是畫布,通過Javascript來繪制2D圖形,是逐像素進行渲染的。其位置發生改變,就會重新進行繪制。
其特點如下:
- 依賴分辨率
- 不支援事件處理器
- 弱的文本渲染能力
- 能夠以 .png 或 .jpg 格式儲存結果圖像
- 最适合圖像密集型的遊戲,其中的許多對象會被頻繁重繪
注:矢量圖,也稱為面向對象的圖像或繪圖圖像,在數學上定義為一系列由線連接配接的點。矢量檔案中的圖形元素稱為對象。每個對象都是一個自成一體的實體,它具有顔色、形狀、輪廓、大小和螢幕位置等屬性。
iframe 有那些優點和缺點?
iframe 元素會建立包含另外一個文檔的内聯架構(即行内架構)。
優點:
- 用來加載速度較慢的内容(如廣告)
- 可以使腳本可以并行下載下傳
- 可以實作跨子域通信
缺點:
- iframe 會阻塞首頁面的 onload 事件
- 無法被一些搜尋引擎索識别
- 會産生很多頁面,不容易管理
畫一條0.5px的線
- 采用transform: scale()的方式,該方法用來定義元素的2D 縮放轉換:
transform: scale(0.5,0.5);
複制代碼
複制
- 采用meta viewport的方式
<meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5"/>
複制代碼
複制
這樣就能縮放到原來的0.5倍,如果是1px那麼就會變成0.5px。viewport隻針對于移動端,隻在移動端上才能看到效果
display:inline-block 什麼時候會顯示間隙?
- 有空格時會有間隙,可以删除空格解決;
-
正值時,可以讓margin
使用負值解決;margin
- 使用
時,可通過設定font-size
、font-size:0
、letter-spacing
解決;word-spacing