前端面試 知識大盤點

JavaScript

作用域和作用域

執(zhí)行上下文

  • 范圍: 一段<script>或一個函數(shù)
  • 全局: 變量定義,函數(shù)聲明 一段<script>
  • 函數(shù): 變量定義,函數(shù)聲明,this,arguments 函數(shù)

作用域

jsES6前無塊級作用域

// 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í)行

  • 形參賦值

    1. 全局環(huán)境不能直接訪問局部環(huán)境
    2. 如果function(私有作用)沒有形參賦值, 私有作用域中變量無聲明修飾符,該變量就上級作用域找賦值;如果有形參賦值,私有作用域不會變量提升.
  • 變量提升

    1. 變量提升: 只是提升變量的聲明做祝,并不會把賦值也提升上來型诚。
    2. ? var a;在 全局作用域下聲明的變量,也是相當于給window設(shè)置了一個對象屬性,而且兩者之間建立了映射的機制 <==> window.a=undefined;
    3. 方法變量提升是會把函數(shù)體提上上來
    4. 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)事件綁定

循環(huán)事件綁定
循環(huán)事件綁定2

嚴格模式

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ù)引用

image

1.非嚴格模式下自行函數(shù)如果不是.出來的 就是屬于window

var b = 2;
var c = 5 + (b++); // c=7; b=2;  這里注意了b++加不加括號都是順序計算 這里括號沒意義

類:具備某些共同特征的實體的集合

實例:某一個類別具體的一個事務(wù)
js有5中簡單數(shù)據(jù)類型(也稱為基本數(shù)據(jù)類型): UndefinedNull荞胡、Boolean妈踊、NumberString。還有1中復(fù)雜的數(shù)據(jù)類型————Object泪漂,Object本質(zhì)上是由一組無序的名值對組成的廊营。
其中UndefinedNull萝勤、Boolean露筒、Number都屬于基本類型。Object敌卓、ArrayFunction則屬于引用類型慎式,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” 字符串,
nullobject
NaNnumber
undefinedundefined
那么此時我們有第二個方法可以使用, 是 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,因為toStringObject的原型方法,而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
image

原型重定向

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ū)е碌膯栴}

  1. 自己開辟的堆內(nèi)存中沒有constructor屬性争涌,導(dǎo)致類的原型構(gòu)造函數(shù)缺失(解決:自己手動在堆內(nèi)存中增加constructor屬性)
  2. 當原型重定向后,瀏覽器默認開辟的那個類原型堆內(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)境對象

  1. 元素綁定事件携狭,方法中的this是當前操作的元素
  2. 方法名簽是否有點继蜡,有點,點前面是誰this就是誰逛腿,沒有thiswindow(嚴格模式下是undefined)
  3. 構(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)點

  1. 避免命名沖突(減少全局命名空間污染);
  2. 更好的分離,按需加載;
  3. 代碼復(fù)用;
  4. 高可維護維護.

全局函數(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)的基石

問題:

  1. 請求過多
  2. 依賴模糊
  3. 難以維護
