當你對一個文檔進行佈局時,瀏覽器引擎會根據css-box模型將全部元素描述爲一個盒子,css會決定這些盒子的大小,位置,屬性(顏色邊框等)css
盒模型分爲兩類:IE盒模型和標準盒模型,二者區別在於html
IE盒模型的width/height = content +border + padding
標準盒模型的width/height = content
複製代碼
IE盒模型jquery
標準盒模型當你想讓兩個子容器float:left,寬度各50%,而後給一點padding,最後讓子容器並排充滿父容器,一切想的挺美好,然而你發現結果並非這麼美好,由於子容器的盒子寬度已經超出了父容器的一半,致使了折行,因而,width就不能50%了,只能是50%再減去padding的像素值程序員
// W3C 標準盒模型(瀏覽器默認)
box-sizing: content-box;
// IE 怪異盒模型
box-sizing: border-box;
//避免css在不一樣瀏覽器下的表現不一樣,最好加上
*, *:before, *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
複製代碼
當子元素設置position或是float時,子元素會亂跑,給父元素設置BFC,子元素便可以依舊被父元素包裹 2. 和浮動元素產生邊界。web
通常狀況下若是沒有 BFC的話,咱們想要讓普通元素與浮動元素產生左右邊距,須要將 maring 設置爲浮動元素的寬度加上你想要產生邊距的寬度。編程
這裏的浮動元素的寬度爲 200px ,若是想和它產生 20px 的右邊距,須要將非浮動元素的 margin-left 設置爲 200px+20px 。windows
<div class="box" id="box">
<p>Lorem ipsum dolor sit.</p>
<div style="overflow: hidden;">
<p>Lorem ipsum dolor sit.</p>
</div>
<p>Lorem ipsum dolor sit.</p>
</div>
複製代碼
.one {
float: left;
width: 100px;
height: 100px;
background-color: pink;
}
.two {
height: 200px;
background-color: red;
/* 設置 BFC */
overflow: hidden;
}
<div class="one"></div>
<div class="two"></div>
複製代碼
侵佔 數組
設置BFC聖盃佈局和雙飛翼佈局解決的問題是同樣的,就是兩邊定寬,中間自適應的三欄佈局(中間欄放在文檔流前面優先渲染!)瀏覽器
聖盃佈局和雙飛翼佈局解決問題的方案在前一半是相同的: 也就是三欄所有浮動,但左右兩欄加上負margin讓其跟中間欄div並排。bash
不一樣在於解決 「中間欄div內容不被遮擋」 問題的思路不同。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://lib.sinaapp.com/js/jquery/2.0.2/jquery-2.0.2.min.js"></script>
</head>
<style>
body {
min-width: 550px; /* 2x leftContent width + rightContent width */
font-weight: bold;
font-size: 20px;
}
#header, #footer {
background: rgba(29, 27, 27, 0.726);
text-align: center;
height: 60px;
line-height: 60px;
}
#footer {
clear: both;
}
#container {
padding-left: 200px; /* leftContent width */
padding-right: 150px; /* rightContent width */
overflow: hidden;
}
#container .column {
position: relative;
float: left;
text-align: center;
height: 300px;
line-height: 300px;
}
#center {
width: 100%;
background: rgb(206, 201, 201);
}
#left {
width: 200px; /* leftContent width */
right: 200px; /* leftContent width */
margin-left: -100%;
background: rgba(95, 179, 235, 0.972);
}
#right {
width: 150px; /* rightContent width */
margin-right: -150px; /* rightContent width */
background: rgb(231, 105, 2);
}
</style>
<body>
<div id="header">#header</div>
<div id="container">
<div id="center" class="column">#center</div>
<div id="left" class="column">#left</div>
<div id="right" class="column">#right</div>
</div>
<div id="footer">#footer</div>
</body>
</html>
複製代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://lib.sinaapp.com/js/jquery/2.0.2/jquery-2.0.2.min.js"></script>
</head>
<style>
body {
min-width: 550px;
font-weight: bold;
font-size: 20px;
}
#header, #footer {
background: rgba(29, 27, 27, 0.726);
text-align: center;
height: 60px;
line-height: 60px;
}
#container {
display: flex;
}
#container .column {
text-align: center;
height: 300px;
line-height: 300px;
}
#center {
flex: 1;
background: rgb(206, 201, 201);
}
#left {
width: 200px;
background: rgba(95, 179, 235, 0.972);
}
#right {
width: 150px;
background: rgb(231, 105, 2);
}
</style>
<body>
<div id="header">#header</div>
<div id="container">
<div id="left" class="column">#left</div>
<div id="center" class="column">#center</div>
<div id="right" class="column">#right</div>
</div>
<div id="footer">#footer</div>
</body>
</html>
複製代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>實現三欄水平佈局之雙飛翼佈局</title>
<style type="text/css">
.left, .main, .right {
float: left;
min-height: 130px;
text-align: center;
}
.left {
margin-left: -100%;
background: green;
width: 200px;
}
.right {
margin-left: -300px;
background-color: red;
width: 300px;
}
.main {
background-color: blue;
width: 100%;
}
.content{
/* 關鍵點:用margin把div擠到中間正常展現*/
margin: 0 300px 0 200px;
}
</style>
</head>
<body>
<div class="container">
  <div class="main">
   <div class="content">main</div>
