2019前端知識(shí)點(diǎn)復(fù)習(xí)

TypeScript

1.可兼容JavaScript应民;相比js始绍,加入了注釋胜茧;添加一個(gè)完整的類結(jié)構(gòu)睹簇,更好的面向?qū)ο螅?/p>

2.Mac OS X環(huán)境搭建:

2.1.安裝homebrew(套件管理器)官網(wǎng):brew.sh:

ruby -e "$(curl -fsSL 
https://raw.githubusercontent.com/Homebrew/install/master/install)" 

2.2安裝npm(nodejs包管理器):

brew install node

2.3.安裝typescript(安裝成功后掌眠,可以鍵入tsc測(cè)試):

npm install -g typescript
tsc

3.IDE中新建typescript文件蕾盯,添加watcher實(shí)現(xiàn)自動(dòng)編譯

也可以用命令的方式編譯一個(gè)ts文件為js文件:終端目錄下,tsc xx.ts蓝丙,會(huì)生成同名js文件

4.TypeScript 7種 基礎(chǔ)數(shù)據(jù)類型:

Boolean:

var isBone: boolean = false; //使用時(shí)要給個(gè)初始化的值

Number:

var height: number = 6;

String:

var name: string = "bob";

Array:

var list: number[] = [1, 2, 3]; 
// 或者
var list: Array<string> = [ "joe", ”kevin” ];

獲取數(shù)組中數(shù)據(jù):

List[0];

Enum:

枚舉類型:有一定范圍的數(shù)據(jù)可以用枚舉類型最好

enum Color {Red, Green, Blue};

var colorName:string = Color[1];

console...

枚舉類型還可以進(jìn)行(數(shù)組)下標(biāo)賦值操作:

enum Color {Red = 10, Green = 12, Blue = 13};
var colorName: string = Color[12]; // Green
// 獲取下標(biāo):
var c:Color = Color.Green; // 1

Any:

任意類型:可以多次賦值级遭,后面的會(huì)覆蓋前面的。

var notSure: any = 10;
notSure = "hello";

也可以指定類型渺尘,如下指定為數(shù)組

Var list: any[ ] =  [1, “hello”, false];
List[2] // false

Void:

對(duì)函數(shù)進(jìn)行聲明:

function tell( ):string{
  return  "hello";
}

不需要任何返回值挫鸽,就可以把函數(shù)聲明為void

function tell1( ):void{}

類:

創(chuàng)建一個(gè)類:

class Preson{
  // 屬性
  name:string;
  age:number;
  // 構(gòu)造方法
  constructor(name:string, age:number){
    this.name = name;
    this.age = age;
  }
  // 普通方法
  print(){
    return this.name + " : " + this.age;
  }
}

調(diào)用這個(gè)類:

var p = new Person(“mike”, 10);
p.print();

類的繼承

無(wú)構(gòu)造函數(shù)的類的繼承:

class Person{
  name: string;
  age: number;
  tell(){
    return this.name + ":" + this.age;
  }
}

class Student extends Person{
  school: string;
  tell(){
    return this.name + ":" + this.age + ":" + this.school; 
  }
}
var s = new Student("捷克學(xué)院");
// 沒(méi)有構(gòu)造函數(shù),無(wú)法使用參數(shù)鸥跟,就用如下方式傳入?yún)?shù)值
s.name = "jack";
s.age = 23;
s.school = "捷克學(xué)院";
// 執(zhí)行
s.tell();

有構(gòu)造函數(shù)的類的繼承:

class Person{
  name: string;
  age: number;
  constructor(name:string, age:number){
    this.name = name;
    this.age = age;
  }
  tell(){
    return this.name + ":" + this.age;
  }
}

class Student extends Person{
  school: string;
  constructor(school:string){
    // 修改父類的參數(shù)丢郊,super要寫在最前面
    super("ime", 23);
    this.school = school;
  }
  tell(){
    return this.name + ":" + this.age + ":" + this.school; 
  }
}
var s = new Student("捷克學(xué)院");
// s.name = "jack";
// s.age = 23;
// s.school = "捷克學(xué)院";
s.tell();

訪問(wèn)修飾符

公有:public(默認(rèn))
私有:private

class Person{
  public name: string; 
  /**
  * 默認(rèn)就是public盔沫,如果我在這里寫private,
  * 或者在下面constructor參數(shù)中這樣寫:private name:string枫匾,
  * 那么繼承自它的 Student 就無(wú)法訪問(wèn)到 name 這個(gè)屬性了
  **/
  age: number;
  constructor(name:string, age:number){
    this.name = name;
    this.age = age;
  }
  tell(){
    return this.name + ":" + this.age;
  }
}

class Student extends Person{
  school: string;
  constructor(school:string){
    this.school = school;
    super("ime", 23);
  }
  tell(){
    return this.name + ":" + this.age + ":" + this.school; 
  }
}
var s = new Student("捷克學(xué)院");
// s.name = "jack";
// s.age = 23;
// s.school = "捷克學(xué)院";
s.tell();

封裝的實(shí)現(xiàn)

· 利用private實(shí)現(xiàn)的私有屬性架诞,可以在類中通過(guò)getter和setter來(lái)對(duì)外開(kāi)發(fā)(私有屬性或方法)接口
· 在調(diào)用方法時(shí),getter和setter會(huì)默認(rèn)執(zhí)行

class Hello{
  private _age:number;
  show(){
    return this._age; 
  }
  get age():number{
    // 可對(duì)外開(kāi)發(fā)私有屬性
    return this._age;
  }
  set age(newage:number){
    // 可修改私有屬性
    if(newage >200 || newage < 0){
      alert("請(qǐng)輸入正確的年齡");
    }else{
      this._age = newage;
    }
  }
}

var h = new Hello();
h.age = 300;
alert(h.tell());

static(靜態(tài))和其使用技巧

類的屬性或方法(非靜態(tài))干茉,可以通過(guò)實(shí)例化對(duì)象調(diào)用谴忧,如下:

class Person{
  name:string;
  tell(){
    alert("姓名:" + this.name);
  }
}

var p = new Person();
p.name = "hello";
p.tell();

但一旦屬性或方法聲明為Static后,就變?yōu)榱遂o態(tài)屬性 / 靜態(tài)方法
不能再通過(guò)實(shí)例化對(duì)象調(diào)用角虫,必須通過(guò)類本身調(diào)用:

class Person{
  static name:string;
  tell(){
    alert("姓名:" + Person.name);
  }
}

var p = new Person();
Person.name = "hello";
p.tell();

static靜態(tài)的使用(進(jìn)階)技巧:引用數(shù)據(jù)類型

class Greeter{
  greeting:string;
  constructor(msg:string){
    this.greeting = msg;
  }
  greet(){
    return "Hello," + this.greeting;
  }
}
/**
創(chuàng)建一個(gè)類型沾谓,指定它的類型為當(dāng)前類的類型;
其實(shí)戳鹅,創(chuàng)建了一個(gè)類均驶,也就是創(chuàng)建了一個(gè)聲明,
可以把當(dāng)前數(shù)據(jù)的類型枫虏,聲明為當(dāng)前類的類型
我們稱它為“引用數(shù)據(jù)類型”
**/
var green:Greeter;
green = new Greeter("mike");
alert(green.greet());

函數(shù):函數(shù)類型

函數(shù)有 命名函數(shù) 和 匿名函數(shù)妇穴,在Ts中,可以指定函數(shù)參數(shù)的類型和函數(shù)本身(返回值)的類型:

// 命名函數(shù)
function add(x:number, y:number):string{
  // 如果返回值的類型不是函數(shù)指定的類型模软,就會(huì)報(bào)錯(cuò)
  // 例如:return x+y伟骨;就會(huì)報(bào)錯(cuò)
  return "hello ts";// 類型相符燃异,不會(huì)報(bào)錯(cuò)
}
// 匿名函數(shù)(在js中,上面的命名函數(shù)是這種方式的語(yǔ)法糖)
var myadd = function(x:number, y:string):string{
  return "hello ts";
}

上面的x继蜡,y,并不清楚其意義,我們可以讓參數(shù)更明確其意義:

/** 
* "=>" 前面可以聲明參數(shù)的語(yǔ)義化名稱和類型
* "=>" 后面緊跟著函數(shù)類型
**/
var myAddts:(name:string, age:number) => number = function(n, a){
  return a;
}

函數(shù):可選和默認(rèn)參數(shù)

可選參數(shù):用 钮蛛?表示碟绑,可傳可不傳

function buildName(firstName:string, lastName?:string){
  if(lastName){
    return fristName + " " + lastName;
  } else {
    return firstName;
  }
}
// 均不會(huì)報(bào)錯(cuò)
var result1 = buildName("james", "bonde");
var result2 = buildName("bonde");
// 超出最大的參數(shù)個(gè)數(shù),則會(huì)報(bào)錯(cuò)
// var result3 = buildName("james", "bonde", "valin"); 

默認(rèn)參數(shù):直接在參數(shù)上賦值

function buildName(firstName:string, lastName="bonde"){
  return firstName + " and " + lastName;
}
// 可以只傳一個(gè)參數(shù)碘举,也可以傳兩個(gè)參數(shù)忘瓦,傳兩個(gè),則會(huì)覆蓋默認(rèn)參數(shù)值
var result1 = buildName("Hello: ");
var result2 = buildName("james", "wade");
// 超出最大的參數(shù)個(gè)數(shù)引颈,仍舊會(huì)報(bào)錯(cuò)
// var result3 = buildName("james", "bonde", "valin"); 

函數(shù):可變參數(shù)

利用es6的擴(kuò)展運(yùn)算符(展開(kāi)運(yùn)算符)

function peopleName(firstName:string, ...restOfName:string[]){
  // restOfName:string[] 代表數(shù)據(jù)類型為字符串的數(shù)組
  return firstName + " " + restOfName.join(" ");
}
// 利用可變參數(shù)耕皮,可以傳遞無(wú)數(shù)個(gè)參數(shù),在需要傳入不確定參數(shù)個(gè)數(shù)時(shí)蝙场,很好用
var pn = peolpleName("jack", "mary", "honly", "kevin"...);

Lambads 和 this 關(guān)鍵字的使用

var people = {
  name:["iwen","ime","if","bean"];
  getName:function(){
    return function(){
      var i = Math.floor(Math.random()*4);
      return {
        n:this.name[i]
      }
    }
  }
}

var myName = people.getName();
alert("名字:" + myName().n); // 這是訪問(wèn)不到的

此時(shí)這個(gè)this指向的不是people凌停。調(diào)用發(fā)現(xiàn)getName中的this關(guān)鍵字指向的是getName,訪問(wèn)不到外部的name屬性。

可以用lambads表達(dá)式"()=>"修改售滤,箭頭函數(shù)能保存函數(shù)創(chuàng)建時(shí)的 this值罚拟,而不是調(diào)用時(shí)的值台诗。

var people = {
  name:["iwen","ime","if","bean"];
  getName:function(){
    return () => {
      var i = Math.floor(Math.random()*4);
      return {
        // 這里的this代表函數(shù)創(chuàng)建時(shí)的this,因此可以訪問(wèn)到name屬性
        n:this.name[i]
      }
    }
  }
}

var myName = people.getName();
alert("名字:" + myName().n); // 此時(shí)可訪問(wèn)

TypeScript 的重載

function attr(name:string):string;
fucntion attr(age:number):number;

function attr(nameOrAge:any):any{
  if(nameOrAge && typeof nameOrAge === "string"){  
    alert("姓名");
  }else{
    alert("年齡");
  }
 }

/**
*  在某些編程語(yǔ)言中赐俗,函數(shù)重載或方法重載是指能夠創(chuàng)建具有不同實(shí)現(xiàn)的同名方法拉队。
*  對(duì)重載函數(shù)的調(diào)用將運(yùn)行與調(diào)用上下文相適應(yīng)的該函數(shù)的特定實(shí)現(xiàn),
*  允許一個(gè)函數(shù)調(diào)用根據(jù)上下文執(zhí)行不同的任務(wù)阻逮。
*  這里如果有不清楚可以看一下這里:https://www.zhihu.com/question/63751258
**/
attr("Hello");
attr(19);

接口-創(chuàng)建接口

// 規(guī)范了參數(shù)的類型
function printeLabel(labelObj:{label:string}){
  console.log(labelObj.label);
}
var myObj = {label:"Hello"};
// var myObj = {label:10};
printeLabel(myObj);

接口-可選屬性

interface USB{
  name:string;
  age:number;
}
function printUSB(pu:USB){
  console.log(pu.name);
}
// 如果不定義參數(shù)可選粱快,my必須包含兩個(gè)參數(shù),即string類型的name和number類型的age夺鲜,否則編譯會(huì)報(bào)錯(cuò)
var my = {name: "ime", age: 100};
printUSB(my);

可選參數(shù)通過(guò) " 皆尔?"定義

interface USB{
  name?:string;
  age?:number;
}
function printUSB(pu:USB){
  console.log(pu.name);
}
// 定義了參數(shù)可選,my就可以按照可選參數(shù)傳遞參數(shù)了币励。
var my = {name: "ime"};
printUSB(my);

接口-函數(shù)類型

用接口的類型 來(lái) 規(guī)定函數(shù)的類型

interface SearchFunc{
  (source:string, subString:string):boolean;
}
// 定義函數(shù)的類型為接口的類型
var mySearch:SearchFunc:
mySearch = function(src:string, sub:string){
  var result = src.search(sub);
  if(result != -1){
    return true;
  }else{
    return false;
   }
}

接口-數(shù)組類型

使用接口對(duì)數(shù)組進(jìn)行規(guī)范化

interface StringArray{
  /**
  * 數(shù)組中index對(duì)應(yīng)的是一個(gè)number類型慷蠕,但數(shù)組最終返回的是一個(gè)string類型
  * 可理解為下標(biāo)是number類型;數(shù)組中的數(shù)據(jù)是字符串類型
**/
  [index:number]:string; 
}

var myArray:StringArray;
// 規(guī)定了數(shù)組中必須是string類型食呻,其他類型都不可以流炕,即使為空,也必須是空字符串仅胞。
myArray = ["10", "12"];
alert(myArray[1]);

接口-class類型

interface ClockInterface{
  currentTime:Date;
  setTime(d:Date);
}
class Clock implements ClockInterface{
  currentTime:Date;
  setTime(d:Date){
    this.currentTime = d;
  }
  constructor(h:number; m:number){

  }
}

接口 - 接口繼承與混合類型

接口的繼承:

interface Shape{
  color:string;
}
interface PenStroke{
  penWidth:number;
}
interface Square extends Shape,PenStroke{
  sideLength:number;
}

var s = <Square>{}; // 將一個(gè)接口初始化在一個(gè)變量中
s.color = "blue";
s.penWidth = 10;
s.sideLength = 10;

接口的混合類型:

interface Counter{
  interval:number;
  reset():void;
  (start:number):string;
}

var c:Counter;
c(10);
c.reset();
...

認(rèn)識(shí)泛型