//
(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

AMDRequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。
CMDSeaJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出趴泌。
類似的還有 CommonJS Modules/2.0 規(guī)范舟舒,是 BravoJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。
還有不少??
這些規(guī)范的目的都是為了 JavaScript 的模塊化開發(fā)嗜憔,特別是在瀏覽器端的秃励。
目前這些規(guī)范的實現(xiàn)都能達成瀏覽器端模塊化開發(fā)的目的。

區(qū)別:

  1. 對于依賴的模塊吉捶,AMD 是提前執(zhí)行夺鲜,CMD 是延遲執(zhí)行。不過RequireJS 從 2.0 開始呐舔,也改成可以延遲執(zhí)行(根據(jù)寫法不同币励,處理方式不同)。CMD 推崇 as lazy as possible.

  2. 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 的作者默認是最喜歡上面的寫法,也是官方文檔里默認的模塊定義寫法剑辫。

  1. AMDAPI 默認是一個當多個用饼问,CMDAPI 嚴格區(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 的兩個特性改變了一切:

  1. 代碼拆分(Code Splitting) 使你可以將應(yīng)用程序分解成可管理的代碼塊拂酣,可以按需加載,這意味著你的用戶可以快速獲取交互性的網(wǎng)站仲义,而不必等到整個應(yīng)用程序下載和解析完成婶熬。當然你可以手動來完成這項工作,那么祝你好運埃撵。
  2. 靜態(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)流程

  1. Webpack編譯期蓝撇,為需要熱更新的 entry 注入熱更新代碼(EventSource通信) → 最新的webpack-dev-server,改成用WebSocket協(xié)議了叮姑,基于sockjs
  2. 頁面首次打開后唉地,服務(wù)端與客戶端通過 EventSource 建立通信渠道,把下一次的 hash 返回前端
  3. 客戶端獲取到hash传透,這個hash將作為下一次請求服務(wù)端 hot-update.jshot-update.jsonhash
  4. 修改頁面代碼后耘沼,Webpack 監(jiān)聽到文件修改后,開始編譯朱盐,編譯完成后群嗤,發(fā)送 build 消息給客戶端
  5. 客戶端獲取到hash,成功后客戶端構(gòu)造hot-update.js script鏈接兵琳,然后插入主文檔
  6. hot-update.js 插入成功后狂秘,執(zhí)行hotAPIcreateRecordreload方法,獲取到 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 三要素

  1. 響應(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)
        }
    
  2. 模板引擎: vue的模板是如何解析,指令如何處理,模板是什么?

    render函數(shù)執(zhí)行解析模板 vnode

    • 本質(zhì):字符串
    • 有邏輯,如 v-if v-for
    • HTML格式很像,但有很大區(qū)別
    • 最終還是要轉(zhuǎn)換為HTML來顯示
  3. 渲染:vue的模板如何渲染成HTML? 以及渲染過程

    • render函數(shù)執(zhí)行vnode
    • UpdateComponentpatch

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ù)逻杖,獲得vnodepatch轉(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頁面中

簡化

  1. 解析模版驯绎,通過render函數(shù)生成vnode格式的JS
  2. 響應(yīng)式監(jiān)聽數(shù)據(jù)變化(getter,setter)
  3. 渲染頁面,綁定依賴
  4. data數(shù)據(jù)變化,觸發(fā)re-render更新視圖

React vs Vue

本質(zhì)區(qū)別

  1. Vue - 本質(zhì)是 MVVM 框架完慧,由 MVC 發(fā)展而來
  2. React - 本質(zhì)是前端組件化框架,由后端組件化發(fā)展而來

但這并不妨礙他們兩者都能實現(xiàn)相同的功能

模板區(qū)別

  1. Vue - 使用模板(最初由angular 提出)
  2. React - 使用 JSX (模板與js混在一起,未分離)
  3. 模板語法上剩失,我更加傾向于 JSX
  4. 模板分離上屈尼,我更加傾向于 Vue

組件化的區(qū)別

  1. React 本身就是組件化册着,沒有組件化就不是 React
  2. Vue 也支持組件化,不過是在 MVVM 上的擴展
  3. 查閱 Vue 組件化的文檔脾歧,洋洋灑灑很多(側(cè)面反映)
  4. 對于組件化甲捏,我更加傾向于 React ,做的徹底而清晰

兩者共同點

  1. 都支持組件化
  2. 都是數(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ù):
    1. 微任務(wù): Promise mutationObserver interSectionObserver
    2. 宏任務(wù): setTimeout setInterval requestAnimationFrame

JS為什么是單線程?

避免DOM渲染沖突

  • 瀏覽器需要渲染DOM
  • JS 可以修改DOM
  • JS執(zhí)行的時候,瀏覽器DOM渲染會暫停
  • 兩段JS也不能同時執(zhí)行(都修改DOM就沖突了)
  • webworker 支持多線程,但不能訪問DOM

同步異步區(qū)別:

  • 同步會阻塞代碼(alert),異步不會(setTimeout)

異步問題

  1. 沒按書寫方式執(zhí)行,可讀性差
  2. 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ù)砖顷。

特點

  1. 對象的狀態(tài)不受外界影響贰锁。Promise對象代表一個異步操作,有三種狀態(tài):pending(進行中)滤蝠、fulfilled(已成功)和rejected(已失斖阆ā)。只有異步操作的結(jié)果物咳,可以決定當前是哪一種狀態(tài)锣险,任何其他操作都無法改變這個狀態(tài)。
  2. 一旦狀態(tài)改變,就不會再變

為什么要使用Promise
有了Promise對象囱持,就可以將異步操作以同步操作的流程表達出來夯接,避免了層層嵌套的回調(diào)函數(shù); 解決回調(diào)地獄(異步).

原型方法:

  1. 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.
  1. Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的別名盔几,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。

  1. Promise.prototype.finally()

finally方法用于指定不管 Promise 對象最后狀態(tài)如何掩幢,都會執(zhí)行的操作逊拍。該方法是 ES2018 引入標準的。