</div>
  <div class="left">left</div>
  <div class="right">right</div>
</div>
</body>
</html>
複製代碼
JS中一切皆對象,而每一個對象都有一個原型(Object除外),這個原型,大概就像Java中的父類,因此,基本上你能夠認爲原型就是這個對象的父對象,即每個對象(Object除外)內部都保存了它本身的父對象,這個父對象就是原型。通常建立的對象若是沒有特別指定原型,那麼它的原型就是Object(這就很相似Java中全部的類默認繼承自Object類)。
在js中,對象建立方法常見的以下
//第一種,手動建立
var a={'name':'lala'};
//第二種,構造函數
function A(){
this.name='lala';
}
var a=new A();
//第三種,class (ES6標準寫法)
class A{
constructor(){
//super();此處沒有使用extends顯式繼承,不可以使用super()
this.name='lala';
}
}
var a=new A()
//其實後面兩種方法本質上是一種寫法
複製代碼
這三種寫法建立的對象的原型(父對象)都是Object,須要提到的是,ES6經過引入class ,extends等關鍵字,以一種語法糖的形式把構造函數包裝成類的概念,更便於你們理解。是但願開發者再也不花精力去關注原型以及原型鏈,也充分說明原型的設計意圖和類是同樣的。
當對象被建立以後,查看它們的原型的方法不止一種,之前通常使用對象的proto屬性,ES6推出後,推薦用Object.getPrototypeOf()方法來獲取對象的原型
function A(){
this.name='lala';
}
var a=new A();
console.log(a.__proto__)
//輸出:Object {}
//推薦使用這種方式獲取對象的原型
console.log(Object.getPrototypeOf(a))
//輸出:Object {}
複製代碼
不管對象是如何建立的,默認原型都是Object,在這裏須要說起的比較特殊的一點就是,經過構造函數來建立對象,函數A自己也是一個對象,而A有兩個指向表示原型的屬性,分別是proto和prototype,並且兩個屬性並不相同
function A(){
this.name='lala';
}
var a=new A();
console.log(A.prototype)
//輸出:Object {}
console.log(A.__proto__)
//輸出:function () {}
console.log(Object.getPrototypeOf(A))
//輸出:function () {}
複製代碼
函數的的prototype屬性只有在看成構造函數建立的時候,把自身的prototype屬性值賦給對象的原型。而實際上,做爲函數自己,它的原型應該是function對象,而後function對象的原型纔是Object。
其實原型和類的繼承的用法是一致的:當你想用某個對象的屬性時,將當前對象的原型指向該對象,你就擁有了該對象的使用權了。
function A(){
this.name='world ';
}
function B(){
this.bb="hello"
}
var a=new A();
var b=new B();
Object.setPrototypeOf(a,b);
//將b設置爲a的原型,此處有一個問題,即a的constructor也指向了B構造函數,可能須要糾正
a.constructor=A;
console.log(a.bb)
//輸出 hello
複製代碼
若是使用ES6來作的話則簡單許多,甚至不涉及到prototype這個屬性
class B{
constructor(){
this.bb='hello'
}
}
class A extends B{
constructor(){
super()
this.name='world'
}
}
var a=new A();
console.log(a.bb+" "+a.name);
//輸出hello world
console.log(typeof(A))
//輸出 "function"
複製代碼
怎麼樣?是否是已經徹底看不到原型的影子了?活脫脫就是類繼承,可是你也看獲得實際上類A 的類型是function,因此說,本質上class在JS中是一種語法糖,JS繼承的本質依然是原型,不過,ES6引入class,extends 來掩蓋原型的概念也是一個很友好的舉動,對於長期學習那些類繼承爲基礎的面對對象編程語言的程序員而言。
這個概念其實也變得比較簡單,能夠類比類的繼承鏈條,即每一個對象的原型往上追溯,一直到Object爲止,這組成了一個鏈條,將其中的對象串聯起來,當查找當前對象的屬性時,若是沒找到,就會沿着這個鏈條去查找,一直到Object,若是還沒發現,就會報undefined。那麼也就意味着你的原型鏈不能太長,不然會出現效率問題
做用域是在運行時代碼中的某些特定部分中變量,函數和對象的可訪問性。換句話說,做用域決定了代碼區塊中變量和其餘資源的可見性。簡單來講做用域就是用來隔離變量,不一樣做用域下的同名變量不會有衝突。
任何地方都能訪問到的對象擁有全局做用域
var a = 2;
function fn() {
var b =1;
return b;
}
console.log(fn())//1
console.log(a);//2
console.log(b);//b is not defined
複製代碼
var a = 2;
function fn() {
b =1;
return b;
}
console.log(fn())//1
console.log(a);//2
console.log(b);//1
複製代碼
局部做用域通常只在固定的代碼片斷內可訪問到,最多見的例如函數內部,因此在一些地方會把這種做用域成爲函數做用域。
上文代碼一中,b是函數內部聲明並賦值,擁有局部做用域,只能帶函數fn內部使用,在fn外部使用就會報錯,這就是局部做用域的特性,外部沒法訪問。
ES5只有全局做用域和函數做用域,沒有塊級做用域,會帶來下面問題:
ES6引入了塊級做用域,明確容許在塊級做用域中聲明函數,let和const命令都涉及塊級做用域。
塊級做用域容許聲明函數只在使用大括號的狀況下成立,若是未使用大括號,會報錯。
if (true) {
function func1() {} // 不報錯
}
if (true)
function func2() {} // 報錯
複製代碼
通俗地講,當聲明一個函數時,局部做用域一級一級向上包起來,就是做用域鏈。
1.當執行函數時,老是先從函數內部找尋局部變量
2.若是內部找不到(函數的局部做用域沒有),則會向建立函數的做用域(聲明函數的做用域)尋找,依次向上
var a =1;
function fn() {
var a = 10;
function fn1() {
var a =20;
console.log(a);//20
}
function fn2 () {
console.log(a);//10
}
fn1();
fn2();
}
fn();
console.log(a)//1
複製代碼
當執行fn1時,建立函數fn1的執行環境,並將該對象置於鏈表開頭,而後將函數fn的調用對象放在第二位,最後是全局對象,做用域鏈的鏈表的結構是fn1->fn->window。從鏈表的開頭尋找變量a,即fn1函數內部找變量a,找到了,結果是20。
一樣,執行fn2時,做用域鏈的鏈表的結構是fn2->fn->window。從鏈表的開頭尋找變量a,即fn2函數內部找變量a,找不到,因而從fn內部找變量a,找到了,結果是10。
最後在最外層打印出變量a,直接從變量a的做用域即全局做用域內尋找,結果爲1。
提到做用域就不得不提到閉包,簡單來說,閉包外部函數可以讀取內部函數的變量。
優勢:閉包能夠造成獨立的空間,永久的保存局部變量。
缺點:保存中間值的狀態缺點是容易形成內存泄漏,由於閉包中的局部變量永遠不會被回收
function fn1() {
var n = 999;
nAdd = function () {
n += 1
}
function fn2() {
console.log(n)
}
return fn2
}
var result = fn1();
result();//999
nAdd();//執行n +=1;
result();//1000
複製代碼
在 ES5 中,其實 this 的指向,始終堅持一個原理:this 永遠指向最後調用它的那個對象
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a();
console.log("outer:" + this) // outer: Window
複製代碼
調用a的時候,前面沒有調用對象那麼即爲全局對象window。這裏咱們沒有使用嚴格模式,若是使用嚴格模式的話,全局對象就是 undefined,那麼就會報錯 Uncaught TypeError: Cannot read property 'name' of undefined。
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // windowsName
}
}
var f = a.fn;
f();
複製代碼
這裏你可能會有疑問,爲何不是 Cherry,這是由於雖然將 a 對象的 fn 方法賦值給變量 f 了,可是沒有調用,再接着跟我念這一句話:「this 永遠指向最後調用它的那個對象」,因爲剛剛的 f 並無調用,因此 fn() 最後仍然是被 window 調用的。因此 this 指向的也就是 window。 出處。
目前主要有如下幾種方法
箭頭函數的 this 始終指向函數定義時的 this,而非執行時。箭頭函數中沒有 this 綁定,必須經過查找做用域鏈來決定其值,若是箭頭函數被非箭頭函數包含,則 this 綁定的是最近一層非箭頭函數的 this,不然,this 爲 undefined」。
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( () => {
this.func1()
},100);
}
};
a.func2() // Cherry
複製代碼
若是不使用 ES6,那麼這種方式應該是最簡單的不會出錯的方式了,咱們是先將調用這個函數的對象保存在變量 this_ 中,而後在函數中都使用這個 this_,這樣 this_ 就不會改變了
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var this_ = this;
setTimeout( function() {
this_.func1()
},100);
}
};
a.func2() // Cherry
複製代碼
//apply
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.apply(a),100);
}
};
a.func2() // Cherry
//call
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.call(a),100);
}
};
a.func2() // Cherry
//bind
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.bind(a)(),100);
}
};
a.func2() // Cherry
複製代碼
apply定義
apply() 方法調用一個函數, 其具備一個指定的this值,以及做爲一個數組(或相似數組的對象)提供的參數
複製代碼
語法
fun.apply(thisArg, [argsArray])
複製代碼
二者基本相似,區別在於傳入的參數不一樣 call語法
fun.call(thisArg[, arg1[, arg2[, ...]]])
複製代碼
apply 和 call 的區別是 call 方法接受的是若干個參數列表,而 apply 接收的是一個包含多個參數的數組。
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.apply(a,[1,2]) // 3
複製代碼
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.call(a,1,2) // 3
複製代碼
bind()方法建立一個新的函數,當被調用時,將其this關鍵字設置爲提供的值,在調用新函數時,在任何提供以前提供一個給定的參數序列。
因此bind是建立一個新的函數,必須手動調用
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)() // 3
複製代碼
Function.prototype._call = function (context = window) {
var context = context;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
// 測試一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar._call(null); // 2
console.log(bar._call(obj, 'kevin', 18));
// 1
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }
複製代碼
Function.prototype.apply = function (context = window, arr) {
var context = context;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
複製代碼
Function.prototype._bind = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
複製代碼
// 經過字面量方式實現的函數each
var each = function(object, callback){
var type = (function(){
switch (object.constructor){
case Object:
return 'Object';
break;
case Array:
return 'Array';
break;
case NodeList:
return 'NodeList';
break;
default:
return 'null';
break;
}
})();
// 爲數組或類數組時, 返回: index, value
if(type === 'Array' || type === 'NodeList'){
// 因爲存在類數組NodeList, 因此不能直接調用every方法
[].every.call(object, function(v, i){
return callback.call(v, i, v) === false ? false : true;
});
}
// 爲對象格式時,返回:key, value
else if(type === 'Object'){
for(var i in object){
if(callback.call(object[i], i, object[i]) === false){
break;
}
}
}
}
複製代碼
參考文獻
做者:拉丁吳 連接:juejin.im/post/57f336… 來源:掘金
做者:sunshine小小倩 連接:juejin.im/post/59bfe8… 來源:掘金
做者:Chris_Ping 連接:juejin.im/post/5d1f1c… 來源:掘金