可以讓參數(shù)類型在使用時(shí)才指定類型每辟,而不需要在方法定義時(shí)就指定,這樣使用更加靈活干旧;它相比于any類型渠欺,在使用方法時(shí)有更好的明確性。

function Hello(num:number):number{
  return num; // 必須是number類型
}
function hello(str:any):any{
  return str; // 可以是任意類型
}
// 泛型一般用<T>表示,也可以用其他的大寫字母
function hello<T>(arg:T):T{
  return arg; // 在調(diào)用之前可以是任意類型
}
// 調(diào)用時(shí)指定參數(shù)類型后椎眯,就必須是這種類型
var output = hello<string>("world");

泛型的使用

Vue

1挠将、談?wù)勀銓?duì)MVVM開(kāi)發(fā)模式的理解
Model:代表數(shù)據(jù)模型,數(shù)據(jù)和業(yè)務(wù)邏輯都在Model層中定義编整;
View:代表UI視圖舔稀,負(fù)責(zé)數(shù)據(jù)的展示;
ViewModel:負(fù)責(zé)監(jiān)聽(tīng)Model中數(shù)據(jù)的改變并且控制視圖的更新掌测,處理用戶交互操作内贮;

Model和View并無(wú)直接關(guān)聯(lián),而是通過(guò)ViewModel來(lái)進(jìn)行聯(lián)系的汞斧,Model和ViewModel之間有著雙向數(shù)據(jù)綁定的聯(lián)系夜郁。因此當(dāng)Model中的數(shù)據(jù)改變時(shí)會(huì)觸發(fā)View層的刷新,View中由于用戶交互操作而改變的數(shù)據(jù)也會(huì)在Model中同步断箫。

這種模式實(shí)現(xiàn)了Model和View的數(shù)據(jù)自動(dòng)同步拂酣,因此開(kāi)發(fā)者只需要專注對(duì)數(shù)據(jù)的維護(hù)操作即可,而不需要自己操作dom仲义。

2婶熬、Vue 有哪些指令剑勾?
v-html、v-text赵颅、v-show虽另、v-if、v-for等

3饺谬、v-if 和 v-show 的區(qū)別
v-show 僅僅控制元素的顯示方式捂刺,將 display 屬性在 block 和 none 來(lái)回切換;而v-if會(huì)控制這個(gè) DOM 節(jié)點(diǎn)的存在與否募寨。當(dāng)我們需要經(jīng)常切換某個(gè)元素的顯示/隱藏時(shí)族展,使用v-show會(huì)更加節(jié)省性能上的開(kāi)銷;當(dāng)只需要一次顯示或隱藏時(shí)拔鹰,使用v-if更加合理仪缸。

4、簡(jiǎn)述Vue的響應(yīng)式原理
當(dāng)一個(gè)Vue實(shí)例創(chuàng)建時(shí)列肢,vue會(huì)遍歷data選項(xiàng)的屬性恰画,用 Object.defineProperty 將它們轉(zhuǎn)為getter/setter并且在內(nèi)部追蹤相關(guān)依賴,在屬性被訪問(wèn)和修改時(shí)通知變化瓷马。 每個(gè)組件實(shí)例都有相應(yīng)的watcher程序?qū)嵗┗梗鼤?huì)在組件渲染的過(guò)程中把屬性記錄為依賴,之后當(dāng)依賴項(xiàng)的setter被調(diào)用時(shí)欧聘,會(huì)通知watcher重新計(jì)算片林,從而致使它關(guān)聯(lián)的組件得以更新。
用ES5實(shí)現(xiàn):

// 第一步 實(shí)現(xiàn)基本架構(gòu)
// 第二步 把模型的數(shù)據(jù) 顯示到視圖
// 第三步  更新視圖同步到模型怀骤,再更新視圖
// 發(fā)布者
class Vue{
  constructor(options){
    this.options = options;
    this.$data = options.data;
    this.$el = document.querySelector(options,el);
    this._directives = {
       // 存放訂閱者集合的數(shù)組對(duì)象  
    }
    this.Observer(this.$data);
    this.Compile(this.$el);
  }
  // 劫持?jǐn)?shù)據(jù)
  Observer(data){
    // 更新視圖  局部更新
    for(let key in data){
      this._driectives[key] = []; // 空類數(shù)組對(duì)象
      
      let val = data[key]; // 當(dāng)前的值 
      let _this = this;
      Object.defineProperty(this.$data, key, {
        get: function(){
          return val;
        },
        set: function(newVal){
           if(newVal !== val){
              val = newVal;
              //_this._directives[key] 得到myText的數(shù)組
              _this._directives[key].forEach(watch=>{
                // 遍歷訂閱者的實(shí)例
                watch.update(); // 更新視圖
              })
            }
        }
      });
    }
  }
  // 解析指令
  Compile(el){
    let nodes = el.children;] // 獲取APP下面的子元素
    for(let i = 0;i < nodes.length; i++){
      let node = nodes[i]; // 當(dāng)前元素
      if(node.children.length){
        // 如果當(dāng)前元素含有子元素拇厢,就遞歸調(diào)用自己
        this.Compile(node);
      }
      if(node.hasAttribute('v-text')){
         // 獲取屬性值,對(duì)號(hào)加入對(duì)應(yīng)的訂閱者數(shù)組
         let attrVal = node.getAttribute("v-text")
         this._directives[attrVal].push(new Watcher(node, attrVal, this, 'innerHTML'));
      }
      if(node.hasAttribute('v-model')){
        let attrVal = node.getAttribute("v-model");
        this._directives[attrVal].push(new Watcher(node, attrVal, this, 'value'));
        // 監(jiān)聽(tīng)文本框事件
        node.addEventListener('input', (function(){
          return function(){
            // console.log(node.value);
            // 更新視圖到模型
            this.$data[attrVal] = node.value;
          }
        })());
      }
    }
  }
  class Watcher{
    constructor(el, vm, mySelf, attr){
      this.el = el; 
      this.el = vm; 
      this.el = mySelf; 
      this.el = attr; 

      this.update(); // 初始化數(shù)據(jù)
    }
    update(){
      // div對(duì)象[innerHTML] = vue對(duì)象.data['myText']
      // input對(duì)象[value] = vue對(duì)象.data['myText']
      this.el[this.attr] = this.mySelf.$data[this.vm];
    }
  }
}

在Vue3.0版本中式采用ES6的Proxy對(duì)象來(lái)實(shí)現(xiàn)晒喷。

// 獲取段落節(jié)點(diǎn)
const paragraph = document.getElementById('pararaph');
// 獲取輸入框節(jié)點(diǎn)
const input = document.getElementById('input');

// 需要代理的數(shù)據(jù)對(duì)象
const data = {
  text:  'hello world'
}

const handler = {
  // 監(jiān)控data中的text屬性變化
  set: function(target, prop, value){
    if(prop === 'text'){
      // 更新值
      target[prop] = value;
      // 更新試圖
      paragraph.innerHTML = value;
      input.value = value;
      retur true;
    } else {
      return false;
    }
  }
}
// 構(gòu)造 proxy 對(duì)象
const myText = new Proxy(data, handler);

// 添加input 監(jiān)聽(tīng)事件
input.addEventListener('input', function(e){
   myText.text = e.target.value; // 更新myText的值
});

初始化值
myText.text = data.text;

5、Vue中如何在組件內(nèi)部實(shí)現(xiàn)一個(gè)雙向數(shù)據(jù)綁定访敌?
假設(shè)有一個(gè)輸入框組件凉敲,用戶輸入時(shí),同步父組件頁(yè)面中的數(shù)據(jù)寺旺。
具體思路:父組件通過(guò)props傳值給子組件爷抓,子組件通過(guò) $emit 來(lái)通知父組件修改相應(yīng)的props值,具體實(shí)現(xiàn)如下:

import Vue from 'vue'
const component = {
    props: ['value'],
    template: ` < div > <input type = "text"@input = "handleInput": value = "value" > </div>`,
    data(){
        return{}
    },
    methods:{
        handleInput(e){
             this.$emit('input',e.target.value)
        }
    }
}

new Vue({
    components:{CompOne:component},
    el:'#root',template:`<div><comp-one:value1="value"@input="value = arguments[0]"></comp - one > </div>`,
    data(){
        return{value:'123'}
    }
})

6阻塑、Vue中v-model的實(shí)現(xiàn)原理是怎樣的蓝撇?
當(dāng)一個(gè)Vue實(shí)例創(chuàng)建時(shí),vue會(huì)遍歷data選項(xiàng)的屬性陈莽,用 Object.defineProperty 將它們轉(zhuǎn)為getter/setter并且在內(nèi)部追蹤相關(guān)依賴渤昌,在屬性被訪問(wèn)和修改時(shí)通知變化虽抄。 每個(gè)組件實(shí)例都有相應(yīng)的watcher程序?qū)嵗鼤?huì)在組件渲染的過(guò)程中把屬性記錄為依賴独柑,之后當(dāng)依賴項(xiàng)的setter被調(diào)用時(shí)迈窟,會(huì)通知watcher重新計(jì)算,從而致使它關(guān)聯(lián)的組件得以更新

7忌栅、Vue中如何監(jiān)控某個(gè)屬性值的變化车酣?
比如現(xiàn)在需要監(jiān)控data中, obj.a 的變化索绪。Vue中監(jiān)控對(duì)象屬性的變化你可以這樣:

watch: {
    obj: {
        handler(newValue, oldValue) {
            console.log('obj changed')
        },
        deep: true
    }
}

deep屬性表示深層遍歷湖员,但是這么寫會(huì)監(jiān)控obj的所有屬性變化,并不是我們想要的效果瑞驱,所以做點(diǎn)修改:

watch: {
    'obj.a': {
        handler(newName, oldName) {
            console.log('obj.a changed')
        }
    }
}

還有一種方法娘摔,可以通過(guò)computed 來(lái)實(shí)現(xiàn),只需要:

// 利用計(jì)算屬性的特性來(lái)實(shí)現(xiàn)钱烟,當(dāng)依賴改變時(shí)晰筛,便會(huì)重新計(jì)算一個(gè)新值。
computed: {
    a1() {
        return this.obj.a
    }
}

8拴袭、Vue中給data中的對(duì)象屬性添加一個(gè)新的屬性時(shí)會(huì)發(fā)生什么读第,如何解決?

<template>
    <div>
        <ul>
            <li v-for="value in obj" :key="value">
                {{value}}
            </li>
        </ul>
        <button @click="addObjB">
            添加obj.b
        </button>
    </div>
</template>
<script>
    export  default {
        data() {
            return {
                obj:
                {
                    a:  'obj.a'
                }
            }
        },
        methods: {
            addObjB() {
                this.obj.b = 'obj.b'console.log(this.obj)
            }
        }
    }
</script>
<style>
</style>

點(diǎn)擊button會(huì)發(fā)現(xiàn)拥刻, obj.b 已經(jīng)成功添加怜瞒,但是視圖并未刷新:


image.png

image.png

原因在于在Vue實(shí)例創(chuàng)建時(shí), obj.b 并未聲明般哼,因此就沒(méi)有被Vue轉(zhuǎn)換為響應(yīng)式的屬性吴汪,自然就不會(huì)觸發(fā)視圖的更新,這時(shí)就需要使用Vue的全局api—— $set():

// $set() 方法相當(dāng)于手動(dòng)的去把 obj.b 處理成一個(gè)響應(yīng)式的屬性蒸眠,此時(shí)視圖也會(huì)跟著改變了:
addObjB() {
    this.$set(this.obj, 'b', 'obj.b')
    console.log(this.obj)
}

視圖改變:


image.png

9漾橙、delete和Vue.delete刪除數(shù)組的區(qū)別
delete只是被刪除的元素變成了 empty/undefined 其他的元素的鍵值還是不變。
Vue.delete / this.$delete 直接刪除了數(shù)組 改變了數(shù)組的鍵值楞卡。

var a = [1, 2, 3, 4]
var b = [1, 2, 3, 4]
delete a[1]
console.log(a)
this.$delete(b, 1)
console.log(b)
image.png

image.png

10霜运、如何優(yōu)化SPA應(yīng)用的首屏加載速度慢的問(wèn)題?
· 將公用的JS庫(kù)通過(guò)script標(biāo)簽外部引入蒋腮,減小 app.bundel 的大小淘捡,讓瀏覽器并行下載資源文件,提高下載速度池摧;
· 在配置 路由時(shí)焦除,頁(yè)面和組件使用懶加載的方式引入,進(jìn)一步縮小 app.bundel 的體積作彤,在調(diào)用某個(gè)組件時(shí)再加載對(duì)應(yīng)的js文件膘魄;
· 加一個(gè)首屏loading圖乌逐,提升用戶體驗(yàn);

11瓣距、前端如何優(yōu)化網(wǎng)站性能黔帕?
1、減少 HTTP 請(qǐng)求數(shù)量
- css sprites
- 合并css和js文件蹈丸,壓縮
- 采用lazyLoad懶加載

2成黄、控制資源文件加載優(yōu)先級(jí)

3、利用瀏覽器緩存

4逻杖、減少重排(Reflow)
- 如果需要在DOM操作時(shí)添加樣式奋岁,盡量使用增加class屬性,而不是通過(guò)style操作樣式荸百。

5闻伶、減少 DOM 操作

6、圖標(biāo)使用 IconFont 替換

12够话、網(wǎng)頁(yè)從輸入網(wǎng)址到渲染完成經(jīng)歷了哪些過(guò)程蓝翰?
- 輸入網(wǎng)址;
- 發(fā)送到DNS服務(wù)器女嘲,并獲取域名對(duì)應(yīng)的web服務(wù)器對(duì)應(yīng)的ip地址畜份;
- 與web服務(wù)器建立TCP連接;
- 瀏覽器向web服務(wù)器發(fā)送http請(qǐng)求欣尼;
- web服務(wù)器響應(yīng)請(qǐng)求爆雹,并返回指定url的數(shù)據(jù)(或錯(cuò)誤信息,或重定向的新的url地址)愕鼓;
- 瀏覽器下載web服務(wù)器返回的數(shù)據(jù)及解析html源文件露懒;
- 生成DOM樹(shù)黔攒,解析css和js,渲染頁(yè)面订雾,直至顯示完成撩炊;

Vue的生命周期

beforeCreate(創(chuàng)建前)蟹但,在數(shù)據(jù)觀測(cè)和初始化事件還未開(kāi)始

created(創(chuàng)建后)恕刘,完成數(shù)據(jù)觀測(cè)桑嘶,屬性和方法的運(yùn)算,初始化事件册着, $el 屬性還沒(méi)有顯示出來(lái)

beforeMount(載入前),在掛載開(kāi)始之前被調(diào)用脾歧,相關(guān)的render函數(shù)首次被調(diào)用甲捏。實(shí)例已完成以下的配置:編譯模板,把data里面的數(shù)據(jù)和模板生成html鞭执。注意此時(shí)還沒(méi)有掛載html到頁(yè)面上司顿。