缺點

  1. 無法取消Promise际邻,一旦新建它就會立即執(zhí)行芯丧,無法中途取消。
  2. 如果不設(shè)置回調(diào)函數(shù)世曾,Promise內(nèi)部拋出的錯誤缨恒,不會反應(yīng)到外部。
  3. 當處于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)性標記

特點

  1. 獨占一行(從上到下排版);
  2. 可以設(shè)置Css盒子模型所有屬性;再不設(shè)置寬高時,寬繼承父級,寬有內(nèi)容決定;
  3. 可以嵌套其他元素(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 表單

行類元素 大多為描述性標記

特點

  1. 從左到右一行顯示;
  2. 不可以設(shè)置寬高;(但可設(shè)置marginpadding左右值)
  3. 不設(shè)置寬高時,寬高是又內(nèi)容決定的
  4. 編輯代碼時,行內(nèi)元素出現(xiàn)回車或者換行時,會默認又間隙(解決:父級(body)設(shè)置 font-size:0)
  5. 不可以嵌套塊級元素
  6. 基線對齊問題

元素

屬性名 描述
span 內(nèi)聯(lián)容器
a
strong b
em i
label

行類塊級元素

特點

  1. 從左到右一行顯示;
  2. 可以設(shè)置Css盒子模型所有屬性;不設(shè)置寬高時,寬高是又內(nèi)容決定的
  3. 編輯代碼時,行內(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-topborder-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ā)生位置改變時,又不影響其他元素,就用相對定位

  1. 不脫離文檔流
  2. 當位置發(fā)生改變時,原來位置還在占用
  3. 層級大于文檔流內(nèi)的其他元素(會覆蓋在其他元素之上)
  4. 參照物是本身
  5. 給絕對定位當作參照物(常用)
  6. 同時設(shè)置topbottom值時 top生效,同時設(shè)置leftrightleft生效

絕對定位 absoluto

  1. 不設(shè)置參照物時,參照物是body

  2. 人為設(shè)置參照物時

    • 參照物必須時父級元素
    • 父級元素必須帶有定位屬性
    • 同級之間不能設(shè)為參照物
    <div style ="position:relative;"> <!--  <--- 這個才是參照物 -->
        <div style = "position:relative;"></div>
        <div>
            <div style = "position:absolute;"></div>
        </div>
    </div>
    
  3. 脫離文檔流的

  4. 不設(shè)置四個方向值時,這個絕對以為元素前面有其他元素,就會默認排在這個元素的后面

  5. 同時設(shè)置topbottom值時 top生效,同時設(shè)置leftrightleft

  6. 當設(shè)置了寬高為100%時,將繼承參照物寬高

改變定位的層級關(guān)系 z-index

  1. 當定位屬性時平級時,哪個元素在上面就設(shè)置哪個屬性的z-index值(前提時設(shè)置定位屬性)
  2. 當定位的父級元素同時設(shè)置z-index值時,子元素與父元素相比較時不生效的.

display

行類塊級元素轉(zhuǎn)換

  • block 塊級元素

    1. 獨占一行
    2. 可設(shè)置寬高
    3. 可以嵌套
  • inline: 行內(nèi)

    1. 不可設(shè)置寬高
    2. 有間隙
    3. 基線對齊問題
    4. 只能從左到右
  • inline-block: 行內(nèi)塊

    1. 有間隙
    2. 基線對齊問題
    3. 只能從左到右
  • float:浮動

    1. 脫離文檔流(父級找不到子集),相當于來到了第二層級,平行默認文檔流
    2. 不在設(shè)置寬高時,寬高有內(nèi)容決定
    3. 所有元素都可以設(shè)置float 無論是img,a,span,div...
    4. 設(shè)置是浮動屬性,這個元素相當于是行內(nèi)塊級元素(可以設(shè)置寬高)
    5. 行類元素鲫凶、行內(nèi)塊級元素和文字圍繞著浮動元素排布(圖文混編)

清除float: 面試點

  1. 設(shè)置父級高度(不常用),不管子集有沒有元素,高度一定的.
  2. 給父級元素設(shè)置overflow:hidden; 把子集元素拉回到文檔流內(nèi)
  3. 清除浮動clear:both,必須保證三個前提
    • 使用這個這個元素必須是塊級元素
    • 使用這個屬性的元素必須放再最后一個浮動元素后置,使用:after偽類搞定
    • 使用這個屬性的元素不能帶有float屬性
.clear:after{
    display:block;
    content:"";
    clear:both; /**高版瀏覽器**/
}
.clear{
    *zoom:1;/**兼容低版本瀏覽器**/
}

BFC

就是頁面上的一個隔離的獨立容器禀崖,容器里面的子元素不會影響到外面的元素。反之也如此螟炫。

BFC``布局規(guī)則:

  1. 內(nèi)部的Box會在垂直方向波附,一個接一個地放置。
  2. Box垂直方向的距離由margin決定不恭。屬于同一個BFC的兩個相鄰Boxmargin會發(fā)生重疊
  3. 每個元素的margin box的左邊叶雹, 與包含塊border
  4. box的左邊相接觸(對于從左往右的格式化财饥,否則相反)换吧。即使存在浮動也是如此。
  5. BFC的區(qū)域不會與float box重疊钥星。
  6. 計算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事件

  1. Dom事件級別(版本)

Dom0 element.onclick = function(){}
Dom2 element.addEventListener('click',function(){},false)
Dom3 element.addEventListener('keyup',function(){},false)

Dom1 制訂的時候沒有設(shè)計與事件相關(guān)的東西,所有沒有Dom1 ,Dom2與3寫法沒差別贯莺,Dom3 多了許多事件類型风喇; true:冒泡,false:捕獲

  1. Dom事件模型(捕獲缕探,冒泡)

捕獲:上到下 (windowdocumenthtmlbody→目標元素)
冒泡:下到上 (目標元素→ bodyhtmldocumentwindow

  1. Dom事件流

事件流:捕獲(階段)→ 目標階段→冒泡(階段)

  1. Dom捕獲流程(參考2)

  2. Event對象常用應(yīng)用

    • event.preventDefault():阻止默認行為(a標簽:設(shè)置了click事件魂莫,阻止其默認跳轉(zhuǎn))
    • event.stopPropagetion():阻止冒泡
    • event.stoplmmediatePropagetion():事件優(yōu)先級設(shè)置
    • event.currentTarget: 當前綁定事件元素,父級元素 (事件代理) ?
    • event.target:獲取具體點擊元素 ?
  3. 自定義事件

    //與其他事件結(jié)合使用
    var eve = new Event('test');
            ev.addEventListener('test', function () {
                console.log('test dispatch');
            });
    
    setTimeout(function () {
        ev.dispatchEvent(eve);  //調(diào)用是對象爹耗,不是名稱
    }, 1000);
    

HTTP 協(xié)議類 參考

特點

  1. 簡單快速:每個資源都是固定的耙考,直接請求獲取
  2. 無連接:不會保持連接,HTTP 0.91.0使用非持續(xù)連接:限制每次連接只處理一個請求,服務(wù)器處理完客戶的請求潭兽,并收到客戶的應(yīng)答后倦始,即斷開連接。HTTP 1.1使用持續(xù)連接:不必為每個web對象創(chuàng)建一個新的連接山卦,一個連接可以傳送多個對象鞋邑,采用這種方式可以節(jié)省傳輸時間
  3. 無狀態(tài):單次http協(xié)議是不能區(qū)分連接者身份,無狀態(tài)是指協(xié)議對于事務(wù)處理沒有記憶能力。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息账蓉,則它必須重傳枚碗,這樣可能導(dǎo)致每次連接傳送的數(shù)據(jù)量增大。另一方面剔猿,在服務(wù)器不需要先前信息時它的應(yīng)答就較快视译。
  4. 靈活: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ū)別

  1. GET在瀏覽器回退時是無害的酷含,而POST會再次提交請求。?
  2. GET產(chǎn)生的URL地址可以被Bookmark汪茧,而POST不可以椅亚。
  3. GET請求會被瀏覽器主動cache,而POST不會舱污,除非手動設(shè)置呀舔。?
  4. GET請求只能進行url編碼,而POST支持多種編碼方式扩灯。
  5. GET請求參數(shù)會被完整保留在瀏覽器歷史記錄里媚赖,而POST中的參數(shù)不會被保留。
  6. GET請求在URL中傳送的參數(shù)是有長度限制的珠插,而POST么有惧磺。?
  7. 對參數(shù)的數(shù)據(jù)類型,GET只接受ASCII字符捻撑,而POST沒有限制磨隘。
  8. GET比POST更不安全缤底,因為參數(shù)直接暴露在URL上,所以不能用來傳遞敏感信息番捂。?
  9. 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后)

  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)閉時需要四次揮手歧蒋。這些都是需要時間的土砂。

  2. 什么是管線化 → 客戶端并行發(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é)議、域名避矢、端口有任何一個不同悼瘾,就是跨域。

