問題:說一下 css 的盒模型css
盒模型分爲標準模型和怪異盒模型(IE 盒模型)html
標準盒模型:盒模型的寬高只是內容(content)的寬高前端
怪異盒模型:盒模型的寬高是內容(content)+填充(padding)+邊框(border)的總寬高vue
問題:css 如何設置兩種模型react
/* 標準模型 */
box-sizing:content-box;
/*IE模型*/
box-sizing:border-box;
複製代碼
問題:有沒有遇到過邊距重疊,怎麼解決 邊距重疊問題以下圖所示 linux
利用 BFC 去解決,下方詳細說明 BFCgit
參考代碼:利用 BFC 的特性,把另外一個盒子放入另外一個獨立的 BFC 中,從而使得兩個盒子之間互相不受影響es6
<section class="top">
<h1>上</h1>
margin-bottom:30px;
</section>
<div style="overflow: hidden;">
<section class="bottom">
<h1>下</h1>
margin-top:50px;
</section>
</div>
複製代碼
問題:說一下 BFCgithub
什麼是 BFC面試
BFC(Block Formatting Context)格式化上下文,是 Web 頁面中盒模型佈局的 CSS 渲染模式,指一個獨立的渲染區域或者說是一個隔離的獨立容器。
造成 BFC 的條件
BFC 的特性
rem 佈局的本質是等比縮放,通常是基於寬度,假設將屏幕寬度分爲 100 份,每份寬度是 1rem,1rem 的寬度是屏幕寬度/100,,而後子元素設置 rem 單位的屬性
經過改變 html 元素的字體大小,就能夠設置子元素的實際大小。
比 rem 更好的方案(缺點兼容很差)
vw(1vw 是視口寬度的 1%,100vw 就是視口寬度),vh(100vh 就是視口高度)
(兩側定寬,中間自適應)
這裏寫出五種實現方式:
/* css */
.box {
display: flex;
justify-content: center;
height: 200px;
}
.left {
width: 200px;
background-color: red;
height: 100%;
}
.content {
background-color: yellow;
flex: 1;
}
.right {
width: 200px;
background-color: green;
}
/* html */
<div class="box">
<div class="left"></div>
<div class="content"></div>
<div class="right"></div>
</div>
複製代碼
/* css */
.box {
height: 200px;
}
.left {
width: 200px;
background-color: red;
float: left;
height: 100%;
}
.content {
background-color: yellow;
height: 100%;
}
.right {
width: 200px;
background-color: green;
float: right;
height: 100%;
}
/* html */
<div class="box">
<div class="left"></div>
<div class="right"></div>
<div class="content"></div>
</div>
複製代碼
/* css */
.box {
position: relative;
height: 200px;
}
.left {
width: 200px;
background-color: red;
left: 0;
height: 100%;
position: absolute;
}
.content {
background-color: yellow;
left: 200px;
right: 200px;
height: 100%;
position: absolute;
}
.right {
width: 200px;
background-color: green;
right: 0;
height: 100%;
position: absolute;
}
/* html */
<div class="box">
<div class="left"></div>
<div class="content"></div>
<div class="right"></div>
</div>
複製代碼
/* css */
.box {
display: table;
height: 200px;
}
.left {
width: 200px;
background-color: red;
height: 100%;
display: table-cell;
}
.content {
background-color: yellow;
height: 100%;
display: table-cell;
}
.right {
width: 200px;
background-color: green;
height: 100%;
display: table-cell;
}
/* html */
<div class="box">
<div class="left"></div>
<div class="content"></div>
<div class="right"></div>
</div>
複製代碼
/* css */
.box {
display: grid;
grid-template-columns: 200px auto 200px;
grid-template-rows: 200px;
}
.left {
background-color: red;
}
.content {
background-color: yellow;
}
.right {
background-color: green;
}
/* html */
<div class="box">
<div class="left"></div>
<div class="content"></div>
<div class="right"></div>
</div>
複製代碼
這裏寫出五種實現方式:
/* css */
#box{
width: 400px;
height: 200px;
position: relative;
background: red;
}
#box1{
width: 200px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
margin-left: -100px;
margin-top: -50px;
background: green;
}
/* html */
<div id="box">
<div id="box1">
</div>
</div>
複製代碼
/* css */
#box{
width: 800px;
height: 400px;
position: relative;
background: red;
}
#box1{
width: 100px;
height: 50px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
background: green;
}
/* html */
<div id="box">
<div id="box1">
</div>
</div>
複製代碼
/* css */
#box{
width: 400px;
height: 200px;
background: #f99;
display: flex;
justify-content: center;//實現水平居中
align-items: center;//實現垂直居中
}
#box1{
width: 200px;
height: 100px;
background: green;
}
/* html */
<div id="box">
<div id="box1">
</div>
</div>
複製代碼
/* css */
#box{
width: 400px;
height: 200px;
background: red;
position: relative;
}
#box1{
width: 200px;
height: 100px;
background: #9ff;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* html */
<div id="box">
<div id="box1">
</div>
</div>
複製代碼
/* css */
#box{
display: table-cell;
vertical-align: middle
}
#box1{
margin: 0 auto;
}
/* html */
<div id="box">
<div id="box1">
</div>
</div>
複製代碼
可以讀取其餘函數內部變量的函數,或簡單理解爲定義在一個函數內部的函數,內部函數持有外部函數內變量的引用。
閉包的用途:
閉包是 JavaScript 一個很是重要的特性,這意味着當前做用域老是可以訪問外部做用域中的變量。 由於 函數 是 JavaScript 中惟一擁有自身做用域的結構,所以閉包的建立依賴於函數。
類似之處:
手動實現 call 方法:
Function.prototype.myCall = function(context = window, ...rest) {
context.fn = this; //此處this是指調用myCall的function
let result = context.fn(...rest);
//將this指向銷燬
delete context.fn;
return result;
};
複製代碼
手動實現 apply 方法:
Function.prototype.myCall = function(context = window, params = []) {
context.fn = this; //此處this是指調用myCall的function
let result
if (params.length) {
result = context.fn(...params)
}else {
result = context.fn()
}
//將this指向銷燬
delete context.fn;
return result;
};
複製代碼
手動實現 bind 方法:
Function.prototype.myBind = function(oThis, ...rest) {
let _this = this;
let F = function() {}
// 根據 bind 規定,若是使用 new 運算符構造 bind 的返回函數時,第一個參數綁定的 this 失效
let resFn = function(...parmas) {
return _this.apply(this instanceof resFn ? this : oThis, [
...rest,
...parmas
]);
};
// 繼承原型
if (this.prototype) {
F.prototype = this.prototype;
resFn.prototype = new F;
}
return resFn;
};
複製代碼
每一個函數都有 prototype
每個對象都有 __proto__
實例的 __proto__
指向構造函數的 prototype
js 引擎會沿着 __proto__
-> ptototype 的順序一直往上方查找,找到 Object.prototype 爲止,Object 爲原生底層對象,到這裏就中止了查找,若是沒有找到,就會報錯或者返回 undefined
經常使用繼承:組合繼承,寄生組合繼承
組合繼承: 利用 call 繼承父類上的屬性,用子類的原型等於父類實例去繼承父類的方法
缺點:調用兩次父類,形成性能浪費
function Parent(name) {
this.name = name;
}
Parent.prototype.say = function() {
console.log(this.name);
};
function Child(name) {
Parent.call(this, name)
}
Child.prototype = new Parent;
let c = new Child("YaoChangTuiQueDuan");
c.say()
複製代碼
寄生組合繼承:利用 call 繼承父類上的屬性,用一個乾淨的函數的原型去等於父類原型,再用子類的原型等於乾淨函數的實例
function Parent(name) {
this.name = name;
}
Parent.prototype.say = function() {
console.log(this.name);
};
function ExtendMiddle() {}
function Child(name) {
Parent.call(this, name)
}
ExtendMiddle.prototype = Parent.prototype;
Child.prototype = new ExtendMiddle
let c = new Child("YaoChangTuiQueDuan");
c.say()
複製代碼
請參考文章 Eventloop 不可怕,可怕的是趕上 Promise
function create(...rest) {
// 建立一個空的對象
let obj = new Object()
// 得到構造函數
let Con = rest.shift()
// 連接到原型
obj.__proto__ = Con.prototype
// 綁定 this,執行構造函數
let result = Con.apply(obj, arguments)
// 確保 new 出來的是個對象
return typeof result === 'object' ? result : obj
}
複製代碼
Promise 的幾個特性:
Promise 的簡單實現:
class MyPromise {
constructor(fn) {
this.resolvedCallbacks = [];
this.rejectedCallbacks = [];
this.state = "PADDING";
this.value = "";
fn(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {
if (this.state === "PADDING") {
this.state = "RESOLVED";
this.value = value;
this.resolvedCallbacks.forEach(cb => cb());
}
}
reject(value) {
if (this.state === "PADDING") {
this.state = "REJECTED";
this.value = value;
this.rejectedCallbacks.forEach(cb => cb());
}
}
then(resolve = function() {}, reject = function() {}) {
if (this.state === "PADDING") {
this.resolvedCallbacks.push(resolve);
this.rejectedCallbacks.push(reject);
}
if (this.state === "RESOLVED") {
resolve(this.value);
}
if (this.state === "REJECTED") {
reject(this.value);
}
}
}
複製代碼
能夠在 Object 的 prototype 上掛載一個 Symbol.iterator
方法,該方法返回一個對象,對象中包含 next
方法, next
方法也會返回一個對象,對象中包含 value
和 done
。 value 表示 每次迭代完成的值,done 表示是否迭代完成,爲 false 時會繼續迭代,爲 true 表示迭代完成,中止迭代
Object.prototype[Symbol.iterator] = function () {
let values = Object.values(this);
let keys = Object.keys(this);
let len = 0;
return {
next() {
return {
value: {
value: values[len],
key: keys[len++]
},
done: len > values.length
}
}
}
}
複製代碼
爲了方便,Node爲每一個模塊提供一個 exports 變量,指向 module.exports,至關於 exports 是 module.exports 地址的引用
會產生的問題:若是將 exports 新賦值了一個對象,如: exports = {},這個時候,就會打斷與 module.exports 的聯繫,會致使導出不成功
如何區分深拷貝與淺拷貝,簡單點來講,就是假設B複製了A,當修改A時,看B是否會發生變化,若是B也跟着變了,說明這是淺拷貝,若是B沒變,那就是深拷貝。
因爲 js 中分爲兩種變量類型,基本數據類型和引用數據類型,基本數據類型包括,number,string,boolean,null,undefined,symbol。
引用數據類型(Object類)有常規名值對的無序對象 {a:1}
,數組 [1,2,3]
,以及函數等。
而這兩類數據存儲分別是這樣的:
基本類型--名值存儲在棧內存中,例如let a=1;
當你 b=a 複製時,棧內存會新開闢一個內存,例如這樣:
因此當你此時修改 a=2,對 b 並不會形成影響,由於此時的 b 已更換內存地址,不受 a 的影響了
引用數據類型--名存在棧內存中,值存在於堆內存中,可是棧內存會提供一個引用的地址指向堆內存中的值
當 b=a 進行拷貝時,其實複製的是 a 的引用地址,而並不是堆裏面的值。
而當咱們 a[0]=1 時進行數組修改時,因爲 a 與 b 指向的是同一個地址,因此天然 b 也受了影響,這就是所謂的淺拷貝了。
那,若是咱們須要實現深拷貝的效果,就須要在堆內存中也開闢一個新的的內存空間來存放 b 的值,如圖:
深拷貝的實現方式:
json.parse & json.stringify
先將一個對象轉爲json對象。而後再解析這個json對象,但此方法有幾個缺點:
遞歸方式實現深拷貝(ps:這裏只是簡易版,完整版建議看 lodash 的 cloneDeep 方法源碼)
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
let toString = Object.prototype.toString;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
//判斷ojb子元素是否爲對象,若是是,遞歸複製
if ( toString.call(obj[key]) === "[object Object]" || toString.call(obj[key]) === "[object Array]" ) {
objClone[key] = deepClone(obj[key]);
} else {
//若是不是,簡單複製
objClone[key] = obj[key];
}
}
}
return objClone;
}
複製代碼
棧(stack):棧會自動分配內存空間,會自動釋放,存放基本類型,簡單的數據段,佔據固定大小的空間。 堆(heap):動態分配的內存,大小不定也不會自動釋放,存放引用類型
爲何會有棧內存和堆內存之分?
一般與垃圾回收機制有關。爲了使程序運行時佔用的內存最小。
當一個方法執行時,每一個方法都會創建本身的內存棧,在這個方法內定義的變量將會逐個放入這塊棧內存裏,隨着方法的執行結束,這個方法的內存棧也將天然銷燬了。所以,全部在方法中定義的變量都是放在棧內存中的;
當咱們在程序中建立一個對象時,這個對象將被保存到運行時數據區中,以便反覆利用(由於對象的建立成本一般較大),這個運行時數據區就是堆內存。堆內存中的對象不會隨方法的結束而銷燬,即便方法結束後,這個對象還可能被另外一個引用變量所引用(方法的參數傳遞時很常見),則這個對象依然不會被銷燬,只有當一個對象沒有任何引用變量引用它時,系統的垃圾回收機制纔會在覈實的時候回收它。
方案一:
分頁,懶加載,把數據分頁,而後每次接受必定的數據,避免一次性接收太多
複製代碼
方案二:
setInterval,setTimeout,requestAnimationFrame 分批渲染,讓數據在不一樣幀內去作渲染
複製代碼
方案三:
使用 virtual-scroll,虛擬滾動。
這種方式是指根據容器元素的高度以及列表項元素的高度來顯示長列表數據中的某一個部分,而不是去完整地渲染長列表,以提升無限滾動的性能
virtual-scroll原理:
在用戶滾動時,改變列表在可視區域的渲染部分
以下圖所示:
startOffset 和 endOffset 會撐開容器元素的內容高度,讓其可持續的滾動;此外,還能保持滾動條處於一個正確的位置。
當瀏覽器再次訪問一個已經訪問過的資源時,它會這樣作:
瀏覽器緩存的位置:
緩存的實現: 強緩存和協商緩存都是根據 HTTP Header 來實現的
迴流:佈局或者幾何屬性須要改變就稱爲迴流。 重繪:當節點須要更改外觀而不會影響佈局的,好比改變 color 就叫稱爲重繪
區別:
迴流必將引發重繪,而重繪不必定會引發迴流。好比:只有顏色改變的時候就只會發生重繪而不會引發迴流 當頁面佈局和幾何屬性改變時就須要迴流
好比:添加或者刪除可見的DOM元素,元素位置改變,元素尺寸改變——邊距、填充、邊框、寬度和高度,內容改變
MVVM是雙向數據綁定
view 層經過事件去綁定 Model 層, Model 層經過數據去綁定 View 層
在以前,渲染數據時,會直接替換掉 DOM 裏的全部元素,換成新的數據,爲了渲染無用 DOM 所形成的性能浪費,因此出現了 Virtual DOM, Virtual DOM 是虛擬 DOM,是用 js 對象表示的樹結構,把 DOM 中的屬性映射到 js 的對象屬性中,它的核心定義無非就幾個關鍵屬性,標籤名、數據、子節點、鍵值等。當數據改變時,從新渲染這個 js 的對象結構,找出真正須要更新的 DOM 節點,再去渲染真實 DOM。Virtual DOM 本質就是在 JS 和 DOM 之間作了一個緩存
由於 DOM 是屬於渲染引擎中的東西,而 JS 又是 JS 引擎中的東西。當咱們經過 JS 操做 DOM 的時候,其實這個操做涉及到了兩個線程之間的通訊,那麼勢必會帶來一些性能上的損耗。操做 DOM 次數一多,也就等同於一直在進行線程之間的通訊,而且操做 DOM 可能還會帶來重繪迴流的狀況,因此也就致使了性能上的問題。
Vue 內部使用了 Object.defineProperty()
來實現數據響應式,經過這個函數能夠監聽到 set
和 get
的事件。
Object.defineProperty()
給 data 中的屬性去設置 set
, get
事件get
事件中去收集觀察者依賴set
事件中去通知每個觀察者,作到所有更新vuex 就像一個全局的倉庫,公共的狀態或者複雜組件交互的狀態咱們會抽離出來放進裏面。
vuex的核心主要包括如下幾個部分:
commit mutations
去修改狀態使用:
父子組件:父組件經過 Props 傳遞子組件,子組件經過 $emit 通知父組件 兄弟組件:可使用 vuex 全局共享狀態,或 eventBus
能夠經過插件 vuex-persistedstate 來解決 插件原理:利用 HTML5 的本地存儲 + Vuex.Store 的方法,去同步本地和 store 中的數據,作到同步更新
待補充...
兩種路由模式分爲 hash 模式 和 history 模式
hash 模式:
hash 模式背後的原理是 onhashchange 事件,能夠在window對象上監聽這個事件,在 hash 變化時,瀏覽器會記錄歷史,而且觸發事件回調,在 hash 模式中,地址欄中會帶一個 #
history 模式:
前面的 hashchange,你只能改變 # 後面的 url 片斷,而 history api 則給了前端徹底的自由
history api能夠分爲兩大部分,切換和修改,參考 MDN
使用 history 須要服務端的支持,在hash模式下,前端路由修改的是#中的信息,而瀏覽器請求時是不帶着的,因此沒有問題.可是在history下,你能夠自由的修改path,當刷新時,若是服務器中沒有相應的響應或者資源,會刷出一個404來
待補充...
待補充...
待補充...
待補充...
待補充...
待補充...
已知以下數組:
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
編寫一個程序將數組扁平化去併除其中重複部分數據,最終獲得一個升序且不重複的數組
方法一:
function handleArr(arr) {
let flatten = (arr) => arr.push ? arr.reduce((pre, current) => [...pre, ...flatten(current)], []) : [arr];
return [...new Set(flatten(arr))].sort((a, b) => a - b);
}
方法二:
[...new Set(arr.toString().split(",").map(Number))].sort((a,b)=> a - b)
方法三:
[...new Set(arr.flat(Infinity))].sort((a,b)=>{ return a - b})
複製代碼
let test = (function (a) {
this.a = a;
return function (b) {
console.log(this.a + b);
}
})((function(a){
return a;
})(1,2))
test(4)
複製代碼
答案:5
解析:咱們把(function (a) { this.a = a; return function (b) { console.log(this.a + b); } })()
自執行函數體叫作 z1
把 (function(a){ return a; })(1,2)
叫作 z2
把function (b) { console.log(this.a + b); }
叫作 n1
test 函數爲匿名自執行函數z1
的返回值,實際上 test 函數爲 n1
,函數 z1
接收 a 做爲參數,這裏的 a 實際上爲自執行函數 z2
返回值爲 1, 那麼 n1
函數中的參數 a 就是 1,在函數 z1
中 this.a = a
這裏的 this 指向的是 window
因此在 window
上掛載了一個 a 的屬性,值爲 1
在 text(4) 執行時,傳入了 b 爲 4, 函數 n1
中的 this 指向的是 window
,window 中有以前掛載的屬性 a 爲 1 因此函數執行結果爲 5