mounted(載入后)芒粹,在el 被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用大溜。實(shí)例已完成以下的配置:用上面編譯好的html內(nèi)容替換el屬性指向的DOM對(duì)象化漆。完成模板中的html渲染到html頁(yè)面中。此過(guò)程中進(jìn)行ajax交互钦奋。

beforeUpdate(更新前)座云,在數(shù)據(jù)更新之前調(diào)用,發(fā)生在虛擬DOM重新渲染和打補(bǔ)丁之前付材‰希可以在該鉤子中進(jìn)一步地更改狀態(tài),不會(huì)觸發(fā)附加的重渲染過(guò)程厌衔。

updated(更新后)璧帝,在由于數(shù)據(jù)更改導(dǎo)致的虛擬DOM重新渲染和打補(bǔ)丁之后調(diào)用。調(diào)用時(shí)富寿,組件DOM已經(jīng)更新睬隶,所以可以執(zhí)行依賴于DOM的操作。然而在大多數(shù)情況下页徐,應(yīng)該避免在此期間更改狀態(tài)苏潜,因?yàn)檫@可能會(huì)導(dǎo)致更新無(wú)限循環(huán)。該鉤子在服務(wù)器端渲染期間不被調(diào)用泞坦。

beforeDestroy(銷毀前)窖贤,在實(shí)例銷毀之前調(diào)用。實(shí)例仍然完全可用贰锁。

destroyed(銷毀后)赃梧,在實(shí)例銷毀之后調(diào)用。調(diào)用后豌熄,所有的事件監(jiān)聽(tīng)器會(huì)被移除授嘀,所有的子實(shí)例也會(huì)被銷毀。該鉤子在服務(wù)器端渲染期間不被調(diào)用锣险。

什么是vue生命周期蹄皱?

Vue 實(shí)例從創(chuàng)建到銷毀的過(guò)程,就是生命周期芯肤。從開(kāi)始創(chuàng)建巷折、初始化數(shù)據(jù)、編譯模板崖咨、掛載Dom→渲染锻拘、更新→渲染、銷毀等一系列過(guò)程,稱之為 Vue 的生命周期署拟。

2婉宰、vue生命周期的作用是什么?
它的生命周期中有多個(gè)事件鉤子推穷,讓我們?cè)诳刂普麄€(gè)Vue實(shí)例的過(guò)程時(shí)更容易形成好的邏輯心包。

3、vue生命周期總共有幾個(gè)階段馒铃?
它可以總共分為8個(gè)階段:創(chuàng)建前/后蟹腾、載入前/后、更新前/后骗露、銷毀前/銷毀后岭佳。

4、第一次頁(yè)面加載會(huì)觸發(fā)哪幾個(gè)鉤子萧锉?
beforeCreate珊随、created、beforeMount柿隙、mounted 叶洞。

5、DOM 渲染在哪個(gè)周期中就已經(jīng)完成禀崖?
mounted

Vue實(shí)現(xiàn)數(shù)據(jù)雙向綁定的原理: Object.defineProperty()

1衩辟、首先Object.defineProperty()的用法:
Object.defineProperty() 方法會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改一個(gè)對(duì)象的現(xiàn)有屬性波附, 并返回這個(gè)對(duì)象艺晴。
語(yǔ)法:Object.defineProperty(obj, prop, descriptor)
obj:要在其上定義屬性的對(duì)象。
prop:要定義或修改的屬性的名稱掸屡。
descriptror:將被定義或修改的屬性描述符封寞。

2、vue實(shí)現(xiàn)數(shù)據(jù)雙向綁定主要是:
采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式仅财,通過(guò) Object.defineProperty() 來(lái)劫持各個(gè)屬性的setter狈究,getter,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者盏求,觸發(fā)相應(yīng)監(jiān)聽(tīng)回調(diào)抖锥。當(dāng)把一個(gè)普通 Javascript 對(duì)象傳給 Vue 實(shí)例來(lái)作為它的 data 選項(xiàng)時(shí),Vue 將遍歷它的屬性碎罚,用 Object.defineProperty() 將它們轉(zhuǎn)為 getter/setter磅废。用戶看不到 getter/setter,但是在內(nèi)部它們讓 Vue 追蹤依賴荆烈,在屬性被訪問(wèn)和修改時(shí)通知變化还蹲。

vue的數(shù)據(jù)雙向綁定 將MVVM作為數(shù)據(jù)綁定的入口,整合Observer,Compile和Watcher三者谜喊,通過(guò)Observer來(lái)監(jiān)聽(tīng)自己的model的數(shù)據(jù)變化,通過(guò)Compile來(lái)解析編譯模板指令(vue中是用來(lái)解析 {{}})倦始,最終利用watcher搭起observer和Compile之間的通信橋梁斗遏,達(dá)到數(shù)據(jù)變化 —>視圖更新;視圖交互變化(input)—>數(shù)據(jù)model變更雙向綁定效果鞋邑。

js通過(guò)Object.defineProperty()實(shí)現(xiàn)簡(jiǎn)單的雙向綁定:

<body>
    <div id="app">
        <input type="text" id="txt">
        <p id="show">
        </p>
    </div>
</body>
<script type="text/javascript">
    var obj = {};

    Object.defineProperty(obj, 'txt', {
        get: function (){
            return obj
        },
        set: function (newValue) {
          document.getElementById('txt').value = newValue;
          document.getElementById('show').innerHTML = newValue
        }
    }) ;

    document.addEventListener('keyup', function (e) {
        obj.txt = e.target.value;
    });
</script>

js通過(guò)Observer诵次、Compile、Watcher實(shí)現(xiàn)一個(gè)原生JS的雙向綁定:

// 第一步 實(shí)現(xiàn)基本架構(gòu)
// 第二步 把模型的數(shù)據(jù) 顯示到視圖
// 第三步  更新視圖同步到模型枚碗,再更新視圖
// 發(fā)布者
class Vue{
  constructor(options){
    this.options = options;
    this.$data = options.data;
    this.$el = document.querySelector(options,el);
    this._directives = {
       // 存放訂閱者集合的數(shù)組對(duì)象  
    }
    this.Observer(this.$data);
    this.Compile(this.$el);
  }
  // 劫持?jǐn)?shù)據(jù)
  Observer(data){
    // 更新視圖  局部更新
    for(let key in data){
      this._driectives[key] = []; // 空類數(shù)組對(duì)象
      
      let val = data[key]; // 當(dāng)前的值 
      let _this = this;
      Object.defineProperty(this.$data, key, {
        get: function(){
          return val;
        },
        set: function(newVal){
           if(newVal !== val){
              val = newVal;
              //_this._directives[key] 得到myText的數(shù)組
              _this._directives[key].forEach(watch=>{
                // 遍歷訂閱者的實(shí)例
                watch.update(); // 更新視圖
              })
            }
        }
      });
    }
  }
  // 解析指令
  Compile(el){
    let nodes = el.children;] // 獲取APP下面的子元素
    for(let i = 0;i < nodes.length; i++){
      let node = nodes[i]; // 當(dāng)前元素
      if(node.children.length){
        // 如果當(dāng)前元素含有子元素逾一,就遞歸調(diào)用自己
        this.Compile(node);
      }
      if(node.hasAttribute('v-text')){
         // 獲取屬性值,對(duì)號(hào)加入對(duì)應(yīng)的訂閱者數(shù)組
         let attrVal = node.getAttribute("v-text")
         this._directives[attrVal].push(new Watcher(node, attrVal, this, 'innerHTML'));
      }
      if(node.hasAttribute('v-model')){
        let attrVal = node.getAttribute("v-model");
        this._directives[attrVal].push(new Watcher(node, attrVal, this, 'value'));
        // 監(jiān)聽(tīng)文本框事件
        node.addEventListener('input', (function(){
          return function(){
            // console.log(node.value);
            // 更新視圖到模型
            this.$data[attrVal] = node.value;
          }
        })());
      }
    }
  }
  class Watcher{
    constructor(el, vm, mySelf, attr){
      this.el = el; 
      this.vm= vm; 
      this.mySelf= mySelf; 
      this.attr= attr; 

      this.update(); // 初始化數(shù)據(jù)
    }
    update(){
      // div對(duì)象[innerHTML] = vue對(duì)象.data['myText']
      // input對(duì)象[value] = vue對(duì)象.data['myText']
      this.el[this.attr] = this.mySelf.$data[this.vm];
    }
  }
}

Vue組件間的參數(shù)傳遞

1肮雨、父組件與子組件傳值
父組件傳給子組件:子組件通過(guò)props方法接受數(shù)據(jù)遵堵;
子組件傳給父組件: $emit 方法傳遞參數(shù)

2、非父子組件間的數(shù)據(jù)傳遞怨规,兄弟組件傳值
eventBus陌宿,就是創(chuàng)建一個(gè)事件中心,相當(dāng)于中轉(zhuǎn)站波丰,可以用它來(lái)傳遞事件和接收事件壳坪。項(xiàng)目比較小時(shí),用這個(gè)比較合適(雖然也有不少人推薦直接用VUEX掰烟,具體來(lái)說(shuō)看需求咯爽蝴。技術(shù)只是手段,目的達(dá)到才是王道)纫骑。

Vue的路由實(shí)現(xiàn):hash模式 和 history模式

hash模式:在瀏覽器中符號(hào)“#”蝎亚,#以及#后面的字符稱之為hash,用 window.location.hash 讀取惧磺。特點(diǎn):hash雖然在URL中颖对,但不被包括在HTTP請(qǐng)求中;用來(lái)指導(dǎo)瀏覽器動(dòng)作磨隘,對(duì)服務(wù)端安全無(wú)用缤底,hash不會(huì)重加載頁(yè)面。

history模式:history采用HTML5的新特性番捂;且提供了兩個(gè)新方法: pushState()个唧, replaceState()可以對(duì)瀏覽器歷史記錄棧進(jìn)行修改,以及popState事件的監(jiān)聽(tīng)到狀態(tài)變更设预。

Vue與Angular以及React的區(qū)別徙歼?

1、與AngularJS的區(qū)別

相同點(diǎn):都支持指令:內(nèi)置指令和自定義指令;都支持過(guò)濾器:內(nèi)置過(guò)濾器和自定義過(guò)濾器魄梯;都支持雙向數(shù)據(jù)綁定桨螺;都不支持低端瀏覽器。

不同點(diǎn):AngularJS的學(xué)習(xí)成本高酿秸,比如增加了Dependency Injection特性灭翔,而Vue.js本身提供的API都比較簡(jiǎn)單、直觀辣苏;在性能上肝箱,AngularJS依賴對(duì)數(shù)據(jù)做臟檢查,所以Watcher越多越慢稀蟋;Vue.js使用基于依賴追蹤的觀察并且使用異步隊(duì)列更新煌张,所有的數(shù)據(jù)都是獨(dú)立觸發(fā)的。

2退客、與React的區(qū)別

相同點(diǎn):React采用特殊的JSX語(yǔ)法骏融,Vue.js在組件開(kāi)發(fā)中也推崇編寫.vue特殊文件格式,對(duì)文件內(nèi)容都有一些約定井辜,兩者都需要編譯后使用绎谦;中心思想相同:一切都是組件,組件實(shí)例之間可以嵌套粥脚;都提供合理的鉤子函數(shù)窃肠,可以讓開(kāi)發(fā)者定制化地去處理需求;都不內(nèi)置列數(shù)AJAX刷允,Route等功能到核心包冤留,而是以插件的方式加載;在組件開(kāi)發(fā)中都支持mixins的特性树灶。

不同點(diǎn):React采用的Virtual DOM會(huì)對(duì)渲染出來(lái)的結(jié)果做臟檢查纤怒;Vue.js在模板中提供了指令,過(guò)濾器等天通,可以非常方便泊窘,快捷地操作Virtual DOM。

vue路由的鉤子函數(shù)

首頁(yè)可以控制導(dǎo)航跳轉(zhuǎn)像寒,beforeEach烘豹,afterEach等,一般用于頁(yè)面title的修改诺祸。一些需要登錄才能調(diào)整頁(yè)面的重定向功能携悯。

beforeEach主要有3個(gè)參數(shù)to,from筷笨,next典勇。
to:route即將進(jìn)入的目標(biāo)路由對(duì)象。
from:route當(dāng)前導(dǎo)航正要離開(kāi)的路由炒嘲。
next:function一定要調(diào)用該方法resolve這個(gè)鉤子。執(zhí)行效果依賴next方法的調(diào)用參數(shù)昌跌。可以控制網(wǎng)頁(yè)的跳轉(zhuǎn)照雁。

vuex是什么避矢?怎么使用?哪種功能場(chǎng)景使用它囊榜?

只用來(lái)讀取的狀態(tài)集中放在store中; 改變狀態(tài)的方式是提交mutations亥宿,這是個(gè)同步的事物卸勺; 異步邏輯應(yīng)該封裝在action中。

在main.js引入store烫扼,注入曙求。新建了一個(gè)目錄store,… export 映企。

場(chǎng)景有:?jiǎn)雾?yè)應(yīng)用中悟狱,組件之間的狀態(tài)、音樂(lè)播放堰氓、登錄狀態(tài)挤渐、加入購(gòu)物
image.png

state

Vuex 使用單一狀態(tài)樹(shù),即每個(gè)應(yīng)用將僅僅包含一個(gè)store 實(shí)例,但單一狀態(tài)樹(shù)和模塊化并不沖突双絮。存放的數(shù)據(jù)狀態(tài)浴麻,不可以直接修改里面的數(shù)據(jù)。

mutations

mutations定義的方法動(dòng)態(tài)修改Vuex 的 store 中的狀態(tài)或數(shù)據(jù)囤攀。

getters

類似vue的計(jì)算屬性软免,主要用來(lái)過(guò)濾一些數(shù)據(jù)。

action

actions可以理解為通過(guò)將mutations里面處里數(shù)據(jù)的方法變成可異步的處理數(shù)據(jù)的方法焚挠,簡(jiǎn)單的說(shuō)就是異步操作數(shù)據(jù)膏萧。view 層通過(guò) store.dispath 來(lái)分發(fā) action。

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count ++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment');
    }
  }
 })

modules

項(xiàng)目特別復(fù)雜的時(shí)候蝌衔,可以讓每一個(gè)模塊擁有自己的state榛泛、mutation、action胚委、getters挟鸠,使得結(jié)構(gòu)非常清晰,方便管理亩冬。

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const moduleC = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const store = new Vuex.Store({
  modules: {
    a:  moduleA,
    b:  moduleB,
    c:  moduleC
  }
})

其他知識(shí)點(diǎn):

1艘希、css只在當(dāng)前組件起作用

答:在style標(biāo)簽中寫入scoped即可 例如: <stylescoped></style>

2硼身、v-if 和 v-show 區(qū)別

答:v-if按照條件是否渲染,v-show是display的block或none覆享;

3佳遂、route和router的區(qū)別
route是“路由信息對(duì)象”,包括path撒顿,params丑罪,hash,query凤壁,fullPath吩屹,matched,name等路由信息參數(shù)拧抖。而router是“路由實(shí)例”對(duì)象包括了路由的跳轉(zhuǎn)方法煤搜,鉤子函數(shù)等。