為什么不能跨域审胸?
瀏覽器有一個同源策略亥宿,用來保護用戶的安全。

跨域的解決方案

  1. JSONP 原理:script標簽引入一個js文件,下載文件

    • 優(yōu)點:兼容性好砂沛,在很古老的瀏覽器中也可以用烫扼,簡單易用,支持瀏覽器與服務(wù)器雙向通信碍庵。
    • 缺點:只支持GET請求映企,且只支持跨域HTTP請求這種情況(不支持HTTPS)
  2. 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接口挟鸠,就可以跨源通信。

  3. 服務(wù)器代理.

  4. 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);
    
  5. 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;
    };
    
  6. 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())

三. 語法糖

在上述兩段代碼中分別加入如下代碼艘希,運行。

console.log(typeof MathHandle) // 'function'
console.log(MathHandle.prototype.constructor === MathHandle) //true
console.log(m.__proto__ === MathHandle.prototype) //true

運行結(jié)果一致硅急。我認為覆享,class是構(gòu)造函數(shù)的語法糖。

四营袜、繼承

  1. 構(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()
    
  2. 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é)

  1. class在語法上更貼近面向?qū)ο蟮膶懛ā?/li>
  2. class實現(xiàn)繼承更加易讀易理解。

前端安全類

  • CSRF
    CSRF(Cross-site request forgery):跨站請求偽造荚板。
    CSRF:又稱XSRF核蘸,冒充用戶發(fā)起請求(在用戶不知情的情況下),完成一些違背用戶意愿的請求(如惡意發(fā)帖,刪帖啸驯,改密碼客扎,發(fā)郵件等)。

  • XSS

