JavaScript
作用域和作用域
執(zhí)行上下文
- 范圍: 一段
<script>
或一個函數(shù) - 全局: 變量定義,函數(shù)聲明 一段
<script>
- 函數(shù): 變量定義,函數(shù)聲明,
this
,arguments
函數(shù)
作用域
js
在ES6
前無塊級作用域
// Hoisting
if(true){
var name = 'wangcai'
}
函數(shù)(私有)作用域和全局作用域
var a=100;
function fn(){
var a = 200;
console.log(fn,a);
}
console.log('global',a);
fn();
作用域鏈
// 通俗地講蔬芥,當聲明一個函數(shù)時,局部作用域一級一級向上包起來签餐,就是作用域鏈。
var a= 100;
function f1(){
var b = 200;
function f2(){
var c = 300;
console.log(a);
console.log(b);
console.log(c);
}
}
私有作用域執(zhí)行順序
正常來講程序自上往下執(zhí)行
-
形參賦值
- 全局環(huán)境不能直接訪問局部環(huán)境
- 如果
function
(私有作用)沒有形參賦值, 私有作用域中變量無聲明修飾符,該變量就上級作用域找賦值;如果有形參賦值,私有作用域不會變量提升.
-
變量提升
- 變量提升: 只是提升變量的聲明做祝,并不會把賦值也提升上來型诚。
- ?
var a
;在 全局作用域下聲明的變量,也是相當于給window
設(shè)置了一個對象屬性,而且兩者之間建立了映射的機制 <==>window.a=undefined
; - 方法變量提升是會把函數(shù)體提上上來
-
let
,const
聲明不存再變量提升,如果let
,const
同一作用域存在重復(fù)聲明,瀏覽器有自我檢測機制,代碼(一開始)直接報錯,不存在解釋執(zhí)行
fn()
function fn(){console.log(1)}
fn()
function fn(){console.log(2)}
fn()
var fn = 10
fn() //報錯, 下面就不會再執(zhí)行
function fn(){console.log(3)}
fn()
function fn(){console.log(4)}
fn()
//最終輸出: 4 4 4 fn is not function
//exp2
let a=10,b=10;
function fn(){
// console.log(a,b); //會報錯, a is not defined
let a = b =20; // 這樣寫語法 === let a =20; b = 20;
console.log(a,b);
}
fn()
console.log(a,b)
//輸出:10 10
// 10 20
// exp3
var a = 12,b= 13,c = 14;
function fu(a){
/****
* 1.形參賦值
* a = 12
* 2.變量提升
* var b;
* => 在私有作用域中, 只有下面兩種情況是私有變量
* A:聲明過的變量 (帶var/function)
* B:形參也是私有變量
* 剩下的都不是自己的私有變量,都需要基于作用域鏈的機制向上查找
*/
console.log(a,b,c);
var b = c = a = 20;
console.log(a,b,c);
}
fn(a) // 執(zhí)行就是把全局a的**值**12 當實參傳遞給函數(shù)的形參 ==>fn(12),
// 注意:如果傳的是引用類型,傳遞的值是棧內(nèi)存地址
console.log(a,b,c);
//exp 4
var ary = [12,13];
function fn(ary){
console.log(ary); // =>[12,13]
// 雖然這里的ary 是私有的, 但其指針還是與公有的ary是同一個對內(nèi)存數(shù)據(jù)
ary[0]=100;
ary = [100]; // 這里是重新賦值了,指針改變
ary[0] = 0;
console.log(ary) //=>[0]c
}
fn(ary); // 引用類型,傳遞的值是棧內(nèi)存地址
console.log(ary); // =>[100,13]
in
:檢測某一個屬性是否隸屬的于這個對象(不管是私有屬性還是共有屬性婚瓜,只要有這個屬性結(jié)果就是true
)
hasOwnProperty
:檢測某一個屬性是否隸屬的于這個對象(只有這個屬性是私有的才可以)
var a;
console.log("a" in window);
var v='Hello World';
alert(v); //Hello World
var v='Hello World';
(function(){
alert(v); //hello world
})()
var v='Hello World';
(function(){
alert(v); //undefined
var v='I love you';
})()
等同
var v='Hello World';
(function(){
var v; // 變量提升
alert(v); //undefined
v='I love you';
})()
// 當解析器讀到 if 語句的時候宝鼓,它發(fā)現(xiàn)此處有一個變量聲明和賦值,
// 于是解析器會將其聲明提升至當前作用域的頂部(這是默認行為巴刻,并且無法更改)愚铡,
// 這個行為就叫做 Hoisting。
var a = 1;
function foo() {
if (!a) {
var a = 2;
}
alert(a);
};
foo();
// 等同
var a = 1;
function foo() {
var a;
if (!a) {
a = 2;
}
alert(a);
};
foo();
(function(){
var a='One';
var b='Two';
var c='Three';
})()
// 等同
(function(){
var a,b,c;
a='One';
b='Two';
c='Three';
})()
函數(shù)提升:
function
是可以在被執(zhí)行的位置之后定義胡陪,JS實際運行時會首先將它提升到頂部
函數(shù)提升是把整個函數(shù)都提到前面去沥寥。只有函數(shù)才會創(chuàng)建新的作用域(函數(shù)聲明才會被提升,表達式聲明不會被提升)
// 在我們寫js code 的時候柠座,我們有2中寫法邑雅,一種是函數(shù)表達式,另外一種是函數(shù)聲明方式妈经。
// 我們需要重點注意的是淮野,只有函數(shù)聲明形式才能被提升。
// 函數(shù)聲明方式提升【成功】
// 代碼如下:
function myTest(){
foo();
function foo(){
alert("我來自 foo");
}
}
myTest();
// 函數(shù)表達式方式提升【失敗】
// 代碼如下:
function myTest(){
foo();
var foo =function foo(){
alert("我來自 foo");
}
}
myTest(); //foo is not a function
?
當前函數(shù)執(zhí)行,形成一個私有作用域A,A的上級作用域是誰,和他再那執(zhí)行沒有關(guān)系, 和他在哪創(chuàng)建(定義)的有關(guān)系, 在哪創(chuàng)建的,他的上級作用域就是誰!
var a = 1;
function fn(){
console.log(a)
var a = 5
console.log(a)
a++
var a
fn3()
fn2()
console.log(a)
function fn2(){
console.log(a)
a = 20
}
}
function fn3(){
console.log(a)
a = 200
}
fn()
console.log(a)
//輸出
undefined
5
1
6
20
200
循環(huán)事件綁定
嚴格模式
arguments
是一個對應(yīng)于傳遞給函數(shù)的參數(shù)(實參)的類數(shù)組對象吹泡,映射關(guān)系(賦值瞬間關(guān)聯(lián)),能建立映射的就建立映射,不能建立的,后面不管怎么操作都無法關(guān)聯(lián);
?在嚴格模式下不存在映射
"use strict"
function fn(x,y,z){
var arg = arguments;
console.log(x); // ==> 10
arg[1] = 30;
// 這里再賦值是沒有用的
// 雖然argments:
// 0:10
// 1:30
// length:1 ?沒錯還是1
// ....
// 但與形參不會再映射
console.log(arg);
console.log(y); // ==> undefined
y=40;
console.log(arg[1]);// 還是==30;
}
fn(10);
/**
* argments:
* 0:10
* length:1
~function(){
"use strict"
function fn(x){
arguments[0]=100;
console.log(x); // => 10 不存在映射
}
fn(10);
}();
* callee:指向當前執(zhí)行的函數(shù)
* ...
**/
"use strict"
function getInfo(name, age, sex){
console.log('name:',name);
console.log('age:', age);
console.log('sex:', sex);
console.log(arguments);
arguments[0] = 'valley';
console.log('name', name);
}
getInfo('Tim', 2, '男');
//name: Tim
//age: 2
//sex: 男
//{
// 0: Tim,
// 1: 2,
// 2: 男,
// length: 3,
// callee: f(),
//}
//name: Tim ==>非嚴格模式下是 valley
第一骤星,嚴格模式下無法再意外創(chuàng)建全局變量
邏輯計算
// 1. 邏輯或賦值 false 取 或后面值
var a = false || 1; // =>1
// 2. 邏輯與相反
var b = false && 2; // =>false
// 邏輯與一般回調(diào)函數(shù)中用到
function fn(callback){
// 如果傳遞的值是個函數(shù), 我們才讓其執(zhí)行
// if(typeof callback === "function"){
// callback;
// }
// 簡寫版(不嚴謹,但用的多)
// 默認callback要不然就傳函數(shù),要不然就不穿
callback && callback();
}
fn(function(){
// xxx
})
3.邏輯與和邏輯或的混合模式
// 優(yōu)先級: 邏輯與的優(yōu)先級高于邏輯或
方法執(zhí)行運算
注意數(shù)組的參數(shù)引用
1.非嚴格模式下自行函數(shù)如果不是.出來的 就是屬于window
var b = 2;
var c = 5 + (b++); // c=7; b=2; 這里注意了b++加不加括號都是順序計算 這里括號沒意義
類:具備某些共同特征的實體的集合
實例:某一個類別具體的一個事務(wù)
js有5中簡單數(shù)據(jù)類型(也稱為基本數(shù)據(jù)類型): Undefined
、Null
荞胡、Boolean
妈踊、Number
和String
。還有1中復(fù)雜的數(shù)據(jù)類型————Object
泪漂,Object
本質(zhì)上是由一組無序的名值對組成的廊营。
其中Undefined
、Null
萝勤、Boolean
露筒、Number
都屬于基本類型。Object
敌卓、Array
和Function
則屬于引用類型慎式,String
有些特殊:
因為字符串具有可變的大小,所以顯然它不能被直接存儲在具有固定大小的變量中趟径。由于效率的原因瘪吏,我們希望JS只復(fù)制對字符串的引用,而不是字符串的內(nèi)容蜗巧。但是另一方面掌眠,字符串在許多方面都和基本類型的表現(xiàn)相似,而字符串是不可變的這一事實(即沒法改變一個字符串值的內(nèi)容)幕屹,因此可以將字符串看成行為與基本類型相似的不可變引用類型
var x = 1;
var y = x;
x.name = 'hanna'; // 基本類型無法添加屬性
console.log(y); //1
console.log(x.name); //undefined
typeof
會根據(jù)對象類型返回對應(yīng)的類型字符串, 但是有幾個缺點:
對于數(shù)組蓝丙、函數(shù)级遭、對象來說,其關(guān)系錯綜復(fù)雜渺尘,使用 typeof
都會統(tǒng)一返回 “object
” 字符串,
null
→object
NaN
→number
undefined
→ undefined
那么此時我們有第二個方法可以使用, 是 ES3
中的 Object.prototype.toString
方法,我們可以用Object.prototype.toString.call(obj)
檢測對象類型:
何時使用 ==
和 ===
if(obj.a==null){
// 這里相當于 obj.a == null || obj.a == undefined , 簡寫形式
// 這是Jquery 源碼中推薦寫法, 其他都用 ===
// 注意 == 不能用于 未定變量判斷
if(xxx==null){} //報錯 xxx is not defined
if
判斷轉(zhuǎn)換只有 0挫鸽、NaN
、空字符串鸥跟、null
丢郊、undefined
會轉(zhuǎn)換為false
,其他都為true
console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(Math));//[object Math]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
function Person(){};
console.log(Object.prototype.toString.call(new Person));//[object Object]
為什么得用Object.prototype.toString.call
,因為toString
為Object
的原型方法,而Array
医咨、Function
等類型作為Object
的實例蚂夕,都重寫了toString
方法,不同的對象類型調(diào)用toString
方法時,根據(jù)原型鏈的知識腋逆,調(diào)用的是對應(yīng)的重寫之后的toString
方法(Function
類型返回內(nèi)容為函數(shù)體的字符串,Array
類型返回元素組成的字符串.....)侈贷,而不會去調(diào)用Object
上原型toString
方法(返回對象的具體類型)惩歉,所以采用obj.toString()
不能得到其對象類型,只能將obj
轉(zhuǎn)換為字符串類型俏蛮;因此撑蚌,在想要得到對象的具體類型時,應(yīng)該調(diào)用Object
上原型toString
方法搏屑。
原型鏈
prototype
: 顯示原型
__proto__
: 隱式原型
function Fn(){
this.x = 100;
this.y = 200;
var z = 300;
this.getX = function(){
console.log(this.x);
}
}
Fn.prototype.y = 500;
Fn.prototype.getX = function(){
console.log(this.x);
}
Fn.prototype.getY = function(){
console.log(this.y);
}
var f1 = new Fn; // 當沒傳參時,寫不寫()都行
var f2 = new Fn();
console.log(f1.x) //undefined 只有函數(shù)類的this. 屬性或方法 實例才能繼承
console.log(f1.getX === f2.getX) // false 兩個不同的實例的 **私有** 方法
console.log(f1.getY === f2.getY) // true 如果私有屬性沒有 就往上級查找
console.log(f1.__proto__.getY === Fn.prototype.getY) // true
console.log(f1.__proto__.getX === F2.getX) // false
console.log(f1.constructor) // FN; f1是實例沒有constructor,往上查找 prototype.constructor ==> Fn;
console.log(Fn.prototype.__proto__.constructor); // Fn.prototype.__proto__ === Object.prototype;
f1.getX();// 100 this:f1 ==>console.log(f1.x)
f1.__proto__.getX() // undefined f1.__proto__ === Fn.prototype
f2.getY();// 200 this:f2 ==>console.log(f2.y)
Fn.prototype.getY() // 500
Fn.prototype.getX() // undefined
原型重定向
EXP1
function fun(){
this.a = 0;
this.b = function(){
alert(this.a);
}
}
fun.prototype = {
b: function(){
this.a = 20;
alert(this.a);
},
c: function(){
this.a = 30;
alert(this.a);
}
}
var my_fun = new fun();
my_fun.b(); // 0
my_fun.c(); // this => my_fun.a = 30 ; 30
// 結(jié)果:0 30
my_fun.a
用來設(shè)置私有屬性
my_fun.__proto__.a
用來設(shè)置公有屬性
為什么要重定向原型
項目開發(fā)中方便批量擴展屬性與方法的時候.
原型重定向?qū)е碌膯栴}
- 自己開辟的堆內(nèi)存中沒有
constructor
屬性争涌,導(dǎo)致類的原型構(gòu)造函數(shù)缺失(解決:自己手動在堆內(nèi)存中增加constructor
屬性) - 當原型重定向后,瀏覽器默認開辟的那個類原型堆內(nèi)存會被釋放掉辣恋,如果之前已經(jīng)存儲了一些方法或?qū)傩粤恋妫紩G失(所以:內(nèi)置累的原型不允許重定向到自己開辟的堆內(nèi)存,因為內(nèi)置類的原型上存在很多屬性方法伟骨,重定向后都沒了饮潦,這樣是不被允許的;但瀏覽器對內(nèi)置類有保護機制)
私有屬性
: 自己對內(nèi)存中存儲的屬性,相對自己來說是私有
公有屬性
: 自己基于proto找到的屬性,相對自己來說是公有的
關(guān)于this
的兩道題
this
指向判斷 MDN
的解釋:當前執(zhí)行代碼的環(huán)境對象
- 元素綁定事件携狭,方法中的
this
是當前操作的元素 - 方法名簽是否有點继蜡,有點,點前面是誰
this
就是誰逛腿,沒有this
是window
(嚴格模式下是undefined
) - 構(gòu)造函數(shù)執(zhí)行稀并,方法中的
this
是當前類的一個實例
全局環(huán)境,
this
就代表Window
對象单默。
var name = 'zhar';
function say(){
console.log(this.name);//zhar
}
say();
對象環(huán)境,
this
指向?qū)ο蟊旧怼?/p>
var obj = {
name : "zhar",
say : function(){
console.log(this.name);//zhar
}
}
obj.say();
構(gòu)造函數(shù)環(huán)境,
this
會指向創(chuàng)建出來的實例對象
function Person() {
this.name = 'zhar';
}
var p = new Person();
console.log(p.name);
事件對象,在
DOM
事件中使用this
碘举,this
指向了觸發(fā)事件的DOM
元素本身
li.onclick = function(){
console.log(this.innerHTML);
}
EXP1
var fullName = 'language';
var obj = {
fullName: 'javascrtpt',
prop: {
getFullName: function(){
return this.fullName;
}
}
};
console.log(obj.prop.getFullName()); // this => obj.prop; obj.prop沒有fullName屬性; undefined
var test = obj.prop.getFullName;
console.log(test()); // this => window.fullName(非嚴格模式); language
EXP2
var name = 'window';
var Tom = {
name: 'Tom',
show: function(){
console.log(this.name); // 4. 'window'
},
wait: function(){
var fun = this.show; // 2. => this: Tom
fun(); // 3. => this: window
}
};
Tom.wait(); // 1. => this: Tom
當this
碰到return
時
function fn()
{
this.user = '小J';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = '小J';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = '小J';
return 1;
}
var a = new fn;
console.log(a.user); //小J
function fn()
{
this.user = '小J';
return undefined;
}
var a = new fn;
console.log(a.user); //小J
//如果返回值是一個對象,那么this指向的就是那個返回的對象雕凹,如果返回值不是一個對象那么this還是指向函數(shù)的實例殴俱。
function fn()
{
this.user = '小J';
return undefined;
}
var a = new fn;
console.log(a); //fn {user: "小J"}
//還有一點就是雖然null也是對象政冻,但是在這里this還是指向那個函數(shù)的實例,因為null比較特殊线欲。
function fn()
{
this.user = '小J';
return null;
}
var a = new fn;
console.log(a.user); //小J
call apply
是什么? Function
原型上的方法
作用? 改變this
的指向
exp
// 數(shù)組拼接
var a1 = [1,2,3]; var a2 = [4,6,5];
[].push.apply(a1,a2);
// 類型版判
Object.prototype.toString.apply(new Date);
// 數(shù)組最大小值
Math.max.apply(null,a1);
// 位數(shù)組的轉(zhuǎn)換
var a ={0:'xxx',1:'xxxx',2:'xx',length:3};
var b = [].slice.call(a);
b.forEach(i=>console.log(i));
// 構(gòu)造繼承
function A(){}
function B(){
A.apply(this,arguments)
}
// 簡便分割字符串
// 原: 分割成數(shù)組再轉(zhuǎn)換
console.log('abc'.split(',').join(','))
=> // ? 當使用apply 或 call 傳入的第一個值傳入的簡單類型時,
// 會自動轉(zhuǎn)換包裝對象
console.log([].join.call('abc',','))
[].forEach.call('abc',i=>{
console.log(i)
})
// 一般框架 嚴格模式下兼容問題
(function(){
'use strict'
}).call(this)
npm
nrm
是專門用來管理和快速切換私人配置的registry
建議全局安裝
npm install nrm -g --save
nrm
有一些默認配置明场,用nrm ls
命令查看默認配置,帶*
號即為當前使用的配置
nrm ls
也可以直接輸入以下命令查看當前使用的是哪個源
nrm current
切到源http://r.cnpmjs.org/
李丰,命令:nrm use
源的別名苦锨,即
nrm use cnpm
模塊化
優(yōu)點
- 避免命名沖突(減少全局命名空間污染);
- 更好的分離,按需加載;
- 代碼復(fù)用;
- 高可維護維護.
全局函數(shù)模式:將不同的功能封裝成不同的全局函數(shù)(最原始的寫法)
缺點:命名沖突,全局污染,其他js可隨意訪問修改變量
let msg = "module1"
function foo(){
console.log('foo()',msg)
}
function bar(){
console.log('foo()',msg)
}
namspace模式: 簡單對象封裝
缺點:其他js還是可隨意訪問修改變量
let obj = {
msg:'module2',
foo:function(){
console.log("xxxx")
}
}
IIFE模式:匿名函數(shù)自調(diào)用(閉包) 立即執(zhí)行函數(shù)
// jquer寫法
(function(win){
let msg = 'xxasdf';
function fn(){
console.log('fn()',msg);
}
win.module3 = {fn};
})(window)
IIFE模式增強:引用依賴: 現(xiàn)代模塊實現(xiàn)的基石
問題:
- 請求過多
- 依賴模糊
- 難以維護
//
(function(win,$){
let msg = "module4";
function foo(){
console.log('foo()',msg);
}
win.module4 = foo;
&('body').css('backgruond','red');
})(window,jQuery);
CommonJS Nodejs 使用
每個文件可當做一個模塊
前端打包工具出現(xiàn),CommonJS前端也可以使用
AMD requsit
AMD
是 RequireJS
在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。
CMD
是 SeaJS
在推廣過程中對模塊定義的規(guī)范化產(chǎn)出趴泌。
類似的還有 CommonJS Modules/2.0
規(guī)范舟舒,是 BravoJS
在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。
還有不少??
這些規(guī)范的目的都是為了 JavaScript
的模塊化開發(fā)嗜憔,特別是在瀏覽器端的秃励。
目前這些規(guī)范的實現(xiàn)都能達成瀏覽器端模塊化開發(fā)的目的。
區(qū)別:
對于依賴的模塊吉捶,
AMD
是提前執(zhí)行夺鲜,CMD
是延遲執(zhí)行。不過RequireJS
從 2.0 開始呐舔,也改成可以延遲執(zhí)行(根據(jù)寫法不同币励,處理方式不同)。CMD
推崇 as lazy as possible.-
CMD
推崇依賴就近珊拼,AMD
推崇依賴前置食呻。看代碼:// CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // 此處略去 100 行 var b = require('./b') // 依賴可以就近書寫 b.doSomething() // ... }) // AMD 默認推薦的是 define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 a.doSomething() // 此處略去 100 行 b.doSomething() ... })
雖然AMD
也支持 CMD
的寫法澎现,同時還支持將 require
作為依賴項傳遞仅胞,但 RequireJS
的作者默認是最喜歡上面的寫法,也是官方文檔里默認的模塊定義寫法剑辫。
-
AMD
的API
默認是一個當多個用饼问,CMD
的API
嚴格區(qū)分,推崇職責(zé)單一揭斧。比如AMD
里莱革,require
分全局require
和局部require
,都叫require
讹开。CMD
里盅视,沒有全局require
,而是根據(jù)模塊系統(tǒng)的完備性旦万,提供seajs.use
來實現(xiàn)模塊系統(tǒng)的加載啟動闹击。CMD
里,每個API
都簡單純粹成艘。
ES6 出現(xiàn),想同一現(xiàn)在所有模塊化標準
模塊化只是代碼的寫法問題, 沒必要弄這么多標準, 標準是越統(tǒng)一越好,越簡單越好.
語法:import
export
(注意有無default
)
環(huán)境:babel
編譯ES6
語法,
模塊化工具: Webpack
(功能強大)始于2012年赏半,rollup
(功能專一,打包文件比較小,Vue
,React
)
Webpack
始于2012年贺归,由 Tobias Koppers發(fā)起,用于解決當時現(xiàn)有工具未解決的的一個難題:構(gòu)建復(fù)雜的單頁應(yīng)用程序(SPA
)断箫。特別是 Webpack
的兩個特性改變了一切:
- 代碼拆分(
Code Splitting
) 使你可以將應(yīng)用程序分解成可管理的代碼塊拂酣,可以按需加載,這意味著你的用戶可以快速獲取交互性的網(wǎng)站仲义,而不必等到整個應(yīng)用程序下載和解析完成婶熬。當然你可以手動來完成這項工作,那么祝你好運埃撵。 - 靜態(tài)資源(
Static assets
) 如圖像和CSS
可以導(dǎo)入到你的應(yīng)用程序中赵颅,而且還能夠被作為依賴圖中的另一個節(jié)點。再也不用關(guān)心你的文件是否放在正確的文件夾中暂刘,再也不用為文件URL
增添hash
而使用hack
腳本饺谬,因為Webpack
會幫我們處理這些事情。
Rollup
則是由于不同的原因被創(chuàng)建的:利用 ES2015
巧妙的模塊設(shè)計谣拣,盡可能高效地構(gòu)建出能夠直接被其它 JavaScript
庫引用的模塊商蕴。其他的模塊打包工具 – 包含 Webpack
– 通過都是將每個模塊封裝在一個函數(shù)中,將它們放在一個包中芝发,通過瀏覽器友好的 require
實現(xiàn)贿肩,最后逐一執(zhí)行這些模塊堪置。如果您需要按需加載妓蛮,webpack 這類的打包工具非常合適仁热。否則有點浪費窝撵,如果你有很多模塊潮梯,它會變得更糟耸袜。
ES2015
模塊啟用了一種不同的方法混驰,這是真是 Rollup
使用的锣尉。所有的代碼都放在同一個地方刻炒,然后一次性執(zhí)行,從而生成更簡潔自沧、更簡單的代碼坟奥,從而啟動更快。您可以 自己使用 Rollup REPL
來查看它 拇厢。
但是有一個權(quán)衡:代碼拆分(Code Splitting
)是一個更加棘手的問題爱谁,在撰寫本文時,Rollup
還不支持孝偎。同樣的访敌,Rollup
也不支持模塊的熱更新HMR。而對于使用 Rollup
的人來說衣盾,最大的痛點可能是 – 它能處理大多數(shù) CommonJS
文件(通過 插件 )寺旺,然而有些東西根本不能轉(zhuǎn)譯為 ES2015
爷抓,而 Webpack
能處理所有你丟給它的事情。
抉擇: 對于應(yīng)用使用 Webpack
阻塑,對于類庫使用 Rollup
Webpack
HMR 熱更新實現(xiàn)流程
-
Webpack
編譯期蓝撇,為需要熱更新的entry
注入熱更新代碼(EventSource
通信) → 最新的webpack-dev-server
,改成用WebSocket
協(xié)議了叮姑,基于sockjs - 頁面首次打開后唉地,服務(wù)端與客戶端通過
EventSource
建立通信渠道,把下一次的hash
返回前端 - 客戶端獲取到
hash
传透,這個hash
將作為下一次請求服務(wù)端hot-update.js
和hot-update.json
的hash
- 修改頁面代碼后耘沼,
Webpack
監(jiān)聽到文件修改后,開始編譯朱盐,編譯完成后群嗤,發(fā)送build
消息給客戶端 - 客戶端獲取到
hash
,成功后客戶端構(gòu)造hot-update.js
script
鏈接兵琳,然后插入主文檔 -
hot-update.js
插入成功后狂秘,執(zhí)行hotAPI
的createRecord
和reload
方法,獲取到Vue
組件的render
方法躯肌,重新render
組件者春, 繼而實現(xiàn) UI 無刷新更新。
虛擬MOD
MVVM
- Model-模型清女、數(shù)據(jù)
- View - 視圖钱烟、模板(視圖和模型是分離的)
- ViewModel - 連接Model和View(連接器,橋的作用),View可以通過事件綁定印象到Model,Model可以通過數(shù)據(jù)綁定影響到View
MVVM是MVC的一種創(chuàng)新,MVC主要用于后端
組件化 模塊化
組件化 | 模塊化 |
---|---|
就是"基礎(chǔ)庫"或者“基礎(chǔ)組件",意思是把代碼重復(fù)的部分提煉出一個個組件供給功能使用嫡丙。 | 就是"業(yè)務(wù)框架"或者“業(yè)務(wù)模塊"拴袭,也可以理解為“框架”,意思是把功能進行劃分曙博,將同一類型的代碼整合在一起拥刻,所以模塊的功能相對復(fù)雜,但都同屬于一個業(yè)務(wù)父泳。 |
使用:Dialog般哼,各種自定義的UI控件、能在項目或者不同項目重復(fù)應(yīng)用的代碼等等惠窄。 | 使用:按照項目功能需求劃分成不同類型的業(yè)務(wù)框架(例如:注冊逝她、登錄、外賣睬捶、直播.....) |
目的:復(fù)用黔宛,解耦。 | 目的:隔離/封裝 (高內(nèi)聚)。 |
依賴:組件之間低依賴臀晃,比較獨立觉渴。 | 依賴:模塊之間有依賴的關(guān)系,可通過路由器進行模塊之間的耦合問題徽惋。 |
架構(gòu)定位:縱向分層(位于架構(gòu)底層案淋,被其他層所依賴) | 架構(gòu)定位:橫向分塊(位于架構(gòu)業(yè)務(wù)框架層)。 |
總結(jié)
- 組件相當于庫险绘,把一些能在項目里或者不同類型項目中可復(fù)用的代碼進行工具性的封裝踢京。
- 而模塊相應(yīng)于業(yè)務(wù)邏輯模塊,把同一類型項目里的功能邏輯進行進行需求性的封裝宦棺。
vue
Vue與jQuery區(qū)別
vue 數(shù)據(jù)與視圖分離, 以數(shù)據(jù)驅(qū)動視圖,只關(guān)心數(shù)據(jù)變化,DOM操作備份裝, jQuery 直接在代碼操作DOM.
VUE 三要素
-
響應(yīng)式:
vue
如何監(jiān)聽到Data
的每一個屬性變化? →Object.defineProperty
var obj = {}; var _name='張三'; Object.defineProperty(obj,"name",{ get:function(){ console.log('get',_name); // 監(jiān)聽 return _name; }, set:function(newVal){ console.log('set',_name); // 監(jiān)聽 _name = newVal; } }); //Vue 監(jiān)聽模擬 var vm = {} var data = { name: 'zhangsan', age: 20 } var key, value for (key in data) { (function (key) { Object.defineProperty(vm, key, { get: function () { console.log('get', data[key]) // 監(jiān)聽 return data[key] }, set: function (newVal) { console.log('set', newVal) // 監(jiān)聽 data[key] = newVal } }) })(key) }
-
模板引擎:
vue
的模板是如何解析,指令如何處理,模板是什么?render
函數(shù)執(zhí)行解析模板vnode
- 本質(zhì):字符串
- 有邏輯,如
v-if
v-for
- 與
HTML
格式很像,但有很大區(qū)別 - 最終還是要轉(zhuǎn)換為
HTML
來顯示
-
渲染:
vue
的模板如何渲染成HTML
? 以及渲染過程-
render
函數(shù)執(zhí)行vnode
-
UpdateComponent
→patch
-
Vue 實現(xiàn)流程
第一步 解析Vue
模板字符串成render
函數(shù), 模板中用到的data
中的屬性瓣距,都變成了JS
變量模板中的 v-model
v-for
v-on
都變成了JS
邏輯,render
函數(shù)執(zhí)行后返回vnode
第二步 響應(yīng)式開始監(jiān)聽, 用Object.defineProperty
修改變量的get
/set
方法,實現(xiàn)響應(yīng)式代咸,開始監(jiān)聽蹈丸,并將 data
的屬性/methods
的方法等代理到 vm
上(從data.list
代理到vm.list
上,這樣render
函數(shù)才能用到那個變量with(this){...list}// vm.list
)
第三步:首次渲染呐芥,執(zhí)行render
函數(shù)逻杖,獲得vnode
,patch
轉(zhuǎn)化成真實DOM
思瘟,顯示頁面荸百,且通過get
收集訂閱者,綁定依賴(執(zhí)行 render
函數(shù)滨攻,會訪問到 vm.list
vm.title
够话,會被響應(yīng)式的 get
方法監(jiān)聽到,只有被get
監(jiān)聽到的data
才會在set
的時候響應(yīng)式更新,data
中有很多屬性铡买,有些被用到,有些可能不被用到,避免不必要的重復(fù)渲染)
第四步:data
屬性變化霎箍,被響應(yīng)式set
監(jiān)聽到(上一步的data
奇钞,不被get
監(jiān)聽到的,這一步不會被set
監(jiān)聽到漂坏,即怎么修改都不會變化)景埃,觸發(fā)updateComponent
,重新執(zhí)行render
函數(shù)顶别,生成新的vnode
谷徙,與舊vnode
執(zhí)行diff
算法對比,重新patch
更新到html
頁面中
簡化
- 解析模版驯绎,通過render函數(shù)生成vnode格式的JS
- 響應(yīng)式監(jiān)聽數(shù)據(jù)變化(getter,setter)
- 渲染頁面,綁定依賴
- data數(shù)據(jù)變化,觸發(fā)re-render更新視圖
React vs Vue
本質(zhì)區(qū)別
-
Vue
- 本質(zhì)是MVVM
框架完慧,由MVC
發(fā)展而來 -
React
- 本質(zhì)是前端組件化框架,由后端組件化發(fā)展而來
但這并不妨礙他們兩者都能實現(xiàn)相同的功能
模板區(qū)別
-
Vue
- 使用模板(最初由angular
提出) -
React
- 使用JSX
(模板與js
混在一起,未分離) - 模板語法上剩失,我更加傾向于
JSX
- 模板分離上屈尼,我更加傾向于
Vue
組件化的區(qū)別
-
React
本身就是組件化册着,沒有組件化就不是React
-
Vue
也支持組件化,不過是在MVVM
上的擴展 - 查閱
Vue
組件化的文檔脾歧,洋洋灑灑很多(側(cè)面反映) - 對于組件化甲捏,我更加傾向于
React
,做的徹底而清晰
兩者共同點
- 都支持組件化
- 都是數(shù)據(jù)驅(qū)動試圖
國內(nèi)使用鞭执,首推 vue 司顿。文檔更易讀、易學(xué)兄纺、社區(qū)夠大
如果團隊水平較高大溜,推薦使用 React 。組件化和 JSX
hybrid
-
hybrid
是客戶端和前端的混合開發(fā) -
hybrid
存在的核心意義在于快速迭代囤热,無需審核 -
hybrid
實現(xiàn)流程: 服務(wù)端做好靜態(tài)頁面包猎提,客戶端檢查包版本,如果客戶端版本低,就獲取服務(wù)端靜態(tài)包, 客戶端拿到前端靜態(tài)頁面,以文件形式存儲在app
中
客戶端 使用file
協(xié)議 通過webview
加載靜態(tài)頁面
使用 NA
:體驗要求極致旁蔼,變化不頻繁(無頭條的首頁)
使用 hybrid
:體驗要求高锨苏,變化頻繁(如頭條的新聞詳情頁)產(chǎn)品型
使用 h5
:體驗無要求,不常用(如舉報棺聊、反饋等頁面) 運維型
優(yōu)點: 體驗好,快速迭代,無需審核
缺點: 開發(fā)成本高,運維成本高
JS與客端通信
String 自帶函數(shù)
substr()
含頭含尾(廢棄)伞租,substring()
含頭不含尾,slice()
也是含頭不含尾
JS 執(zhí)行過程 ??
event loop
事件循環(huán)機制 單線程 任務(wù)隊列
先由上到下執(zhí)行同步任務(wù),異步任務(wù)掛起,等同步任務(wù)處理后再執(zhí)行異步任務(wù)(如果同步任務(wù)沒有結(jié)束→死循環(huán),異步任務(wù)是不會執(zhí)行的.)
- 主線程 由上到下執(zhí)行 (同步任務(wù))
- 異步任務(wù):
- 微任務(wù):
Promise
mutationObserver
interSectionObserver
- 宏任務(wù):
setTimeout
setInterval
requestAnimationFrame
- 微任務(wù):
JS為什么是單線程?
避免DOM
渲染沖突
- 瀏覽器需要渲染
DOM
-
JS
可以修改DOM
-
JS
執(zhí)行的時候,瀏覽器DOM
渲染會暫停 - 兩段
JS
也不能同時執(zhí)行(都修改DOM
就沖突了) -
webworker
支持多線程,但不能訪問DOM
同步異步區(qū)別:
- 同步會阻塞代碼(
alert
),異步不會(setTimeout
)
異步問題
- 沒按書寫方式執(zhí)行,可讀性差
-
callback
中不容易模塊化
setTimeout(function(){
console.log(1)
},0);
new Promise(r=>{
console.log(2);
r();
console.log(3);
}).then(data=>{
console.log(4);
});
console.log(5);
// 2 3 5 4 1
$.ajax({
url:'xxx',
success:function(){
console.log(1)
}
})
setTimeout(function(){
console.log(2)
},1000)
setTimeout(function(){
console.log(3)
})
console.log(4)
// 這里分兩種情況,如果ajax請求數(shù)據(jù)的數(shù)據(jù)在1內(nèi)就返回了就
// 4,3,1,2
// 如果請求的數(shù)據(jù)沒在1內(nèi)返回
// 4,3,2,1
Promise ??
Promise
對象是 JavaScript 的異步操作解決方案限佩,為異步操作提供統(tǒng)一接口葵诈。簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果祟同。它起到代理作用(proxy)作喘,充當異步操作與回調(diào)函數(shù)之間的中介,使得異步操作具備同步操作的接口晕城。Promise
可以讓異步操作寫起來泞坦,就像在寫同步操作的流程,而不必一層層地嵌套回調(diào)函數(shù)砖顷。
特點
- 對象的狀態(tài)不受外界影響贰锁。
Promise
對象代表一個異步操作,有三種狀態(tài):pending
(進行中)滤蝠、fulfilled
(已成功)和rejected
(已失斖阆ā)。只有異步操作的結(jié)果物咳,可以決定當前是哪一種狀態(tài)锣险,任何其他操作都無法改變這個狀態(tài)。 - 一旦狀態(tài)改變,就不會再變
為什么要使用Promise
有了Promise
對象囱持,就可以將異步操作以同步操作的流程表達出來夯接,避免了層層嵌套的回調(diào)函數(shù); 解決回調(diào)地獄(異步).
原型方法:
Promise.prototype.then(resolved,rejected)
注意點:
-
then
有兩個參數(shù),第一個參數(shù)是resolved
狀態(tài)的回調(diào)函數(shù)纷妆,第二個參數(shù)(可選)是rejected
狀態(tài)的回調(diào)函數(shù) -
then
返回的是一個新的Promise
, 將以回調(diào)的返回值來resolve
.
Promise.prototype.catch()
Promise.prototype.catch
方法是.then(null, rejection
)或.then(undefined, rejection)
的別名盔几,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。
Promise.prototype.finally()
finally
方法用于指定不管 Promise
對象最后狀態(tài)如何掩幢,都會執(zhí)行的操作逊拍。該方法是 ES2018
引入標準的。
缺點
- 無法取消
Promise
际邻,一旦新建它就會立即執(zhí)行芯丧,無法中途取消。 - 如果不設(shè)置回調(diào)函數(shù)世曾,
Promise
內(nèi)部拋出的錯誤缨恒,不會反應(yīng)到外部。 - 當處于
pending
狀態(tài)時轮听,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)骗露。
面試題
///實現(xiàn)一個簡單的Promise
function Promise(fn){
var status = 'pending'
function successNotify(){
status = 'fulfilled'//狀態(tài)變?yōu)閒ulfilled
toDoThen.apply(undefined, arguments)//執(zhí)行回調(diào)
}
function failNotify(){
status = 'rejected'//狀態(tài)變?yōu)閞ejected
toDoThen.apply(undefined, arguments)//執(zhí)行回調(diào)
}
function toDoThen(){
setTimeout(()=>{ // 保證回調(diào)是異步執(zhí)行的
if(status === 'fulfilled'){
for(let i =0; i< successArray.length;i ++) {
successArray[i].apply(undefined, arguments)//執(zhí)行then里面的回掉函數(shù)
}
}else if(status === 'rejected'){
for(let i =0; i< failArray.length;i ++) {
failArray[i].apply(undefined, arguments)//執(zhí)行then里面的回掉函數(shù)
}
}
})
}
var successArray = []
var failArray = []
fn.call(undefined, successNotify, failNotify)
return {
then: function(successFn, failFn){
successArray.push(successFn)
failArray.push(failFn)
return undefined // 此處應(yīng)該返回一個Promise
}
}
}
HTML
塊元素 大多為結(jié)構(gòu)性標記
特點
- 獨占一行(從上到下排版);
- 可以設(shè)置
Css
盒子模型所有屬性;再不設(shè)置寬高時,寬繼承父級,寬有內(nèi)容決定; - 可以嵌套其他元素(p不能嵌套p,dt不能嵌套其他元素)
屬性名 | 描述 |
---|---|
section |
文檔節(jié) |
nav |
導(dǎo)航 |
header |
頁眉 |
article |
文章 |
aside |
文章側(cè)欄 |
footer |
頁腳 |
div |
|
h1 ~ h6
|
標題 |
p |
段 |
ul > li
|
無須列表 |
ol > li
|
有序列表 |
dl > dt > dd
|
自定義列表 |
table > tr > td
|
表格 |
form |
表單 |
行類元素 大多為描述性標記
特點
- 從左到右一行顯示;
- 不可以設(shè)置寬高;(但可設(shè)置
margin
、padding
左右值) - 不設(shè)置寬高時,寬高是又內(nèi)容決定的
- 編輯代碼時,行內(nèi)元素出現(xiàn)回車或者換行時,會默認又間隙(解決:父級(
body
)設(shè)置font-size:0
) - 不可以嵌套塊級元素
- 基線對齊問題
元素
屬性名 | 描述 |
---|---|
span |
內(nèi)聯(lián)容器 |
a |
|
strong b
|
|
em i
|
|
label |
行類塊級元素
特點
- 從左到右一行顯示;
- 可以設(shè)置Css盒子模型所有屬性;不設(shè)置寬高時,寬高是又內(nèi)容決定的
- 編輯代碼時,行內(nèi)塊級元素出現(xiàn)回車或者換行時,會默認又間隙(解決:父級(
body
)設(shè)置font-size:0
)
元素
| 屬性名 | 描述 |
|img
|
|input
|
|textarea
| 文本域|
|audio
|
|video
|
視口概念
<meta id="viewport" name="viewport"
content="width=device-width; initial-scale=1.0; maximum-scale=1; user-scalable=no;">
屬性名 | 取值 | 描述 |
---|---|---|
width | 正整數(shù) 或?device-width | 定義視口的寬度血巍,單位為像素 |
height | 正整數(shù) 或?device-height | 定義視口的高度萧锉,單位為像素,一般不用 |
initial-scale | [0.0-10.0] | 定義初始縮放值 |
minimum-scale | [0.0-10.0] | 定義縮小最小比例述寡,它必須小于或等于maximum-scale設(shè)置 |
maximum-scale | [0.0-10.0] | 定義放大最大比例柿隙,它必須大于或等于minimum-scale設(shè)置 |
user-scalable | yes/no | 定義是否允許用戶手動縮放頁面,默認值yes |
Css
盒子模型
標準盒子模型
高寬只有content
IE盒子模型(怪異模型)
高度是border
+padding
+content
設(shè)置方法:
/* 盒子模型最終寬高默認是不計算 邊框與內(nèi)邊距的
* content-box:最終寬高 = 內(nèi)容的寬高
* border-box:最終寬高 = border + padding + 內(nèi)容的寬高
*/
box-sizing:content-box; //默認
box-sizing:border-box; //IE模型
margin-top
傳值問題:
塊級元素嵌套時,當父級元素沒有設(shè)置padding-top
和border-top
時,自己元素設(shè)置了margin-top
值時,這個值會直接傳遞父級元素
解決方法:
父級元素設(shè)置:overflow:hidden
,超出隱藏;
margin
兼容問題:
一個元素設(shè)置了margin-top
,另一個元素設(shè)置了margin-bottom
,這兩個元素不會相加,會取最大值(這兩個元素時同級,平級關(guān)系的時候,? BFC解決)
三角形
//實心
div {
width: 0;
height: 0;
border: 40px solid;
border-color: transparent transparent red;
}
// 箭頭三角形
#blue:after {
content: "";
position: absolute;
top: 2px;
left: -38px;
border-width: 0 38px 38px;
border-style: solid;
border-color: transparent transparent #fff;
}
js 獲取盒子模型對于寬高
dom.style.width
/height
or
dom.currentStyle.width
/height
or
window.getComputedStyle(dom).width
/heigth
定位
相對定位 relative
當盒子本身發(fā)生位置改變時,又不影響其他元素,就用相對定位
- 不脫離文檔流
- 當位置發(fā)生改變時,原來位置還在占用
- 層級大于文檔流內(nèi)的其他元素(會覆蓋在其他元素之上)
- 參照物是本身
- 給絕對定位當作參照物(常用)
- 同時設(shè)置
top
和bottom
值時top
生效,同時設(shè)置left
和right
時left
生效
絕對定位 absoluto
不設(shè)置參照物時,參照物是
body
-
人為設(shè)置參照物時
- 參照物必須時父級元素
- 父級元素必須帶有定位屬性
- 同級之間不能設(shè)為參照物
<div style ="position:relative;"> <!-- <--- 這個才是參照物 --> <div style = "position:relative;"></div> <div> <div style = "position:absolute;"></div> </div> </div>
脫離文檔流的
不設(shè)置四個方向值時,這個絕對以為元素前面有其他元素,就會默認排在這個元素的后面
同時設(shè)置
top
和bottom
值時top
生效,同時設(shè)置left
和right
時left
當設(shè)置了寬高為100%時,將繼承參照物寬高
改變定位的層級關(guān)系 z-index
- 當定位屬性時平級時,哪個元素在上面就設(shè)置哪個屬性的
z-index
值(前提時設(shè)置定位屬性) - 當定位的父級元素同時設(shè)置
z-index
值時,子元素與父元素相比較時不生效的.
display
行類塊級元素轉(zhuǎn)換
-
block
塊級元素- 獨占一行
- 可設(shè)置寬高
- 可以嵌套
-
inline
: 行內(nèi)- 不可設(shè)置寬高
- 有間隙
- 基線對齊問題
- 只能從左到右
-
inline-block
: 行內(nèi)塊- 有間隙
- 基線對齊問題
- 只能從左到右
-
float
:浮動- 脫離文檔流(父級找不到子集),相當于來到了第二層級,平行默認文檔流
- 不在設(shè)置寬高時,寬高有內(nèi)容決定
- 所有元素都可以設(shè)置
float
無論是img
,a
,span
,div
... - 設(shè)置是浮動屬性,這個元素相當于是行內(nèi)塊級元素(可以設(shè)置寬高)
- 行類元素鲫凶、行內(nèi)塊級元素和文字圍繞著浮動元素排布(圖文混編)
清除float: 面試點
- 設(shè)置父級高度(不常用),不管子集有沒有元素,高度一定的.
- 給父級元素設(shè)置
overflow:hidden;
把子集元素拉回到文檔流內(nèi) - 清除浮動
clear:both
,必須保證三個前提- 使用這個這個元素必須是塊級元素
- 使用這個屬性的元素必須放再最后一個浮動元素后置,使用
:after偽類
搞定 - 使用這個屬性的元素不能帶有float屬性
.clear:after{
display:block;
content:"";
clear:both; /**高版瀏覽器**/
}
.clear{
*zoom:1;/**兼容低版本瀏覽器**/
}
BFC
就是頁面上的一個隔離的獨立容器禀崖,容器里面的子元素不會影響到外面的元素。反之也如此螟炫。
BFC``布局規(guī)則:
- 內(nèi)部的
Box
會在垂直方向波附,一個接一個地放置。 -
Box
垂直方向的距離由margin
決定不恭。屬于同一個BFC
的兩個相鄰Box
的margin
會發(fā)生重疊 - 每個元素的
margin box
的左邊叶雹, 與包含塊border
-
box
的左邊相接觸(對于從左往右的格式化财饥,否則相反)换吧。即使存在浮動也是如此。 -
BFC
的區(qū)域不會與float box
重疊钥星。 - 計算
BFC
的高度時沾瓦,浮動元素也參與計算
<iframe height="300" style="width: 100%;" scrolling="no" title="LodVrQ" src="http://codepen.io/luotian/embed/LodVrQ/?height=300&theme-id=31054&default-tab=css,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a >LodVrQ</a> by luotian
(<a >@luotian</a>) on <a >CodePen</a>.
</iframe>
DOM事件
- Dom事件級別(版本)
Dom0
element.onclick = function(){}
Dom2element.addEventListener('click',function(){},false)
Dom3element.addEventListener('keyup',function(){},false)
Dom1 制訂的時候沒有設(shè)計與事件相關(guān)的東西,所有沒有Dom1 ,Dom2與3寫法沒差別贯莺,Dom3 多了許多事件類型风喇; true:冒泡,false:捕獲
- Dom事件模型(捕獲缕探,冒泡)
捕獲:上到下 (
window
→document
→html
→body
→目標元素)
冒泡:下到上 (目標元素→body
→html
→document
→window
)
- Dom事件流
事件流:捕獲(階段)→ 目標階段→冒泡(階段)
Dom捕獲流程(參考2)
-
Event對象常用應(yīng)用
-
event.preventDefault()
:阻止默認行為(a標簽:設(shè)置了click事件魂莫,阻止其默認跳轉(zhuǎn)) -
event.stopPropagetion()
:阻止冒泡 -
event.stoplmmediatePropagetion()
:事件優(yōu)先級設(shè)置 -
event.currentTarget
: 當前綁定事件元素,父級元素 (事件代理) ? -
event.target
:獲取具體點擊元素 ?
-
-
自定義事件
//與其他事件結(jié)合使用 var eve = new Event('test'); ev.addEventListener('test', function () { console.log('test dispatch'); }); setTimeout(function () { ev.dispatchEvent(eve); //調(diào)用是對象爹耗,不是名稱 }, 1000);
HTTP 協(xié)議類 參考
特點
- 簡單快速:每個資源都是固定的耙考,直接請求獲取
- 無連接:不會保持連接,
HTTP 0.9
和1.0
使用非持續(xù)連接:限制每次連接只處理一個請求,服務(wù)器處理完客戶的請求潭兽,并收到客戶的應(yīng)答后倦始,即斷開連接。HTTP 1.1
使用持續(xù)連接:不必為每個web
對象創(chuàng)建一個新的連接山卦,一個連接可以傳送多個對象鞋邑,采用這種方式可以節(jié)省傳輸時間 - 無狀態(tài):單次
http
協(xié)議是不能區(qū)分連接者身份,無狀態(tài)是指協(xié)議對于事務(wù)處理沒有記憶能力。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息账蓉,則它必須重傳枚碗,這樣可能導(dǎo)致每次連接傳送的數(shù)據(jù)量增大。另一方面剔猿,在服務(wù)器不需要先前信息時它的應(yīng)答就較快视译。 - 靈活:
HTTP
允許傳輸任意類型的數(shù)據(jù)對象。正在傳輸?shù)念愋陀?code>Content-Type加以標記归敬。
HTTP報文
A(請求報文 Requst)-->B(請求行)
A--> C(請求頭 -- key/value值請求參數(shù))
A--> D(空行)
A--> E(請求體)
B--> F(http方法)
B--> G(頁面地址)
B--> H(http協(xié)議)
B--> I(http協(xié)議版本)
F--> J(GET - 獲取資源)
F--> K(POST - 傳輸資源)
F--> L(PUT -更新資源)
F--> M(DELETE - 刪除資源)
F--> N(HEAD -獲取報文首部)
Z{響應(yīng)報文 Respose}--> Y(狀態(tài)行)
Z-->X(響應(yīng)頭)
Z-->T(空行)
Z-->S(響應(yīng)體)
Y-->R(協(xié)議/協(xié)議版本)
Y-->Q(狀態(tài)碼)
Y-->P
GET與POST區(qū)別 99%的人都理解錯了HTTP中GET與POST的區(qū)別
- GET在瀏覽器回退時是無害的酷含,而POST會再次提交請求。?
- GET產(chǎn)生的URL地址可以被Bookmark汪茧,而POST不可以椅亚。
- GET請求會被瀏覽器主動cache,而POST不會舱污,除非手動設(shè)置呀舔。?
- GET請求只能進行url編碼,而POST支持多種編碼方式扩灯。
- GET請求參數(shù)會被完整保留在瀏覽器歷史記錄里媚赖,而POST中的參數(shù)不會被保留。
- GET請求在URL中傳送的參數(shù)是有長度限制的珠插,而POST么有惧磺。?
- 對參數(shù)的數(shù)據(jù)類型,GET只接受ASCII字符捻撑,而POST沒有限制磨隘。
- GET比POST更不安全缤底,因為參數(shù)直接暴露在URL上,所以不能用來傳遞敏感信息番捂。?
- GET參數(shù)通過URL傳遞个唧,POST放在Request body中?
GET和POST本質(zhì)上就是TCP鏈接,并無差別设预。但是由于HTTP的規(guī)定和瀏覽器/服務(wù)器的限制徙歼,導(dǎo)致他們在應(yīng)用過程中體現(xiàn)出一些不同。
GET和POST還有一個重大區(qū)別鳖枕,簡單的說:
GET產(chǎn)生一個TCP數(shù)據(jù)包鲁沥;POST產(chǎn)生兩個TCP數(shù)據(jù)包。
長的說:
對于GET方式的請求耕魄,瀏覽器會把http header和data一并發(fā)送出去画恰,服務(wù)器響應(yīng)200(返回數(shù)據(jù));
而對于POST吸奴,瀏覽器先發(fā)送header允扇,服務(wù)器響應(yīng)100 continue,瀏覽器再發(fā)送data则奥,服務(wù)器響應(yīng)200 ok(返回數(shù)據(jù))考润。
也就是說,GET只需要汽車跑一趟就把貨送到了读处,而POST得跑兩趟糊治,第一趟,先去和服務(wù)器打個招呼“嗨罚舱,我等下要送一批貨來井辜,你們打開門迎接我”,然后再回頭把貨送過去管闷。
HTTP 狀態(tài)碼
1xx: 指示信息-表請求已經(jīng)接收,繼續(xù)處理
-
2xx: 成功 - 請求已被成功接收
200.
OK
: 客戶端請求成功206.
Partial Content
:客戶發(fā)送一個帶有Range
頭的GET
請求,服務(wù)器完成了它 (某部分,播放視頻音頻文件) -
3xx: 重定向 - 要完成請求必須進行更進一步的操作
301.
Moved Permanenly
:永久重定向, 所請求的頁面已經(jīng)轉(zhuǎn)移至新的url
302.
Found
:臨時重定向 所有請求頁面已經(jīng)零時轉(zhuǎn)移至新的url
304.
Not Modified
:客戶端緩存可以繼續(xù)使用, 客戶端有緩沖的文檔并發(fā)出了一個條件性的請求,服務(wù)器告訴客戶,原來的緩存的文檔還可以繼續(xù)使用 -
4xx: 客戶端錯誤 - 請求喲語法錯誤或請求無法實現(xiàn)
400.
Bad Requset
客戶端請求有語法錯誤,不能被服務(wù)器理解401.
Unauthorized
請求未授權(quán),這個狀態(tài)碼必須和WWW-Authenticate
報文域一起使用403.
Forbidden
服務(wù)器收到請求粥脚,但是拒絕提供服務(wù)(不能直接訪問,只能服務(wù)器去訪問)404.
Not Found
請求資源不存在 -
5xx: 服務(wù)器錯誤 - 服務(wù)器未能實現(xiàn)合法的請求
500.
Internal Server Error
服務(wù)器錯誤,發(fā)生了不可預(yù)期的錯誤,原理來的緩存的文檔還可以繼續(xù)使用503.
Server Unavailable
請求未完成, 服務(wù)器零時過載或宕機,一段時間后可能恢復(fù)正常
HTTP 持久連接與管線化 (版本1.1后)
-
什么是持久連接? (keep alive模式)
HTTP1.1規(guī)定了默認保持長連接(HTTP persistent connection包个,也有翻譯為持久連接);數(shù)據(jù)傳輸完成了保持TCP連接不斷開(不發(fā)RST包刷允、不四次握手),等待在同域名下繼續(xù)用這個通道傳輸數(shù)據(jù)碧囊;相反的就是短連接树灶。
HTTP 1.1版本支持持久連接 1.0版本不支持
與非持久連接的區(qū)別:
持久連接使客戶端到服務(wù)器端連接持續(xù)有效,避免了重新建立連接
大大減少了連接的建立以及關(guān)閉時延糯而。HTTP連接是建立在TCP協(xié)議之上的天通,建立一條TCP連接需要三次握手,TCP連接關(guān)閉時需要四次揮手歧蒋。這些都是需要時間的土砂。
-
什么是管線化 → 客戶端并行發(fā)送請求,服務(wù)器并發(fā)多個響應(yīng)
管線化機制須通過永久連接(persistent connection)完成,僅HTTP/1.1支持此技術(shù)(HTTP/1.0不支持)
在使用持久連接的情況下谜洽,某個連接消息的傳遞類似于
請求1 → 響應(yīng)1 → 請求2 → 響應(yīng)2
管線化:某個連接上的消息變成了類似這樣
請求1 → 請求2 → 請求3 → 響應(yīng)1 → 響應(yīng)2 → 響應(yīng)3
?
- 那么持久連接和管線化的區(qū)別在于:持久連接的一個缺點是請求和響應(yīng)式是順序執(zhí)行的萝映,只有在請求1的響應(yīng)收到之后,才會發(fā)送請求2阐虚,而管線化不需要等待上一次請求得到響應(yīng)就可以進行下一次請求序臂。實現(xiàn)并行發(fā)送請求
- 只有GET和HEAD要求可以進行管線化,而POST則有所限制
- 初次創(chuàng)建連接時也不應(yīng)啟動管線機制实束,因為對方(服務(wù)器)不一定支持HTTP/1.1版本的協(xié)議
- HTTP1.1要求服務(wù)器端支持管線化奥秆,但并不要求服務(wù)器端也對響應(yīng)進行管線化處理,只是要求對于管線化的請求不失敗咸灿,而且現(xiàn)在很多服務(wù)器端和代理程序?qū)芫€化的支持并不好构订,現(xiàn)代瀏覽器Chrome和Firefox
跨域
只要協(xié)議、域名避矢、端口有任何一個不同悼瘾,就是跨域。
為什么不能跨域审胸?
瀏覽器有一個同源策略亥宿,用來保護用戶的安全。
跨域的解決方案
-
JSONP 原理:script標簽引入一個js文件,下載文件
- 優(yōu)點:兼容性好砂沛,在很古老的瀏覽器中也可以用烫扼,簡單易用,支持瀏覽器與服務(wù)器雙向通信碍庵。
- 缺點:只支持GET請求映企,且只支持跨域HTTP請求這種情況(不支持HTTPS)
CORS 一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)静浴。CORS需要瀏覽器和服務(wù)器同時支持卑吭。目前,所有瀏覽器都支持該功能马绝,IE瀏覽器不能低于IE10豆赏。整個CORS通信過程,都是瀏覽器自動完成富稻,不需要用戶參與掷邦。對于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別椭赋,代碼完全一樣抚岗。瀏覽器一旦發(fā)現(xiàn)AJAX請求跨源,就會自動添加一些附加的頭信息哪怔,有時還會多出一次附加的請求宣蔚,但用戶不會有感覺向抢。因此,實現(xiàn)CORS通信的關(guān)鍵是服務(wù)器胚委。只要服務(wù)器實現(xiàn)了CORS接口挟鸠,就可以跨源通信。
服務(wù)器代理.
-
H5 PostMessage
// postMessage // 窗口A(http:A.com)向跨域的窗口B(http:B.com)發(fā)送信息 Bwindow.postMessage('data', 'http://B.com'); // 在窗口B中監(jiān)聽 Awindow.addEventListener('message', function (event) { console.log(event.origin); console.log(event.source); console.log(event.data); }, false);
-
iframe hash
// 利用hash亩冬,場景是當前頁面 A 通過iframe或frame嵌入了跨域的頁面 B // 在A中偽代碼如下: var B = document.getElementsByTagName('iframe'); B.src = B.src + '#' + 'data'; // 在B中的偽代碼如下 window.onhashchange = function () { var data = window.location.hash; };
-
Websocket【參考資料】http://www.ruanyifeng.com/blog/2017/05/websocket.html
var ws = new WebSocket('wss://echo.websocket.org'); ws.onopen = function (evt) { console.log('Connection open ...'); ws.send('Hello WebSockets!'); }; ws.onmessage = function (evt) { console.log('Received Message: ', evt.data); ws.close(); }; ws.onclose = function (evt) { console.log('Connection closed.'); };
原型鏈
創(chuàng)建對象有幾種方法
// 第一種方式:字面量
var o1 = {name: 'o1'};
var o2 = new Object({name: 'o2'});
// 第二種方式:構(gòu)造函數(shù),任何函數(shù)都可以用作于構(gòu)造函數(shù),
// 只要去用New關(guān)鍵字去操作生成新的實例對象,這個函數(shù)就是構(gòu)造函數(shù)
var M = function (name) { this.name = name; };
var o3 = new M('o3');
// 第三種方式:Object.create
var p = {name: 'p'};
var o4 = Object.create(p);
M.prototype.say = function () {
console.log('say hi');
};
var o5 = new M('o5');
// new 運算符原理
var new2 = function (func) {
var o = Object.create(func.prototype);
var k = func.call(o);
if (typeof k === 'object') {
return k;
} else {
return o;
}
};
console.log(o3 instanceof M)// true
console.log(o3 instanceof Object)// true
什么是原型鏈:
一個實例對象向上找構(gòu)造該實例的相關(guān)聯(lián)對象,該相關(guān)聯(lián)的對象又往上找創(chuàng)造他的它的原型對象,以此類推,一直到object.prototype,這個鏈條就是原型鏈
作用:
公用方法
面向?qū)ο?/h2>
/**
* 類的聲明
*/
var Animal = function () {
this.name = 'Animal';
};
/**
* es6中class的聲明
*/
class Animal2 {
constructor () {
this.name = 'Animal2';
}
}
/**
* 實例化
*/
console.log(new Animal(), new Animal2());
/**
* 借助構(gòu)造函數(shù)實現(xiàn)繼承
* 缺點: 無法繼承 父類的prototype的公有方法
*/
function Parent1 () {
this.name = 'parent1';
}
Parent1.prototype.say = function () {
};
function Child1 () {
Parent1.call(this);
this.type = 'child1';
}
console.log(new Child1(), new Child1().say());
/**
* 借助原型鏈實現(xiàn)繼承
* 缺點: 父類的應(yīng)用類型屬性是公用的,都可以修改
*/
function Parent2 () {
this.name = 'parent2';
this.play = [1, 2, 3];
}
function Child2 () {
this.type = 'child2';
}
Child2.prototype = new Parent2();
var s1 = new Child2();
var s2 = new Child2();
console.log(s1.play, s2.play);
s1.play.push(4);
/**
* 組合方式
*/
function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
function Child3 () {
Parent3.call(this);
this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);
/**
* 組合繼承的優(yōu)化1
* @type {String}
*/
function Parent4 () {
this.name = 'parent4';
this.play = [1, 2, 3];
}
function Child4 () {
Parent4.call(this);
this.type = 'child4';
}
Child4.prototype = Parent4.prototype;
var s5 = new Child4();
var s6 = new Child4();
console.log(s5, s6);
console.log(s5 instanceof Child4, s5 instanceof Parent4);
console.log(s5.constructor);
/**
* 組合繼承的優(yōu)化2
*/
function Parent5 () {
this.name = 'parent5';
this.play = [1, 2, 3];
}
function Child5 () {
Parent5.call(this);
this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);
???
function Elem(id){
this.elem = document.getElementById(id);
}
Elem.prototype.on = function(type,fn){
var elem = this.elem;
elem.addEventListener(type,fn);
return this;
}
Elem.prototype.html = function(hl){
var elem = this.elem;
if(hl){
elem.innerHTML = hl;
return this;
}
else{
return elem.innerHTML;
}
}
var em = new Elem("ad_t2");
em.on('click',function(){console.log('click click')})
.html("<div>yooo~</div>")
.on('mouseover',function(){console.log("mouse mouse over voer....")});
js Class與普通構(gòu)造函數(shù)有何區(qū)別
- js構(gòu)造函數(shù)
function MathHandle(x,y){
this.x = x
this.y = y
}
MathHandle.prototype.add = function(){
return this.x + this.y
}
var m = new MathHandle(1,2)
console.log(m.add())
- class基本語法
class MathHandle{
constructor(x,y){
this.x = x
this.y = y
}
add(){
return this.x + this.y
}
}
const m = new MathHandle(1,2)
console.log(m.add())
/**
* 類的聲明
*/
var Animal = function () {
this.name = 'Animal';
};
/**
* es6中class的聲明
*/
class Animal2 {
constructor () {
this.name = 'Animal2';
}
}
/**
* 實例化
*/
console.log(new Animal(), new Animal2());
/**
* 借助構(gòu)造函數(shù)實現(xiàn)繼承
* 缺點: 無法繼承 父類的prototype的公有方法
*/
function Parent1 () {
this.name = 'parent1';
}
Parent1.prototype.say = function () {
};
function Child1 () {
Parent1.call(this);
this.type = 'child1';
}
console.log(new Child1(), new Child1().say());
/**
* 借助原型鏈實現(xiàn)繼承
* 缺點: 父類的應(yīng)用類型屬性是公用的,都可以修改
*/
function Parent2 () {
this.name = 'parent2';
this.play = [1, 2, 3];
}
function Child2 () {
this.type = 'child2';
}
Child2.prototype = new Parent2();
var s1 = new Child2();
var s2 = new Child2();
console.log(s1.play, s2.play);
s1.play.push(4);
/**
* 組合方式
*/
function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
function Child3 () {
Parent3.call(this);
this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);
/**
* 組合繼承的優(yōu)化1
* @type {String}
*/
function Parent4 () {
this.name = 'parent4';
this.play = [1, 2, 3];
}
function Child4 () {
Parent4.call(this);
this.type = 'child4';
}
Child4.prototype = Parent4.prototype;
var s5 = new Child4();
var s6 = new Child4();
console.log(s5, s6);
console.log(s5 instanceof Child4, s5 instanceof Parent4);
console.log(s5.constructor);
/**
* 組合繼承的優(yōu)化2
*/
function Parent5 () {
this.name = 'parent5';
this.play = [1, 2, 3];
}
function Child5 () {
Parent5.call(this);
this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);
???
function Elem(id){
this.elem = document.getElementById(id);
}
Elem.prototype.on = function(type,fn){
var elem = this.elem;
elem.addEventListener(type,fn);
return this;
}
Elem.prototype.html = function(hl){
var elem = this.elem;
if(hl){
elem.innerHTML = hl;
return this;
}
else{
return elem.innerHTML;
}
}
var em = new Elem("ad_t2");
em.on('click',function(){console.log('click click')})
.html("<div>yooo~</div>")
.on('mouseover',function(){console.log("mouse mouse over voer....")});
function MathHandle(x,y){
this.x = x
this.y = y
}
MathHandle.prototype.add = function(){
return this.x + this.y
}
var m = new MathHandle(1,2)
console.log(m.add())
class MathHandle{
constructor(x,y){
this.x = x
this.y = y
}
add(){
return this.x + this.y
}
}
const m = new MathHandle(1,2)
console.log(m.add())
三. 語法糖
在上述兩段代碼中分別加入如下代碼艘希,運行。
console.log(typeof MathHandle) // 'function'
console.log(MathHandle.prototype.constructor === MathHandle) //true
console.log(m.__proto__ === MathHandle.prototype) //true
運行結(jié)果一致硅急。我認為覆享,class是構(gòu)造函數(shù)的語法糖。
四营袜、繼承
-
構(gòu)造函數(shù)形式的繼承
//動物 function Animal(){ this.eat = function (){ console.log('Animal eat') } } //狗 function Dog() { this.bark = function (){ console.log('Dog bark') } } Dog.prototype = new Animal() var hashiqi = new Dog() hashiqi.bark() hashiqi.eat()
-
class繼承
class Animal { constructor(name){ this.name = name } eat(){ alert(this.name + ' eat') } } class Dog extends Animal { constructor(name){ super(name) //super就是被繼承的對象的constructer } say(){ alert(this.name + ' say') } } const dog = new Dog('哈士奇') dog.say() dog.eat()
五撒顿、總結(jié)
- class在語法上更貼近面向?qū)ο蟮膶懛ā?/li>
- class實現(xiàn)繼承更加易讀易理解。
前端安全類
CSRF
CSRF(Cross-site request forgery):跨站請求偽造荚板。
CSRF:又稱XSRF核蘸,冒充用戶發(fā)起請求(在用戶不知情的情況下),完成一些違背用戶意愿的請求(如惡意發(fā)帖,刪帖啸驯,改密碼客扎,發(fā)郵件等)。XSS
頁面性能
提升頁面性能方法:
資源壓縮,減少HTTP請求
非核心代碼異步加載 → 異步加載的方式 → 異步加載的區(qū)別
利用瀏覽器緩存 → 緩存的分類 → 緩存的原理
使用CDN
-
預(yù)解析DNS
<meta http-equiv="x-dns-prefetch-control" content="on" /> <link rel="dns-prefetch" />
-
異步加載的方式:
- 動態(tài)腳步加載,創(chuàng)建動態(tài)js標簽
- defer → 是在HTML解析完之后才會執(zhí)行,如果多個,按照加載的順序依次執(zhí)行
- async → 是在加載完成之后立即執(zhí)行,如果是多個,執(zhí)行順序和加載順序無關(guān)
-
緩存的分類: http 請求頭
- 強緩存
Expires
(絕對時間): Thu, 21 Jan 2017 18:25:02 GMT
?Cache-Control
: max-age=360(秒) - 協(xié)商緩存
Last-Modified
if-Modified-Since
?Etag
If-None-Match
- 強緩存
錯誤監(jiān)控類
-
即時運行錯誤:代碼錯誤
- try...catch
- window.onerror
-
資源加載錯誤(不會冒泡,但可捕獲)
- object.onerror
- performance.getEntries()
- Error 事件捕獲
window.addEventListener('error',function(e){ (new Image()).src="http://xxxxx上傳反饋錯誤的信息地址"; },true);
-
上報錯誤基本原理
- ajax上報
- Image對象上報 ?