http 和 https

HTTPS(Secure Hypertext Transfer Protocol)安全超文本傳輸協(xié)議:
它是一個(gè)安全通信通道唧席,它基于HTTP開(kāi)發(fā)擦盾,用于在客戶計(jì)算機(jī)和服務(wù)器之間交換信息,它使用安全套接字層(SSL)進(jìn)行信息交換淌哟,簡(jiǎn)單來(lái)說(shuō)它是HTTP的安全版迹卢。它是由Netscape開(kāi)發(fā)并內(nèi)置于其瀏覽器中,用于對(duì)數(shù)據(jù)進(jìn)行壓縮和解壓操作徒仓,并返回網(wǎng)絡(luò)上傳送回的結(jié)果腐碱。
HTTPS實(shí)際上應(yīng)用了Netscape的安全套接字層(SSL)作為HTTP應(yīng)用層的子層。(HTTPS使用端口443蓬衡,而不是象HTTP那樣使用端口80來(lái)和TCP/IP進(jìn)行通信喻杈。)SSL使用40 位關(guān)鍵字作為RC4流加密算法,這對(duì)于商業(yè)信息的加密是合適的狰晚。
HTTPS和SSL支持使用X.509數(shù)字認(rèn)證筒饰,如果需要的話用戶可以確認(rèn)發(fā)送者是誰(shuí)”谏梗總的來(lái)說(shuō)瓷们,HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議要比http協(xié)議安全秒咐。

在URL前加https://前綴表明是用SSL(安全套接字)加密的谬晕,你的電腦與服務(wù)器之間收發(fā)的信息傳輸將更加安全。 Web服務(wù)器啟用SSL需要獲得一個(gè)服務(wù)器證書(shū)并將該證書(shū)與要使用SSL的服務(wù)器綁定携取。

HTTPS和HTTP的區(qū)別:

https協(xié)議需要到ca申請(qǐng)證書(shū)攒钳,一般免費(fèi)證書(shū)很少,需要交費(fèi)雷滋。
http是超文本傳輸協(xié)議不撑,信息是明文傳輸文兢,https 則是具有安全性的ssl加密傳輸協(xié)議。
http和https使用的是完全不同的連接方式用的端口也不一樣,前者是80,后者是443焕檬。
http的連接很簡(jiǎn)單,是無(wú)狀態(tài)的姆坚。
HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議 要比http協(xié)議安全实愚。
詳細(xì)參看:http://www.reibang.com/p/6db0c6dc97a9

GET 和 POST

GET 和 POST 報(bào)文上的區(qū)別

先下結(jié)論兼呵,GET 和 POST 方法沒(méi)有實(shí)質(zhì)區(qū)別,只是報(bào)文格式不同腊敲。

GET 和 POST 只是 HTTP 協(xié)議中兩種請(qǐng)求方式击喂,而 HTTP 協(xié)議是基于 TCP/IP 的應(yīng)用層協(xié)議,無(wú)論 GET 還是 POST碰辅,用的都是同一個(gè)傳輸層協(xié)議茫负,所以在傳輸上,沒(méi)有區(qū)別乎赴。

報(bào)文格式上,不帶參數(shù)時(shí)潮尝,最大區(qū)別就是第一行方法名不同

POST方法請(qǐng)求報(bào)文第一行是這樣的

POST /uri HTTP/1.1 \r\n

GET方法請(qǐng)求報(bào)文第一行是這樣的

 GET /uri HTTP/1.1 \r\n

是的榕吼,不帶參數(shù)時(shí)他們的區(qū)別就僅僅是報(bào)文的前幾個(gè)字符不同而已

帶參數(shù)時(shí)報(bào)文的區(qū)別呢? 在約定中勉失,GET 方法的參數(shù)應(yīng)該放在 url 中羹蚣,POST 方法參數(shù)應(yīng)該放在 body 中

舉個(gè)例子,如果參數(shù)是 name=chengqm, age=22乱凿。
GET 方法簡(jiǎn)約版報(bào)文是這樣的

GET /index.php?name=qiming.c&age=22 HTTP/1.1
Host:  localhost

POST 方法簡(jiǎn)約版報(bào)文是這樣的

POST /index.php  HTTP/1.1
Host:  localhost
Content-Type:  application/x-www-form-urlencoded

name=qiming.c&age=22

現(xiàn)在我們知道了兩種方法本質(zhì)上是 TCP 連接顽素,沒(méi)有差別,也就是說(shuō)徒蟆,如果我不按規(guī)范來(lái)也是可以的胁出。我們可以在 URL 上寫參數(shù),然后方法使用 POST段审;也可以在 Body 寫參數(shù)全蝶,然后方法使用 GET。當(dāng)然寺枉,這需要服務(wù)端支持抑淫。

常見(jiàn)問(wèn)題:

  1. GET 方法參數(shù)寫法是固定的嗎?
    在約定中姥闪,我們的參數(shù)是寫在 ? 后面始苇,用 & 分割。
    我們知道筐喳,解析報(bào)文的過(guò)程是通過(guò)獲取 TCP 數(shù)據(jù)催式,用正則等工具從數(shù)據(jù)中獲取 Header 和 Body函喉,從而提取參數(shù)。
    也就是說(shuō)蓄氧,我們可以自己約定參數(shù)的寫法函似,只要服務(wù)端能夠解釋出來(lái)就行,一種比較流行的寫法是 http://www.example.com/user/name/chengqm/age/22喉童。

  2. POST 方法比 GET 方法安全撇寞?
    按照網(wǎng)上大部分文章的解釋,POST 比 GET 安全堂氯,因?yàn)閿?shù)據(jù)在地址欄上不可見(jiàn)蔑担。
    然而,從傳輸?shù)慕嵌葋?lái)說(shuō)咽白,他們都是不安全的啤握,因?yàn)?HTTP 在網(wǎng)絡(luò)上是明文傳輸?shù)模灰诰W(wǎng)絡(luò)節(jié)點(diǎn)上捉包晶框,就能完整地獲取數(shù)據(jù)報(bào)文排抬。
    要想安全傳輸,就只有加密授段,也就是 HTTPS蹲蒲。

  3. GET 方法的長(zhǎng)度限制是怎么回事?
    HTTP 協(xié)議沒(méi)有 Body 和 URL 的長(zhǎng)度限制届搁,對(duì) URL 限制的大多是瀏覽器和服務(wù)器的原因卡睦。
    瀏覽器原因就不說(shuō)了漱抓,服務(wù)器是因?yàn)樘幚黹L(zhǎng) URL 要消耗比較多的資源,為了性能和安全(防止惡意構(gòu)造長(zhǎng) URL 來(lái)攻擊)考慮乞娄,會(huì)給 URL 長(zhǎng)度加限制补胚。

  4. POST 方法會(huì)產(chǎn)生兩個(gè)TCP數(shù)據(jù)包骚腥?
    HTTP 協(xié)議中沒(méi)有明確說(shuō)明 POST 會(huì)產(chǎn)生兩個(gè) TCP 數(shù)據(jù)包束铭,而且實(shí)際測(cè)試(Chrome)發(fā)現(xiàn)带猴,header 和 body 不會(huì)分開(kāi)發(fā)送。
    所以,header 和 body 分開(kāi)發(fā)送是部分瀏覽器或框架的請(qǐng)求方法涕侈,不屬于 post 必然行為木张。

  5. w3school 里面說(shuō) URL 的最大長(zhǎng)度是 2048 個(gè)字符
    url 長(zhǎng)度限制是某些瀏覽器和服務(wù)器的限制技肩,和 HTTP 協(xié)議沒(méi)有關(guān)系。

跨域 —— 9種跨域方式實(shí)現(xiàn)原理

參考:https://mp.weixin.qq.com/s/nkDkofBidKQFp5oG4-aJNg

什么是跨域?

當(dāng)協(xié)議剧浸、子域名吨艇、主域名冯吓、端口號(hào)中任意一個(gè)不相同時(shí)凸舵,都算作不同域。不同域之間相互請(qǐng)求資源,就算作“跨域”。

同源策略

同源策略是一種約定摄咆,它是瀏覽器最核心也最基本的安全功能恶迈,如果缺少了同源策略步做,瀏覽器很容易受到 XSS、CSFR 等攻擊。所謂同源是指"協(xié)議+域名+端口"三者相同,即便兩個(gè)不同的域名指向同一個(gè) ip 地址一喘,也非同源。

同源策略限制內(nèi)容有:

Cookie、LocalStorage戳粒、IndexedDB 等存儲(chǔ)性內(nèi)容
DOM 節(jié)點(diǎn)
AJAX 請(qǐng)求發(fā)送后涂籽,結(jié)果被瀏覽器攔截了

但是有三個(gè)標(biāo)簽是允許跨域加載資源:
<img src=XXX>
<link href=XXX>
<script src=XXX>

請(qǐng)求跨域了树枫,那么請(qǐng)求到底發(fā)出去沒(méi)有斤吐?

跨域并不是請(qǐng)求發(fā)不出去庄呈,請(qǐng)求能發(fā)出去幌绍,服務(wù)端能收到請(qǐng)求并正常返回結(jié)果,只是結(jié)果被瀏覽器攔截了。

跨域解決方案:

(1)JSONP

(2)cors(后臺(tái)設(shè)置:簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求)

(3)postMeassage
HTML5 XMLHttpRequest Level 2 中的 API寨辩,windows屬性,可用于解決:

  • 頁(yè)面和其他打開(kāi)的新窗口的數(shù)據(jù)傳遞
  • 多窗口之間消息傳遞
  • 頁(yè)面與嵌套的iframe消息傳遞
  • 上面三個(gè)場(chǎng)景的跨域數(shù)據(jù)傳遞

(4)websocket
實(shí)現(xiàn)了瀏覽器與服務(wù)器的全雙工通信缩多,websocket和http都是應(yīng)用層協(xié)議武契,基于TCP協(xié)議内颗,但是 WebSocket 是一種雙向通信協(xié)議恨溜,在建立連接之后躺盛,WebSocket 的 server 與 client 都能主動(dòng)向?qū)Ψ桨l(fā)送或接收數(shù)據(jù)周叮。

(5)node中間件代理(兩次跨域)

借助了服務(wù)器和服務(wù)器之間無(wú)需遵循同源策略的特點(diǎn)各薇。
image.png

(6)nginx 反向代理
使用nginx反向代理實(shí)現(xiàn)跨域棕叫,是最簡(jiǎn)單的跨域方式急侥。
支持所有瀏覽器贝润,支持 session鹏秋,不需要修改任何代碼,并且不會(huì)影響服務(wù)器性能琴锭。

實(shí)現(xiàn)思路:通過(guò) nginx 配置一個(gè)代理服務(wù)器(域名與 domain1 相同蓖捶,端口不同)做跳板機(jī),反向代理訪問(wèn) domain2 接口皮获,并且可以順便修改 cookie 中 domain 信息萌京,方便當(dāng)前域 cookie 寫入比庄,實(shí)現(xiàn)跨域登錄制恍。

//porxy服務(wù)器
server {
  listen  80;
  server_name  www.domain1.com;
  location  /  {
    proxy_pas  http://www.domain2.com:8080; #反向代理
    proxy_cookie_domain  www.domain2.com  www.domain1.com;  #修改cookie里域名
    index  index.html  index.htm;
    #當(dāng)用webpack-dev-server等中間件代理接口訪問(wèn)nignx時(shí)鹃唯,此時(shí)無(wú)瀏覽器參與,故沒(méi)有同源限制,下面的跨域配置可不啟用
    add_header Access-Control-Allow-Origin http://www.domain1.com;  #當(dāng)前端只跨域不帶cookie時(shí),可為*
    add_header Access-Control-Allow-Credentials true;
  }
}

最后通過(guò)命令行nginx -s reload啟動(dòng) nginx

// index.html
var xhr = new XMLHttpRequest();
// 前端開(kāi)關(guān):瀏覽器是否讀寫cookie
xhr.withCredentials = true;
// 訪問(wèn)nginx中的代理服務(wù)器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();

// server.js
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
    var params = qs.parse(req.url.substring(2));
    // 向前臺(tái)寫cookie
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:腳本無(wú)法讀取
    });
    res.write(JSON.stringify(params));
    res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');

(7)window.name + iframe
window.name 屬性的獨(dú)特之處:name 值在不同的頁(yè)面(甚至不同域名)加載后依舊存在仗谆,并且可以支持非常長(zhǎng)的 name 值(2MB)。

其中 a.html 和 b.html 是同域的指煎,都是http://localhost:3000;而 c.html 是http://localhost:4000

// a.html(http://localhost:3000/b.html)
  <iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
  <script>
    let first = true
    // onload事件會(huì)觸發(fā)2次枢纠,第1次加載跨域頁(yè),并留存數(shù)據(jù)于window.name
    function load() {
      if(first){
      // 第1次onload(跨域頁(yè))成功后,切換到同域代理頁(yè)面
        let iframe = document.getElementById('iframe');
        iframe.src = 'http://localhost:3000/b.html';
        first = false;
      }else{
      // 第2次onload(同域b.html頁(yè))成功后,讀取同域window.name中數(shù)據(jù)
        console.log(iframe.contentWindow.name);
      }
    }
  </script>

b.html 為中間代理頁(yè),與 a.html 同域,內(nèi)容為空。

// c.html(http://localhost:4000/c.html)
  <script>
    window.name = '我不愛(ài)你'
  </script>

通過(guò) iframe 的 src 屬性由外域轉(zhuǎn)向本地域劲装,跨域數(shù)據(jù)即由 iframe 的 window.name 從外域傳遞到本地域。這個(gè)就巧妙地繞過(guò)了瀏覽器的跨域訪問(wèn)限制,但同時(shí)它又是安全操作佑附。

(8)location.hash + iframe
實(shí)現(xiàn)原理: a.html 欲與 c.html 跨域相互通信秃嗜,通過(guò)中間頁(yè) b.html 來(lái)實(shí)現(xiàn)恋沃。 三個(gè)頁(yè)面,不同域之間利用 iframe 的 location.hash 傳值,相同域之間直接 js 訪問(wèn)來(lái)通信。

具體實(shí)現(xiàn)步驟:一開(kāi)始 a.html 給 c.html 傳一個(gè) hash 值,然后 c.html 收到 hash 值后朱沃,再把 hash 值傳遞給 b.html,最后 b.html 將結(jié)果放到 a.html 的 hash 值中。
同樣的椭迎,a.html 和 b.html 是同域的,都是http://localhost:3000;而 c.html 是http://localhost:4000

// a.html
  <iframe src="http://localhost:4000/c.html#iloveyou"></iframe>
  <script>
    window.onhashchange = function () { //檢測(cè)hash的變化
      console.log(location.hash);
    }
  </script>
 // b.html
  <script>
    window.parent.parent.location.hash = location.hash
    //b.html將結(jié)果放到a.html的hash值中田盈,b.html可通過(guò)parent.parent訪問(wèn)a.html頁(yè)面
  </script>
 // c.html
 console.log(location.hash);
  let iframe = document.createElement('iframe');
  iframe.src = 'http://localhost:3000/b.html#idontloveyou';
  document.body.appendChild(iframe);