頁面性能

提升頁面性能方法:

  1. 資源壓縮,減少HTTP請求

  2. 非核心代碼異步加載 → 異步加載的方式 → 異步加載的區(qū)別

  3. 利用瀏覽器緩存 → 緩存的分類 → 緩存的原理

  4. 使用CDN

  5. 預(yù)解析DNS

    <meta http-equiv="x-dns-prefetch-control"
    content="on" />
    <link rel="dns-prefetch"
     />
    
  • 異步加載的方式:

    1. 動態(tài)腳步加載,創(chuàng)建動態(tài)js標簽
    2. defer → 是在HTML解析完之后才會執(zhí)行,如果多個,按照加載的順序依次執(zhí)行
    3. async → 是在加載完成之后立即執(zhí)行,如果是多個,執(zhí)行順序和加載順序無關(guān)
  • 緩存的分類: http 請求頭

    1. 強緩存
      Expires(絕對時間): Thu, 21 Jan 2017 18:25:02 GMT
      ?Cache-Control: max-age=360(秒)
    2. 協(xié)商緩存
      Last-Modified if-Modified-Since
      ?Etag If-None-Match

錯誤監(jiān)控類

  • 即時運行錯誤:代碼錯誤

    1. try...catch
    2. window.onerror
  • 資源加載錯誤(不會冒泡,但可捕獲)

    1. object.onerror
    2. performance.getEntries()
    3. Error 事件捕獲
    window.addEventListener('error',function(e){
      (new Image()).src="http://xxxxx上傳反饋錯誤的信息地址";
    },true);
    
  • 上報錯誤基本原理

    1. ajax上報
    2. Image對象上報 ?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子府喳,更是在濱河造成了極大的恐慌,老刑警劉巖袱吆,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異距淫,居然都是意外死亡绞绒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門榕暇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蓬衡,“玉大人,你說我怎么就攤上這事彤枢≌恚” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵缴啡,是天一觀的道長壁晒。 經(jīng)常有香客問我,道長业栅,這世上最難降的妖魔是什么秒咐? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任谬晕,我火速辦了婚禮,結(jié)果婚禮上携取,老公的妹妹穿的比我還像新娘攒钳。我一直安慰自己,他們只是感情好歹茶,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著你弦,像睡著了一般惊豺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上禽作,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天尸昧,我揣著相機與錄音,去河邊找鬼旷偿。 笑死烹俗,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的萍程。 我是一名探鬼主播幢妄,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茫负!你這毒婦竟也來了蕉鸳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤忍法,失蹤者是張志新(化名)和其女友劉穎潮尝,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饿序,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡勉失,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了原探。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乱凿。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咽弦,靈堂內(nèi)的尸體忽然破棺而出告匠,到底是詐尸還是另有隱情,我是刑警寧澤离唬,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布后专,位于F島的核電站,受9級特大地震影響输莺,放射性物質(zhì)發(fā)生泄漏戚哎。R本人自食惡果不足惜裸诽,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望型凳。 院中可真熱鬧丈冬,春花似錦、人聲如沸甘畅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疏唾。三九已至蓄氧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間槐脏,已是汗流浹背喉童。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留顿天,地道東北人堂氯。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像牌废,于是被迫代替她去往敵國和親咽白。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容