(9)document.domain + iframe
該方式只能用于二級(jí)域名相同的情況下畜号,比如 a.test.com 和 b.test.com 適用于該方式允瞧。
只需要給頁(yè)面添加 document.domain ='test.com' 表示二級(jí)域名都相同就可以實(shí)現(xiàn)跨域简软。

實(shí)現(xiàn)原理:兩個(gè)頁(yè)面都通過(guò) js 強(qiáng)制設(shè)置 document.domain 為基礎(chǔ)主域,就實(shí)現(xiàn)了同域述暂。

我們看個(gè)例子:頁(yè)面a.zf1.cn:3000/a.html獲取頁(yè)面b.zf1.cn:3000/b.html中 a 的值

// a.html
<body>
 helloa
  <iframe src="http://b.zf1.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe>
  <script>
    document.domain = 'zf1.cn'
    function load() {
      console.log(frame.contentWindow.a);
    }
  </script>
</body>
// b.html
<body>
   hellob
   <script>
     document.domain = 'zf1.cn'
     var a = 100;
   </script>
</body>

總結(jié):

  • CORS 支持所有類型的 HTTP 請(qǐng)求痹升,是跨域 HTTP 請(qǐng)求的根本解決方案
  • JSONP 只支持 GET 請(qǐng)求,JSONP 的優(yōu)勢(shì)在于支持老式瀏覽器畦韭,以及可以向不支持 CORS 的網(wǎng)站請(qǐng)求數(shù)據(jù)疼蛾。
  • 不管是 Node 中間件代理還是 nginx 反向代理,主要是通過(guò)同源策略對(duì)服務(wù)器不加限制艺配。
  • 日常工作中察郁,用得比較多的跨域方案是 cors 和 nginx 反向代理

PWA

PWA全稱Progressive Web App,即 漸進(jìn)式WEB應(yīng)用转唉。

一個(gè) PWA 應(yīng)用首先是一個(gè)網(wǎng)頁(yè), 可以通過(guò) Web 技術(shù)編寫出一個(gè)網(wǎng)頁(yè)應(yīng)用. 隨后添加上 App Manifest 和 Service Worker 來(lái)實(shí)現(xiàn) PWA 的安裝和離線等功能

解決了哪些問(wèn)題绳锅?
可以添加至主屏幕,點(diǎn)擊主屏幕圖標(biāo)可以實(shí)現(xiàn)啟動(dòng)動(dòng)畫(huà)以及隱藏地址欄
實(shí)現(xiàn)離線緩存功能酝掩,即使用戶手機(jī)沒(méi)有網(wǎng)絡(luò)鳞芙,依然可以使用一些離線功能
實(shí)現(xiàn)了消息推送
它解決了上述提到的問(wèn)題,這些特性將使得 Web 應(yīng)用漸進(jìn)式接近原生 App期虾。
更多可參看:https://segmentfault.com/a/1190000012353473?utm_source=tag-newest

原型與原型鏈

什么是原型原朝?
在js中,所有對(duì)象都是Object的實(shí)例镶苞,并集成Object.prototype的屬性和方法喳坠,但是有一些是隱性的。

所有引用類型都具有對(duì)象特性茂蚓,可以自由擴(kuò)展屬性壕鹉。

var obj = {};
obj.attribute = "new attr";
var arr = [];
arr.attribute = "new attr";
function fn () {}
fn.attribute = "new attr";

所有的引用類型(包括數(shù)組,對(duì)象聋涨,函數(shù))都有隱性原型屬性(proto), 值也是一個(gè)普通的對(duì)象晾浴。

console.log(obj.__proto__);

所有的函數(shù),都有一個(gè) prototype 屬性牍白,值也是一個(gè)普通的對(duì)象脊凰。

console.log(obj.prototype);

所有的引用類型的proto屬性值都指向構(gòu)造函數(shù)的 prototype 屬性值。

console.log(obj.__proto__ === Object.prototype); // true

當(dāng)試圖獲取對(duì)象屬性時(shí)茂腥,如果對(duì)象本身沒(méi)有這個(gè)屬性狸涌,那就會(huì)去他的proto(prototype)中去尋找切省。

function Dog(name){ 
   this.name = name; 
} 
Dog.prototype.callName = function (){ 
   console.log(this.name,"wang wang"); 
}
let dog1 = new Dog("Three Mountain"); 
dog1.printName = function (){ 
   console.log(this.name);
}
dog1.callName();  // Three Mountain wang wang
dog1.printName(); // Three Mountain

原型鏈圖:
image.png

作用域及閉包

只有函數(shù)才能創(chuàng)造作用域。
for if else 不能創(chuàng)造作用域帕胆。

this:

本質(zhì)上來(lái)說(shuō)朝捆,在 js 里 this 是一個(gè)指向函數(shù)執(zhí)行環(huán)境的指針。this 永遠(yuǎn)指向最后調(diào)用它的對(duì)象懒豹,并且在執(zhí)行時(shí)才能獲取值芙盘,定義是無(wú)法確認(rèn)他的值。

var a = { 
    name : "A", 
    fn : function (){
        console.log (this.name) 
    } 
} 
a.fn() // this === a 
a 調(diào)用了fn() 所以此時(shí)this為a
a.fn.call ({name : "B"}) // this === {name : "B"} 
使用call(),將this的值指定為{name:"B"}
var fn1 = a.fn 
fn1() // this === window
雖然指定fn1 = a.fn歼捐,但是調(diào)用是有window調(diào)用,所以this 為window

this 有多種使用場(chǎng)景:

  1. 作為構(gòu)造函數(shù)執(zhí)行:
function  Student(name,age) {
    this.name = name           // this === s
    this.age = age             // this === s
    //return  this
}
var s = new Student("py1988",30)
  1. 作為普通函數(shù)執(zhí)行:
function  fn () {
    console.log (this)       // this === window
}
fn ();
  1. 作為對(duì)象屬性執(zhí)行:
var obj = {
    name : "A",
    printName : function () {
        console.log (this.name)  // this === obj
    }
}
obj.printName ()

4.call(), apply(), bind(): 這是重點(diǎn)晨汹!
三個(gè)函數(shù)都可以修改this的指向:

var name = "小明" , age = "17" 
var obj = { 
    name : "安妮", 
    objAge :this.age, 
    fun : function (like,dislike) { 
              console.log (this.name + "今年" + this.age 豹储,"喜歡吃" + like + "不喜歡吃" + dislike) ;
           } 
    } 
var a = { name : "Jay", age : 23 } 
obj.fun.call(a,"蘋果","香蕉") // Jay今年23 喜歡吃蘋果不喜歡吃香蕉 
obj.fun.apply(a,["蘋果","香蕉"]) // Jay今年23 喜歡吃蘋果不喜歡吃香蕉 
obj.fun.bind(a,"蘋果","香蕉")() // Jay今年23 喜歡吃蘋果不喜歡吃香蕉

首先 call,apply淘这,bind 第一個(gè)參數(shù)都是 this 指向的對(duì)象剥扣,call 和 apply 如果第一個(gè)參數(shù)指向 null 或 undefined 時(shí),那么 this 會(huì)指向 windows 對(duì)象铝穷。

call钠怯,apply,bind 的執(zhí)行方式如上例所示曙聂。call晦炊,apply 都是改變上下文中的 this,并且是立即執(zhí)行的宁脊。bind 方法可以讓對(duì)應(yīng)的函數(shù)想什么時(shí)候調(diào)用就什么時(shí)候調(diào)用断国。

閉包:

閉包的概念很抽象,看下面的例子你就會(huì)理解什么叫閉包了:

function a(){
  var n = 0;
  this.fun = function () {
    n++; 
    console.log(n);
  };
}
var c = new a();
c.fun();  //1
c.fun();  //2

閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)榆苞。在 js 中只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量稳衬。所以可以簡(jiǎn)單的理解為:定義在內(nèi)部函數(shù)的函數(shù)。

異步和單線程

我們先感受下異步坐漏。

console.log("start");
setTimeout(function () {
    console.log("medium");
}, 1000);
console.log("end");

使用異步后薄疚,打印的順序?yàn)?start-> end->medium。因?yàn)闆](méi)有阻塞赊琳。

為什么會(huì)產(chǎn)生異步呢街夭?

首先因?yàn)?js 為單線程,也就是說(shuō) CPU 同一時(shí)間只能處理一個(gè)事務(wù)躏筏。得按順序莱坎,一個(gè)一個(gè)處理。

如上例所示寸士,第一步:執(zhí)行第一行打印 “start”檐什;第二步:執(zhí)行 setTimeout碴卧,將其中的函數(shù)分存起來(lái),等待時(shí)間結(jié)束后執(zhí)行乃正;第三步:執(zhí)行最后一行住册,打印 “end”;第四部:處于空閑狀態(tài)瓮具,查看暫存中荧飞,是否有可執(zhí)行的函數(shù);第五步:執(zhí)行分存函數(shù)名党。

為什么 js 引擎是單線程叹阔?

js 的主要用途是與用戶互動(dòng),以及操作 DOM传睹,這決定它只能是單線程耳幢。例:一個(gè)線程要添加 DOM 節(jié)點(diǎn),一個(gè)線程要?jiǎng)h減 DOM 節(jié)點(diǎn)欧啤,容易造成分歧睛藻。

為了更好使用多 CPU,H5 提供了 web Worker 標(biāo)準(zhǔn)邢隧,允許 js 創(chuàng)建多線程店印,但是子線程受到主線程控制,而且不得操作 DOM倒慧。

任務(wù)列隊(duì)

單線程就意味著按摘,所有的任務(wù)都要排隊(duì),前一個(gè)結(jié)束纫谅,才會(huì)執(zhí)行后面的任務(wù)院峡。如果列隊(duì)是因?yàn)橛?jì)算量大,CPU 忙不過(guò)來(lái)系宜,倒也算了照激。但是更多的時(shí)候,CPU 是閑置的盹牧,因?yàn)?IO 設(shè)備處理得很慢俩垃,例如 ajax 讀取網(wǎng)絡(luò)數(shù)據(jù)。js 設(shè)計(jì)者便想到汰寓,主線程完全可以不管 IO 設(shè)備口柳,將其掛起,然后執(zhí)行后面的任務(wù)有滑。等后面的任務(wù)結(jié)束掉跃闹,在反過(guò)頭來(lái)處理掛起的任務(wù)。

好,我們來(lái)梳理一下:

1)所有的同步任務(wù)都在主線程上執(zhí)行望艺,行程一個(gè)執(zhí)行棧苛秕。

2)除了主線程之外,還存在一個(gè)任務(wù)列隊(duì)找默,只要一步任務(wù)有了運(yùn)行結(jié)果艇劫,就在任務(wù)列隊(duì)中植入一個(gè)時(shí)間。

3)主線程完成所有任務(wù)惩激,就會(huì)讀取列隊(duì)任務(wù)店煞,并將其執(zhí)行。

4)重復(fù)上面三步风钻。

只要主線程空了顷蟀,就會(huì)讀取任務(wù)列隊(duì),這就是 js 的運(yùn)行機(jī)制骡技,也被稱為 event loop(事件循環(huán))鸣个。

ES6 語(yǔ)法

ES6,強(qiáng)制開(kāi)啟嚴(yán)格模式!

let 和 const

作用域的概念:
全局作用域哮兰,函數(shù)作用域毛萌,塊作用域
<暫時(shí)性死區(qū)>

let和const不能重復(fù)定義苟弛;const聲明時(shí)必須賦值喝滞,let 不必須;

解構(gòu)賦值

解構(gòu)賦值的分類:
數(shù)組解構(gòu)賦值膏秫,對(duì)象解構(gòu)賦值右遭,字符串解構(gòu)賦值,
布爾值解構(gòu)賦值缤削,函數(shù)參數(shù)解構(gòu)賦值窘哈,數(shù)值解構(gòu)賦值

使用場(chǎng)景:

數(shù)組結(jié)構(gòu)賦值:

(1)默認(rèn)值:

  function fn(a, b=2){
  ...
  }

(2)變量交換

{
  let a = 1;
  let b = 2;
  [a, b] = [b, a];
  console.log(a,  b);
}

(3)獲取函數(shù)返回值

{
  function  f(){
    return [1,2,3,4];
  }
  let a,  b,  c,  d;
  [a,  b,c亭敢,d] = f();  //  接收所有值
  [a, , , b] = f();  // 接收指定值
  [a, ...b] = f(); //  分開(kāi)接收
}

對(duì)象解構(gòu)賦值:

{
  let o = {p:42,  q:true}
  let {p,q} = o;
  console.log(p,q);
}

(1)默認(rèn)值:

{
  let {a = 5, b = 10} = {a : 3}
  console.log(a,b) // 3, 5
}

(2)json對(duì)象取值 :特別常用的場(chǎng)景滚婉!

{
  let data = {
    title: 'abc',
    test: [{
      title:  'test',
       desc:  '描述'
    }]
  }
  // 聲明一個(gè)跟解構(gòu)對(duì)象格式一致的對(duì)象,來(lái)接收對(duì)象中的值
  let {title : esTitle,  test: [{title: cnTitle}]} = data;
  console.log(esTitle,  cnTitle);
}

正則擴(kuò)展:

構(gòu)造函數(shù)的變化帅刀、正則方法擴(kuò)展忙灼、u修飾符藤韵、y修飾符、s修飾符

es5中有一下兩種正則表達(dá)式寫法:

{
  // 兩個(gè)參數(shù)的情況,前面是匹配字符串
  let regex = new RegExp('xyz', 'i');
  // 一個(gè)參數(shù)的情況淮阐,前面是正則表達(dá)式
  let regex2 = new RegExp(/xyz/i); 
}

es6中,允許前面是正則表達(dá)式躺苦,而后面跟著修飾符的兩個(gè)參數(shù)的寫法:

{
  let regex3 = new RegExp(/xyz/ig, 'i'); // 但這種寫法后面的修飾符會(huì)覆蓋正則中的修飾符
  //  flags是es6中正則表達(dá)式的新的API源葫,輸出的結(jié)果是正則表達(dá)式的修飾符
  console.log(regex3.flags);  //  輸出的是 ' i '
}

y修飾符:

{
  let s = 'bbb_bb_b';
  let a1 = /b+/g;
  let a2 = /b+/y;
  console.log('one', a1.exec(s), a2.exec(s));
  console.log('two', a1.exec(s), a2.exec(s));
}

g和y修飾符的共同點(diǎn):都是全局匹配
不同點(diǎn):
g:可以不連續(xù)匹配,bbb_bb_b
y: 粘連模式,必須連續(xù)匹配b后面如果不是b了嘲恍,就不會(huì)繼續(xù)匹配
sticky可以查看是否開(kāi)啟了粘連模式:a1.sticky

u修飾符:

u修飾符足画,可以識(shí)別大于2個(gè)字節(jié)的字符;
es5中蛔钙,. 能匹配任何字符锌云,但是前提是小于兩個(gè)字節(jié)的
s修飾符:es6中還未實(shí)現(xiàn)

字符串?dāng)U展:(要安裝babel-polyfill)

處理unicode編碼大于0xffff的字符 => 用{}包裹起來(lái)
`\u20BB7` => `\u{20BB7}`
es5中:
獲取unicode編碼對(duì)應(yīng)的字符:fromCharCode()
獲取字符對(duì)應(yīng)的unicode編碼:charCodeAt();

es6中:
獲取unicode編碼對(duì)應(yīng)的字符:fromCharPoint();
獲取字符對(duì)應(yīng)的unicode編碼:codePointAt(); // 能獲取超過(guò)0xFFFF長(zhǎng)度的編碼吁脱,并正確顯示

es6中有一個(gè)字符串遍歷器接口桑涎,可以正確處理unicode編碼大于0xFFFF的字符串

{
  let str = '/u{20BB7}abc';
  for(let code of str){
    console.log('es6', code);
  }
  //let  of 可以正確遍歷出0xFFFF編碼長(zhǎng)度的字符串對(duì)應(yīng)的字符,不會(huì)出現(xiàn)亂碼
}

str.includes('x'):判斷字符串中是否包含了某個(gè)字符
str.startsWith('x'):判斷字符串是不是已某個(gè)字符開(kāi)始的
str.endsWith('x'):判斷字符串是不是已某個(gè)字符結(jié)束的
str.repeat(2):重復(fù)字符串

模板字符串(很重要):

`你好兼贡,${name}攻冷,歡迎你!`

padStart 和 padEnd (這兩個(gè)是ES7的草案遍希,需要babel-polyfill)

console.log('1'.padStart(2,  '0')); //  '01'
console.log('1'.padEnd(2,  '0')); //  '10'

標(biāo)簽?zāi)0澹?/h4>

1.怎么用等曼?

{
  let user = {
    name:   'list',
    info:  'hello world'
  };
  console.log(abc`i am ${user.name}, ${user.info}`);
   function abc(s,  v1,  v2){
    console.log(s,  v1,  v2);
    return s+v1+v2;
  }
}

2.在哪里用?
防止XSS攻擊凿蒜,和處理多語(yǔ)言轉(zhuǎn)換的時(shí)候用

string.raw:可以將所有的 ' \ ' 進(jìn)行了轉(zhuǎn)義禁谦,即在前面又加了一個(gè)
String.raw`Hi\n${1+2}` // Hi\n3

數(shù)值擴(kuò)展:

Number.isFinite() :判斷一個(gè)值是否有盡
Number.isNaN() :判斷一個(gè)數(shù)是不是NaN
Number.isInteger() :判斷是不是整數(shù) 2.0也是true

Number.MAX_SAFE_INTEGER // 最大安全數(shù)值
Number.MIN_SAFE_INTEGER // 最小安全數(shù)值

Number.isSafeInteger() // 判斷一個(gè)數(shù)字是不是在安全數(shù)的范圍內(nèi):
Math.trunc(4.1); //4 // 取小數(shù)的整數(shù)部分
Math.sign() // 結(jié)果只有-1, 0 废封,1州泊, NaN // 判斷是正數(shù),負(fù)數(shù)漂洋,還是零
Math.cbrt(8); // 2 // 計(jì)算立方根

還有三角函數(shù)方法遥皂,對(duì)數(shù)方法,都是es6新增的

數(shù)組擴(kuò)展:

Array.from
Array.of
copyWithin
find \ findIndex
fill
entries \ keys \ values
includes

函數(shù)擴(kuò)展:

1刽漂、參數(shù)默認(rèn)值

{
  function test(x, y = " 23 "){
    console.log(x,  y);
  }
  test(2) // 2  23
}
//  要注意默認(rèn)值后面不能再有沒(méi)有默認(rèn)值的參數(shù)變量
//  這就是錯(cuò)誤的
 function test(x, y = " 23 " , c)  ...

關(guān)于取值作用域的問(wèn)題:

{
  let x = 'test';
  function test(x, y=x){
    console.log(x,y);
  }
  test('kill'); //  kill kill
}

2演训、rest參數(shù)
把一系列的參數(shù)都轉(zhuǎn)換為數(shù)組,在你不確定有多少個(gè)參數(shù)的時(shí)候
注意贝咙,rest參數(shù)后面不能再有其他的參數(shù)

{
 function test( ...arg ){
   for (let v of arg){
     console.log(v);
   }
 }
 test(1,2,3,4); // 1 2 3 4
}

3样悟、擴(kuò)展運(yùn)算符
擴(kuò)展運(yùn)算符和rest運(yùn)算符是一個(gè)相反的作用
它的作用是:把一個(gè)數(shù)組拆成離散的值

{
  console.log(...[1,2,3]); // 1  2  3
  console.log('a', ...[1,2,3]) //  'a'  1  2  3
}

4、箭頭函數(shù)

{
  let arrow = v => v*2;
  //  等同于
  function arrow(v){
    return v*2;
  }

  // 無(wú)參數(shù)
  let arrow = () => v*3;
  //  等同于
  function arrow(){
    return v*3;
  }
}

5庭猩、this綁定
因?yàn)閠his在指向在箭頭函數(shù)和普通函數(shù)不一樣窟她,所以要考慮什么時(shí)候適合用箭頭函數(shù),什么時(shí)候不適合用箭頭函數(shù)眯娱;
this在普通函數(shù)中礁苗,指向函數(shù)被調(diào)用時(shí)的對(duì)象所在;
this在箭頭函數(shù)中徙缴,指向函數(shù)被定義時(shí)的對(duì)象所在试伙;
6嘁信、尾調(diào)用
區(qū)分特征:函數(shù)的最后一句話是不是函數(shù)

{
  function tail(x){
    console.log('tail',  x);
  }
  function fx(x){
    return tail(x);
  }
  fx(123);
}
//  尾調(diào)用的用處:提升(遞歸函數(shù),嵌套函數(shù))性能

對(duì)象(object)擴(kuò)展:

1疏叨、簡(jiǎn)介表示法:

{
  let o = 1;
  let k = 2;
  //  es5中聲明一個(gè)對(duì)象
  let es5 = {
    o:  o,
    k:  k
  };
  //  es6中潘靖,如果鍵值相同,可以這樣寫:
  let es6 = {
    o,
    k
  };
   
  //  如果對(duì)象里面有方法:
   let es5_method = {
    hello: function(){}
  };
  let es6_method = {
    hello(){}
  }
}

2蚤蔓、屬性表達(dá)式:

{
  let a = 'b';
  let es5_obj = {
    a: 'c',
    b: 'c'
  };
  // 對(duì)象的key值卦溢,可以是一個(gè)表達(dá)式,這叫做屬性表達(dá)式
  //  這里的 [a] = 'b';
  let es6_obj = {
    [a]:  'c'
  }
  console.log(es5_obj,  es6_obj);
}

3秀又、擴(kuò)展運(yùn)算符:
babel對(duì)它的支持不是很好,加了babel-polyfill還是會(huì)報(bào)錯(cuò)

{
  let {a, b, ...c} = {a: 'test', b:'re', c:'ccc', d:'ddd'};
  解析過(guò)后c的值應(yīng)該是:
  c: {
    c:'ccc',
    d:'ddd'
  }
}

4单寂、Object新增方法:

  //  新增API
  Object.is() 的功能和 ‘===’ 沒(méi)有區(qū)別,判斷兩個(gè)值是否相等
  Object.is(str, str); //判斷兩個(gè)字符串是否相等
  //  要注意吐辙,數(shù)組和對(duì)象是引用類型宣决,雖然都是空數(shù)組,但是引用的地址不同昏苏,所以:
  object.is([],[])  // 結(jié)果是false

  Object.assign() : 淺拷貝
  //  拷貝的是引用地址尊沸,不是值;
  //  只拷貝自身的屬性贤惯,不會(huì)拷貝繼承的屬性洼专,和不可枚舉的屬性
  Object.assign({a:''a}, {b:'b'}); // {a: 'a', b: 'b'}

  Object.entries() : 方法返回一個(gè)給定對(duì)象自身可枚舉屬性的鍵值對(duì)數(shù)組,
  其排列與使用 for...in 循環(huán)遍歷該對(duì)象時(shí)返回的順序一致
  區(qū)別在于 for-in 循環(huán)也枚舉原型鏈中的屬性

  const object1 = { foo: 'bar', baz: 42 };
  console.log(Object.entries(object1)[1]);
  // expected output: Array ["baz", 42]

  const object2 = { 0: 'a', 1: 'b', 2: 'c' };
  console.log(Object.entries(object2)[2]);
  // expected output: Array ["2", "c"]

  const result = Object.entries(object2).sort((a, b) => a - b);
  console.log(Object.entries(result)[1]);
  // expected output: Array ["1", Array ["1", "b"]]

Symbol:

1孵构、Symbol的概念:
Symbol可提供一個(gè)獨(dú)一無(wú)二的值

let a1 = Symbol();
let a2 = Symbol();
console.log(a1 === a2); // false

// 定義了以后屁商,將來(lái)要取回來(lái),就用這種方式
let a3 = Symbol.for('a3'); // for中的a3是一個(gè)key值浦译,如果在全局注冊(cè)過(guò)棒假,就返回這個(gè)值溯职,如果沒(méi)有注冊(cè)過(guò)精盅,就注冊(cè)一個(gè)獨(dú)一無(wú)二的值

let a4 = Symbol.for('a3');
let a5 = Symbol.for('a3');
console.log(a4 === a5); // true

2、Symbol的作用:

{
  let a1 = Symbol.for('abc');
  let obj = {
    [a1] : '123',
    'abc' : 234,
    'c' : 456
  }
  console.log('obj', obj); // {Symbol(abc): '123', 'abc': 234, 'c': 456}

要注意一點(diǎn):用Symbol做key值谜酒,通過(guò)for...inlet...of是拿不到key值的

  for(let [key, value] of Object.entries(obj)){
    console.log(key, value) ; //拿不到Symbol(abc)和其值
  }

這時(shí)可以通過(guò)Object.getOwnPropertySymbols()拿到Symbol定義的值,但是它拿不到其他的值

  Object.getOwnPropertySymbols(obj).forEach(function(item){
    console.log(obj[item]); // 123
  })

如果想拿到所有的值叹俏,可以通過(guò)Reflect.ownKeys()拿到

  Reflect.ownKeys(obj).forEach(function(item){
    console.log('ownKeys', item, obj[item])
  })

ES6數(shù)據(jù)結(jié)構(gòu):

Set:

1、類似數(shù)組僻族,但集合中的元素不能重復(fù)

{
  let list = new Set();
  list.add(5); // 添加
  list.add(7);
 
  list.size // 獲取set中的長(zhǎng)度(個(gè)數(shù))

  // 初始化set中的值
  let arr = [1,2,3,4,5];
  let list1 = new Set(arr);

  // 不可重復(fù)性
  let list2 = new Set();
  list2.add(1);
  list2.add(2);
  list2.add(1);

  console.log(list2); // 不會(huì)打印出后面重復(fù)的元素粘驰,也不會(huì)報(bào)錯(cuò),這可以用于去重

  這里要注意述么,Set不會(huì)做數(shù)據(jù)類型的轉(zhuǎn)換蝌数,即 2 和 '2' 在set看來(lái)是不一樣的

2、Set實(shí)例的幾個(gè)方法和屬性值:
add():添加某個(gè)元素
delete():刪除某個(gè)元素
clear():清空所有元素
has():判斷有沒(méi)有某個(gè)元素度秘,返回布爾類型
size:返回集合中元素的個(gè)數(shù)

3顶伞、Set實(shí)例的遍歷:

// for...of
for(let key of list.keys()){
  console.log('keys', key);
}
for(let value of list.values()){
  console.log('value', value);
}
for(let value of list){
  console.log('value', value);
}
for(let [key,  value] of list.entries()){
  console.log('entries', key, value);
}
key和value的值一樣,都是元素的名稱;
不加.key或者.values唆貌,也是可以的

Map

1 滑潘、類似Object,key可以是任意類型(object中key只能是字符串)

{
  let map = new Map();
  let arr = ['123'];
  
  map.set(arr, 456); // Map可以用數(shù)組作為key(或其他任意類型)哦~
  console.log('map', map, map.get(arr));
  // 結(jié)果:map  Map{["123"] => 456}  456

  // 初始化Map中的值
  let map1 = new Map([['a', 123], ['b', 456]]);  // 一定要注意這種格式
}

2锨咙、Map實(shí)例的幾個(gè)方法和屬性值:
get():獲取某個(gè)元素
set():添加某個(gè)元素
delete():刪除某個(gè)元素
clear():清空所有元素
has():判斷有沒(méi)有某個(gè)元素语卤,返回布爾類型
size:返回集合中元素的個(gè)數(shù)

3、Map 結(jié)構(gòu)轉(zhuǎn)為數(shù)組結(jié)構(gòu)
比較快速的方法是結(jié)合使用擴(kuò)展運(yùn)算符(...)

let map = new Map([
   [1, 'one'],
   [2, 'two'],
   [3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]

4酪刀、Map 循環(huán)遍歷
Map 原生提供三個(gè)遍歷器:
keys():返回鍵名的遍歷器粹舵。
values():返回鍵值的遍歷器。
entries():返回所有成員的遍歷器骂倘。

let map = new Map([
 ['F', 'no'],
 ['T', 'yes'],
]);

for (let key of map.keys()) {
 console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
 console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
 console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
 console.log(key, value);
}

// 等同于使用map.entries()
// 這個(gè)例子可以看出:Map 結(jié)構(gòu)的默認(rèn)遍歷器接口(Symbol.iterator 屬性)齐婴,就是 entries 方法。
for (let [key, value] of map) {
 console.log(key, value);
}

5稠茂、Map 獲取第一個(gè)元素

const m = new Map();
m.set('key1', {})
m.set('keyN', {})

console.log(m.entries().next().value); // [ 'key1', {} ]

// 獲取第一個(gè)key
console.log(m.keys().next().value); // key1

// 獲取第一個(gè)value
console.log(m.values().next().value); // {}

Map與Array的對(duì)比 柠偶、 Set與Array的對(duì)比

數(shù)據(jù)結(jié)構(gòu)橫向?qū)Ρ龋霾枪亍⒉橛盏!⒏摹h

// Map與Array
let map = new Map();
let array = [];

// 增
map.set('t',  1);
array.push({t:1});
console.info('may-array', map,  array);

// 查
let map_exist = map.has('t');
let array_exist = array.find(item=>item.t);
console.info('may-array',  map_exist, array_exist); //map返回的是布爾值电爹,數(shù)組返回的是這個(gè)值

// 改
map.set('t',  2);
array.forEach(item => item.t ? item.t = 2 : ' '); // 先要判斷是否存在蔫仙,才執(zhí)行
console.info('may-array-modify',  map, array); 

// 刪
map.delete('t');
let index = array.findIndex(item => item.t); // 查找每一個(gè)元素中帶t的元素
array.splice(index, 1);
console.info('may-array-del', map, array); 


// Set與Array
let map = new Set();
let array = [];

// 增
set.add({t :1});
array.push({t:1});
console.info('set-array', set,  array);

// 查
let set_exist = set.has({t:1}); //這里應(yīng)該是填入保存的地址,這樣寫會(huì)報(bào)錯(cuò) 
let array_exist = array.find(item=>item.t);
console.info('may-array',  set_exist, array_exist); //set返回的是布爾值丐箩,數(shù)組返回的是這個(gè)值

// 改
set.forEach(item => item.t ? item.t = 2: ' ');
array.forEach(item => item.t ? item.t = 2 : ' '); // 先要判斷是否存在摇邦,才執(zhí)行
console.info('may-array-modify',  set, array); 

// 刪
set.forEach(item => item.t ? set.delete(item) : ' ');
let index = array.findIndex(item => item.t); // 查找每一個(gè)元素中帶t的元素
array.splice(index, 1);
console.info('may-array-del', set, array); 

Proxy和Reflect

Proxy:可以用來(lái)代理對(duì)對(duì)象的訪問(wèn)
Reflect 的方法和用法和Proxy一模一樣。只是使用方式不一樣屎勘,Proxy需要new施籍,而Reflect直接使用(Reflect.get())

// Proxy的幾個(gè)常用方法:
// 其中target為代理對(duì)象參數(shù),原本obj的屬性概漱,它都有丑慎,實(shí)際傳入的代理對(duì)象,是proxy的實(shí)例化對(duì)象

// 獲取對(duì)象屬性
get(target, key){}

// 設(shè)置對(duì)象屬性
set(target, key, value){}

// 判斷對(duì)象是否有某個(gè)屬性,可以攔截判斷瓤摧,欺騙訪問(wèn)
has(target, key){}

// 刪除對(duì)象屬性:攔截delete竿裂,如果不想用戶刪除對(duì)象中的某些屬性
deleteProperty(target, key){}

// 攔截Object.keys, Object.getOwnPropertySymbols, Object.getOwnPropertyNames
ownKeys(){}

完整示例,及Proxy在開(kāi)發(fā)中的使用:

{
  let obj = {
    time: '2017-03-11',
    name: 'net',
    _r: 123
  }

  let monitor = new Proxy(obj, {
    // 攔截對(duì)象屬性的讀取
    get(target, key) {
      return target[key].replace('2017', '2018')
    },
    // 攔截對(duì)象設(shè)置屬性
    set(target, key, value) {
      if (key === 'name') {
        return (target[key] = value)
      } else {
        return target[key]
      }
    },
    // 攔截key in object操作
    has(target, key) {
      if (key === 'name') {
        return target[key]
      } else {
        return false
      }
    },
    // 攔截delete
    deleteProperty(target, key) {
      if (key.indexOf('_') > -1) {
        delete target[key]
        return true
      } else {
        return target[key]
      }
    },
    // 攔截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
    ownKeys(target) {
      return Object.keys(target).filter(item => item != 'time')
    }
  })

  console.log('get', monitor.time)

  monitor.time = '2018'
  monitor.name = 'mukewang'
  console.log('set', monitor.time, monitor)

  console.log('has', 'name' in monitor, 'time' in monitor)

  // delete monitor.time;
  // console.log('delete',monitor);
  //
  // delete monitor._r;
  // console.log('delete',monitor);
  console.log('ownKeys', Object.keys(monitor))
}

{
  let obj = {
    time: '2017-03-11',
    name: 'net',
    _r: 123
  }

  console.log('Reflect get', Reflect.get(obj, 'time'))
  Reflect.set(obj, 'name', 'mukewang')
  console.log(obj)
  console.log('has', Reflect.has(obj, 'name'))
}

// proxy常用的情景:對(duì)數(shù)據(jù)類型進(jìn)行校驗(yàn)照弥,實(shí)現(xiàn)與業(yè)務(wù)解耦
{
  function validator(target, validator) {
    return new Proxy(target, {
      _validator: validator,
      set(target, key, value, proxy) {
        // 判斷是否有對(duì)應(yīng)可操作的屬性
        if (target.hasOwnProperty(key)) {
          let va = this._validator[key]
          if (!!va(value)) {
            // 如果所訪問(wèn)的屬性有值腻异,則執(zhí)行代理操作(這里僅以Set為例),將代理返回到真實(shí)對(duì)象proxy
            return Reflect.set(target, key, value, proxy)
          } else {
            throw Error(`不能設(shè)置${key}到${value}`)
          }
        } else {
          // 如果沒(méi)有这揣,則拋出異常
          throw Error(`${key} 不存在`)
        }
      }
    })
  }

  // 模擬一個(gè)條件類
  const personValidators = {
    name(val) {
      return typeof val === 'string'
    },
    age(val) {
      return typeof val === 'number' && val > 18
    },
    mobile(val) {}
  }

  // 模擬一個(gè)被代理的類
  class Person {
    constructor(name, age) {
      this.name = name
      this.age = age
      this.mobile = '1111'
      // 這里的this悔常,指的是Person的實(shí)例化對(duì)象
      // Person類返回一個(gè)validator方法敢会,validator方法返回一個(gè)proxy代理,
      //也就是說(shuō)訪問(wèn)Person的實(shí)例这嚣,會(huì)經(jīng)過(guò)validator中的proxy代理鸥昏,進(jìn)行篩選過(guò)濾
      return validator(this, personValidators)
    }
  }

  // 實(shí)例化Person類
  const person = new Person('lilei', 30)
  console.info(person)
  person.name = 'Han mei mei'
  console.info(person)
}

ES6中的 類和對(duì)象


基本語(yǔ)法 | 類的繼承 | 靜態(tài)方法 | 靜態(tài)屬性 | getter | setter

1、基本定義和生成實(shí)例

 class Parent{
    // 定義類中的構(gòu)造函數(shù)
    constructor(name = 'zk'){
      this.name = name;
    }
}

let v_parent = new Parent('z');
console.log('構(gòu)造函數(shù)實(shí)例', v_parent)

2姐帚、類的繼承
定義一個(gè)父類吏垮,子類來(lái)繼承

class parent{
  constructor(name='zk'){
    this.name = name;
  }
}

class child extends parent{
    // 繼承父類的子類,可以覆蓋 / 把實(shí)際需要的參數(shù)傳遞給 父類的參數(shù)
    constructor(name = 'child'){
        super(name); //要覆蓋父類哪個(gè)參數(shù)罐旗,就傳遞哪個(gè)參數(shù)名膳汪;如果不傳,則默認(rèn)全部使用父類參數(shù)的默認(rèn)值
        // 如果子類還要增加自己的屬性九秀,一定要放在super之后
        this.type = 'child';
    }
}

console.log('繼承', new Child('hello'));

getter 和 setter

class Parent{
  constructor(name = 'zk'){
    this.name = name;    
  }
  // getter
  // 注意遗嗽,get后面的是屬性,不是方法鼓蜒!
  get longName(){
    return 'mk' + this.name
  }
  // setter
  set longName(value){
    this.name = value
  }
}

// get操作
let v = new Parent();
console.log('getter',v.longName);
// set操作
v.longName = 'hello'
console.log('setter',v.longName);

靜態(tài)方法 的定義和使用

// static只是用來(lái)定義靜態(tài)方法痹换,不是用來(lái)定義靜態(tài)屬性用的!
// 靜態(tài)屬性是直接在類上定義的都弹。
class Parent{
  constructor(name = 'zk'){
    this.name = name;
  }
  static tell(){
    console.log('tell')
  }
}

Parent.tell();  // tell

靜態(tài)屬性的定義和使用(靜態(tài)屬性沒(méi)有關(guān)鍵詞(暫時(shí)沒(méi)有娇豫,雖然有提案))

class Parent{
  constructor(name = 'zk'){
    this.name = name;
  }
  static tell(){
    console.log('tell')
  }
}

Parent.type = 'type'; // 直接在類上定義屬性,不用new實(shí)例
console.log('靜態(tài)屬性', Parent.type);

思考:什么樣的場(chǎng)景下適合用靜態(tài)方法畅厢,什么樣的場(chǎng)景下適合用靜態(tài)屬性?

Promise
什么是異步 | Promise的作用 |  Promise的基本用法

現(xiàn)在的js異步編程的方式有一下幾種:
1.回調(diào)函數(shù) 2.事件監(jiān)聽(tīng) 3.發(fā)布/訂閱 4.Promise 對(duì)象 5.Generator

之前有兩種方式:通過(guò)回調(diào)函數(shù)冯痢、通過(guò)觸發(fā)方法
而現(xiàn)在的Promise更適合異步操作

// 通過(guò)模擬ajax請(qǐng)求,來(lái)使用promise實(shí)現(xiàn)異步操作
{
  let ajax = function(){
    console.log('執(zhí)行1');
    return new Promise(function(resolve, reject){
      setTimeout(function(){
        resolve()
      }, 1000)                            
    })
  }

  ajax().then(function(){
    console.log("然后執(zhí)行2");
  })
}

如果是多個(gè)異步操作呢框杜?

// 在then()中繼續(xù)使用promise即可
{
  let ajax=function(){
    console.log('執(zhí)行3');
    return new Promise(function(resolve, reject){
      setTimeout(function () {
        resolve()
      }, 1000);
    })
  };

  ajax().then(function(){
    return new Promise(function(resolve, reject){
      setTimeout(function () {
        resolve()
      }, 2000);
    });
  }).then(function(){
    console.log('timeout3');
  })
}

Promise 提供 then 方法加載回調(diào)函數(shù)浦楣,catch方法捕捉執(zhí)行過(guò)程中拋出的錯(cuò)誤。catch捕獲出錯(cuò)信息咪辱,err就是具體的出錯(cuò)信息

{
  let ajax=function(num){
    console.log('執(zhí)行4');
    return new Promise(function(resolve,reject){
      if(num>5){
        resolve()
      }else{
        throw new Error('出錯(cuò)了')
      }
    })
  }

  ajax(6).then(function(){
    console.log('log',6);
  }).catch(function(err){
    console.log('catch',err);
  });
  
  ajax(3).then(function(){
    console.log('log',3);
  }).catch(function(err){
    console.log('catch',err);
  });
}

promise的高級(jí)用法
promise.all promise.race(賽跑)

情景1:保證所有圖片都加載完成振劳,再顯示頁(yè)面

  {
  // 所有圖片加載完再添加到頁(yè)面
  function loadImg(src){
    return new Promise((resolve,reject)=>{
      let img=document.createElement('img');
      img.src=src;
      img.onload=function(){
        resolve(img);
      }
      img.onerror=function(err){
        reject(err);
      }
    })
  }

  function showImgs(imgs){
    imgs.forEach(function(img){
      document.body.appendChild(img);
    })
  }

  Promise.all([
    loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),
    loadImg('http://i4.buimg.com/567751/2b07ee25b08930ba.png'),
    loadImg('http://i2.muimg.com/567751/5eb8190d6b2a1c9c.png')
  ]).then(showImgs)

}

情景2:同一個(gè)資源有多個(gè)來(lái)源,只要一個(gè)來(lái)源請(qǐng)求成功梧乘,就添加到頁(yè)面澎迎,其他的忽略不管(先到先得)

{
  // 有一個(gè)圖片加載完就添加到頁(yè)面
  function loadImg(src){
    return new Promise((resolve,reject)=>{
      let img=document.createElement('img');
      img.src=src;
      img.onload=function(){
        resolve(img);
      }
      img.onerror=function(err){
        reject(err);
      }
    })
  }

  function showImgs(img){
    let p=document.createElement('p');
    p.appendChild(img);
    document.body.appendChild(p)
  }

  Promise.race([
    loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),
    loadImg('http://i4.buimg.com/567751/2b07ee25b08930ba.png'),
    loadImg('http://i2.muimg.com/567751/5eb8190d6b2a1c9c.png')
  ]).then(showImgs)

}

Iterator 和 for...of 循環(huán)

什么是Iterator接口 | Iterator的基本用法 | for...of 循環(huán)

for...of只能遍歷數(shù)組庐杨,如果其他數(shù)據(jù)格式也想通過(guò)for...of來(lái)遍歷选调,前提條件是它本身自帶Iterator接口,如果沒(méi)有灵份,則需要為其自定義Iterator接口

數(shù)組支持遍歷仁堪,因?yàn)樗旧硪呀?jīng)封裝好了Iterator接口:

// arr本身有一個(gè)Iterator的方法,通過(guò)arr[Symbol.iterator]()可以觸發(fā)填渠,這個(gè)方法會(huì)返回一個(gè)對(duì)象弦聂,對(duì)象有一個(gè)next()方法鸟辅,用來(lái)輸出每次遍歷的結(jié)果
{
  let arr=['hello','world'];
  let map=arr[Symbol.iterator]();
  console.log(map.next());
  console.log(map.next());
  console.log(map.next());

  結(jié)果:
  Object{value: 'hello', done: false}
  Object{value: 'world', done: false}
  Object{value: undefined, done: true}
}

如果其他沒(méi)有Iterator接口的數(shù)據(jù)類型,也想實(shí)現(xiàn)遍歷莺葫,則可以自定義Iterator接口

{
  let obj={
    start:[1,3,2],
    end:[7,9,8],
    [Symbol.iterator](){
      let self=this;
      let index=0;
      let arr=self.start.concat(self.end);
      let len=arr.length;
      return {
        // 注意:這里要返回一個(gè)對(duì)象匪凉,對(duì)象中包含next()方法,而且返回值有兩個(gè)捺檬,value和done
        next(){
          if(index<len){
            return {
              // 注意每遍歷一次再层,index都要+1
              value:arr[index++],
              done:false
            }
          }else{
            return {
              value:arr[index++],
              done:true
            }
          }
        }
      }
    }
  }
  // 本身for...of只能用來(lái)遍歷數(shù)組,但通過(guò)為obj自定義了Iterator接口堡纬,從而使for...of也可以遍歷obj
  for(let key of obj){
    console.log(key);
  }
}

說(shuō)到for...of聂受,就來(lái)回顧一下js中,數(shù)據(jù)的操作方法 和 幾個(gè)循環(huán)的區(qū)別:

數(shù)組的操作方法:
會(huì)改變?cè)瓟?shù)組的有:pop push unshift shift splice reverse sort
不會(huì)改變?cè)瓟?shù)組的有:indexOf lastIndexof concat slice

其他常用操作方法:
es5的有:forEach filter map some every reduce
es6的有:find includes

幾個(gè)循環(huán)方法及其區(qū)別:
forEach烤镐、for蛋济、for...in、for..of

1炮叶、for (編程式)

let arr = [1,2,3,4,5];
for(let i = 0;i<arr.length;i++){
  console.log(arr[i]);
}

2碗旅、forEach (聲明式:不關(guān)心如何實(shí)現(xiàn))

// 不支持return,它會(huì)遍歷到結(jié)束
let arr = [1,2,3,4,5];
arr.forEach(function(item){
  console.log(item);
})

3镜悉、for...in (平時(shí)不用)

支持return扛芽,但一般不用它遍歷數(shù)組
1- 因?yàn)樗饕皇菙?shù)字,是字符串积瞒;
2- 因?yàn)閒or...in還會(huì)遍歷出數(shù)組的私有屬性川尖,會(huì)影響length的準(zhǔn)確性
let arr = [1,2,3,4,5];
arr.b = '100';
for(let key in arr){
  // key會(huì)變成字符串類型
  console.log(key) // 會(huì)把私有屬性b也遍歷出來(lái)
}

4、for...of (es6語(yǔ)法)

// 用于遍歷數(shù)組
// 既可以return茫孔,又不會(huì)遍歷出私有屬性
let arr = [1,2,3,4,5];
for(let value of arr){
  console.log(value);
}

// 如果想讓for...of遍歷對(duì)象叮喳,不僅可以通過(guò)Iterator添加接口的方式,也可以通過(guò)Object.keys()來(lái)變通實(shí)現(xiàn)
let obj = {
  school: 'zk',
  age: 9
}
for(let value of Object.keys(obj)){
  console.log(obj[value])
}

filter的用法:

是否操作原數(shù)組:不操作
返回結(jié)果:過(guò)濾后的新數(shù)組
回調(diào)函數(shù)返回結(jié)果:如果返回true缰贝,則將這一項(xiàng)放入新數(shù)組中

// 如果想篩選(刪除)某些項(xiàng)馍悟,可以用filter
{
  let arr = [1,2,3,4,5];
  let newArr = arr.filter(function(item){
    return item>2 && item<5;
  });
  console.log(newArr); // [3,4]
}

map(映射)的用法:

將原有的數(shù)組映射成一個(gè)新的數(shù)組

是否操作原數(shù)組:不操作
返回結(jié)果:映射后的新數(shù)組
回調(diào)函數(shù)返回結(jié)果:回調(diào)函數(shù)中返回什么,這一項(xiàng)就是什么

// 可以用于將json返回值剩晴,拼接成dom元素锣咒,即更新某些項(xiàng),可以用map
{
  let arr1 = [1,2,3];
  let newArr1 = arr1.map(function(item){
    return `<li>${item}</li>`
  });
  console.log(newArr1.join('')); // <li>1</li><li>2</li><li>3</li>
}

includes的用法:

看數(shù)組中是否包含某個(gè)值

是否操作原數(shù)組:不操作
返回結(jié)果:判斷結(jié)果布爾值
回調(diào)函數(shù)返回結(jié)果:包含則返回true赞弥,否則返回false

{
  let arr3 = [1,2,3,4,5,6];
  console.log(arr3.includes(5));
}

find的用法:

找到數(shù)組中的某個(gè)值

是否操作原數(shù)組:不操作
返回結(jié)果:判斷找到的值
回調(diào)函數(shù)返回結(jié)果:回調(diào)中返回true毅整,表示找到了,找到后即停止遍歷绽左;找不到返回undefined

{
  let arr4 = [1,2,3,4,5,55,6];
  let result = arr4.find(function(item, index){
    return item.toString().indexOf(5) > -1
  })
  console.log(result);
}

some的用法:

找到true后停止悼嫉,返回true ;類似 “或”

是否操作原數(shù)組:不操作
返回結(jié)果:判斷數(shù)組中是否存在某值符合條件
回調(diào)函數(shù)返回結(jié)果:判斷有一個(gè)條件成立(或存在某值)拼窥,就返回true戏蔑,并立即停止

{
  let arr4 = [1,2,3,4,5];
  let result = arr4.some(function(item, index){
    return item.toString().indexOf(5) > -1
  })
  console.log(result);
}

every的用法:

找到false后停止蹋凝,返回false蚣抗;類似 “與”

是否操作原數(shù)組:不操作
返回結(jié)果:判斷數(shù)組中是否所有的值都符合條件
回調(diào)函數(shù)返回結(jié)果:判斷一旦有一個(gè)條件不符合(或不存在某值)感凤,就返回false,并立即停止

{
  let arr4 = [1,2,3,4,5,55,6];
  let result = arr4.every(function(item, index){
    return item.toString().indexOf(5) > -1
  })
  console.log(result);
}

reduce(翻譯:收斂闻葵,使還原情龄,使變?nèi)?

有四個(gè)參數(shù)(prev, next, index, item)
用法:將上一次的結(jié)果作為下一次的第一個(gè)伐割,繼續(xù)和后面的數(shù)據(jù)進(jìn)行操作
即1和2的結(jié)果,與3操作刃唤,他們的結(jié)果再與4操作隔心,以此類推

是否操作原數(shù)組:不操作
返回結(jié)果:返回疊加后的結(jié)果
回調(diào)函數(shù)返回結(jié)果:返回回調(diào)函數(shù)返回的結(jié)果
`

{
  let arr4 = [1,2,3,4,5];
  //第一次 prev:代表數(shù)組的前一項(xiàng);next:代表數(shù)組的第二項(xiàng)
  //第二次 prev:是數(shù)組的上一次返回值尚胞;next:是數(shù)組的第三項(xiàng)
  //如果沒(méi)有返回值硬霍,則第二次的prev便是undefined
  let reduce = arr4.reduce(function(prev, next, index, item){
    // item為原數(shù)組
    console.log(arguments);
    // return 100;
  })
  console.log(reduce );
}

應(yīng)用舉例:
1、計(jì)算多個(gè)商品的單價(jià)和數(shù)量的價(jià)格總和笼裳;
2唯卖、將二維數(shù)組,變成一維數(shù)組躬柬;

// 應(yīng)用1:
let sum2 = [{price:30,count:2},{price:30,count:3},{price:30,count:4} ].reduce(function(prev, next){
  console.log(prev, next)
  return prev+next.price*next.count 
}, 0); // 這個(gè)0拜轨,是默認(rèn)執(zhí)行第一次的prev(即默認(rèn)第一項(xiàng)的值)

// reduce有兩個(gè)參數(shù):第一個(gè)是方法,第二個(gè)是默認(rèn)第一項(xiàng)
// 應(yīng)用2:
let arrs = [[1,2,3],[4,5,6],[7,8,9]];
let flat = arrs.reduce(function(prev,next){
  return prev.concat(next);
});
console.log(flat); // [1,2,3,4,5,6,7,8,9] 

Generator(發(fā)生器)

異步編程解決方案(相對(duì)promise更高級(jí))
基本概念 | next函數(shù)的用法 | yield*的用法

Generator是什么允青?
舉例來(lái)說(shuō)橄碾,讀取文件的協(xié)程寫法如下。

functionasnycJob(){
  // ...其他代碼
  var f=yield readFile( fileA);
  // ...其他代碼
}

上面代碼的函數(shù) asyncJob 是一個(gè)協(xié)程颠锉,它的奧妙就在其中的 yield 命令法牲。它表示執(zhí)行到此處,執(zhí)行權(quán)將交給其他協(xié)程琼掠。也就是說(shuō)拒垃,yield命令是異步兩個(gè)階段的分界線。

協(xié)程遇到 yield 命令就暫停瓷蛙,等到執(zhí)行權(quán)返回悼瓮,再?gòu)臅和5牡胤嚼^續(xù)往后執(zhí)行。它的最大優(yōu)點(diǎn)艰猬,就是代碼的寫法非常像同步操作横堡,如果去除yield命令,簡(jiǎn)直一模一樣姥宝。

以上舉例轉(zhuǎn)載自:http://www.reibang.com/p/56ec5c113f1b翅萤,如要了解關(guān)于Generator,可參看腊满。

Generator 函數(shù)是協(xié)程在 ES6 的實(shí)現(xiàn)套么,最大特點(diǎn)就是可以交出函數(shù)的執(zhí)行權(quán)(即暫停執(zhí)行)。

Generator包含next和yield碳蛋,一旦步驟遇到y(tǒng)ield或return胚泌,就停止,遇到next就進(jìn)行下一步肃弟,直到結(jié)束

Generator函數(shù)定義:

// 需要babel-polyfill支持
{
  // genertaor基本定義
  let tell=function* (){
    yield 'a';
    yield 'b';
    return 'c'
  };

  let k=tell();

  console.log(k.next());
  console.log(k.next());
  console.log(k.next());
  console.log(k.next());
}

為其他數(shù)據(jù)結(jié)構(gòu)部署Iterator接口玷室,實(shí)現(xiàn)遍歷,不光可以按照之前Iterator接口手動(dòng)實(shí)現(xiàn)的寫法笤受,還可以使用Generator穷缤。

Generator就是一個(gè)遍歷器生成接口

Generator 應(yīng)用,實(shí)現(xiàn)遍歷器接口(一種應(yīng)用情景)
{
  let obj={};
  obj[Symbol.iterator]=function* (){
    yield 1;
    yield 2;
    yield 3;
  }

  for(let value of obj){
    console.log('value',value);
  }
}

Generator最大的優(yōu)勢(shì) : 狀態(tài)機(jī)
比如一個(gè)對(duì)象只有三種狀態(tài)箩兽,A,B,C 津肛,他們會(huì)在這三種狀態(tài)中循環(huán)出現(xiàn)。

{
  let state = function* (){
    // 因?yàn)橐恢倍际莟rue汗贫,所以會(huì)一直循環(huán)
    while(1){
      yield 'A';
      yield 'B';
      yield 'C';
    }
  }
  let status = state();
  console.log(status.next());// Object {value: 'A',  done: false}
  console.log(status.next());// Object {value: 'B',  done: false}
  console.log(status.next());// Object {value: 'C',  done: false}
  console.log(status.next());// Object {value: 'A',  done: false}
  console.log(status.next());// Object {value: 'B',  done: false}
  ...
}

async...await 語(yǔ)法是Generator函數(shù)的一個(gè)語(yǔ)法糖

{
  let state=async function (){
    while(1){
      await 'A';
      await 'B';
      await 'C';
    }
  }
  let status=state();
  console.log(status.next());
  console.log(status.next());
  console.log(status.next());
  console.log(status.next());
  console.log(status.next());
}

Generator的應(yīng)用:
1身坐、剩余抽獎(jiǎng)次數(shù)邏輯

{
  let draw=function(count){
    //具體抽獎(jiǎng)邏輯
    console.info(`剩余${count}次`)
  }

  let residue=function* (count){
    while (count>0) {
      count--;
      yield draw(count);
    }
  }

  let star=residue(5);
  let btn=document.createElement('button');
  btn.id='start';
  btn.textContent='抽獎(jiǎng)';
  document.body.appendChild(btn);
  document.getElementById('start').addEventListener('click',function(){
    star.next();
  },false)
}

2、長(zhǎng)輪巡

{
  // 長(zhǎng)輪詢
  let ajax=function* (){
    yield new Promise(function(resolve,reject){
      //模擬每200毫秒請(qǐng)求一次
      setTimeout(function () {
        resolve({code:0})
      }, 200);
    })
  }

  let pull=function(){
    // generator實(shí)例
    let genertaor=ajax();
    let step=genertaor.next();
    // step.value就是Promise實(shí)例
    step.value.then(function(d){
      if(d.code!=0){
        setTimeout(function () {
          console.info('wait');
          pull()
        }, 1000);
      }else{
        console.info(d);
      }
    })
  }

  pull();
}

Decorator(修飾器)[針對(duì)類]

基本概念 :
修飾器是一個(gè)函數(shù)落包,用來(lái)修改類的行為(或擴(kuò)展類的功能)

使用準(zhǔn)備:
1部蛇、除了babel-polyfill,還需要安裝一個(gè)插件包:
babel-plugin-transform-decorators-legacy

2咐蝇、修改.babelrc文件
{ "presets":["es2015"], "plugins":["transform-decorators-legacy"] }
3涯鲁、再重啟服務(wù)

基本用法:
聲明函數(shù)體:

{
  // 聲明一個(gè)修飾器
  let readonly = function(target, name, descriptor){
    descriptor.writable = false; //描述是否可寫
    return descriptor
  }
}

使用(在類的里面):

{
  class Test{
    @readonly
    time(){
      return '2018-03-09'
    }
  }
}

let test = new Test();
// 嘗試修改類
test.time = function(){
  console.log('reset time');
}

console.log(test.time());

//會(huì)報(bào)錯(cuò)提示無(wú)法修改只讀屬性time
Uncaught TypeError:  Cannot assign to read only property 'time' of object '#<Test>'

在類的外面使用:

{
  let typename=function(target,name,descriptor){
    target.myname='hello'; // target指的就是類本身
  }

  @typename
  class Test{

  }

  console.log('類修飾符',Test.myname);
  // 第三方庫(kù)修飾器的js庫(kù):core-decorators; 
  // 可以通過(guò)npm安裝:npm install core-decorators
  // import 進(jìn)來(lái)以后,直接就可以@使用
}

Decorator的應(yīng)用:
做埋點(diǎn)有序,日志統(tǒng)計(jì)
把埋點(diǎn)系統(tǒng)抽離出來(lái)撮竿,成為一個(gè)可復(fù)用的模塊

{
  let log=(type)=>{
    return function(target,name,descriptor){
      let src_method=descriptor.value;
      descriptor.value=(...arg)=>{
        src_method.apply(target,arg);
        console.info(`log ${type}`);
      }
    }
  }

  class AD{
    @log('show')
    show(){
      console.info('ad is show')
    }
    @log('click')
    click(){
      console.info('ad is click');
    }
  }

  let ad=new AD();
  ad.show();
  ad.click();
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市笔呀,隨后出現(xiàn)的幾起案子幢踏,更是在濱河造成了極大的恐慌,老刑警劉巖许师,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件房蝉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡微渠,警方通過(guò)查閱死者的電腦和手機(jī)搭幻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)逞盆,“玉大人檀蹋,你說(shuō)我怎么就攤上這事≡坡” “怎么了俯逾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵贸桶,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我桌肴,道長(zhǎng)皇筛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任坠七,我火速辦了婚禮水醋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘彪置。我一直安慰自己拄踪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布拳魁。 她就那樣靜靜地躺著惶桐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪的猛。 梳的紋絲不亂的頭發(fā)上耀盗,一...
    開(kāi)封第一講書(shū)人閱讀 49,985評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音卦尊,去河邊找鬼叛拷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛岂却,可吹牛的內(nèi)容都是我干的忿薇。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼躏哩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼署浩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起扫尺,我...
    開(kāi)封第一講書(shū)人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤筋栋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后正驻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體弊攘,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年姑曙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了襟交。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伤靠,死狀恐怖捣域,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤焕梅,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布迹鹅,位于F島的核電站,受9級(jí)特大地震影響丘侠,放射性物質(zhì)發(fā)生泄漏徒欣。R本人自食惡果不足惜逐样,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一蜗字、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脂新,春花似錦挪捕、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至滞乙,卻和暖如春奏纪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背斩启。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工序调, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兔簇。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓发绢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親垄琐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子边酒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

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