Javascript教程

Javascript教程
laiqun@msn.cn

Contents

1. javascript嵌入方式

  • 放入head <script> </script>
  • 放入js文件 <script src="./xx.js"> </script>

2. javascript語法

  • ;爲結尾,每行後面會自動加;最好不要省略,壓縮取空白換行字符後,代碼會出問題。
  • 爲語句塊
  • //爲行註釋 /**/爲塊註釋

3. 數據類型

數值類: Number:包含浮點數,負數 javascript

NAN:不是數 使用isNan函數來判斷是否爲NAN css

Infinity無限大 html

字符串:以單引號或者雙引號括起來的任意文本 java

布爾值: || && node

比較運算符: == 比較時自動進行類型轉換,比較詭異,最好不要用 === 比較時不會進行類型轉換 web

常識:ajax

浮點數的比較由於精度問題不會相等 正則表達式

(1/3) === (1-2/3) //false chrome

緣由:浮點數的精度表示,應用Math.abs(1/3-(1-2/3))<0.0000001 json

null: 空 注意不是數字0 也不是空字符串「」 

undefine: 未定義,在判斷是否傳入了參數的時候用 數組: 

[]或者new Array(1,2,3);可經過[索引號]來訪問 用Array.isArray來判斷是否爲數組 

對象:一組鍵值對構成想無序集合,能夠.或者[索引名]來訪問屬性

  • 例如: var Person={name:‘Bob’,zipcode:null}

4. javascript變量

變量名的規則:大小寫英文字母,$ 與_構成的集合,不能以數字開頭,不能與關鍵字衝突。

賦值:= 聲明一個變量用var,不用var表示全局,在不一樣的js文件中衝突後會形成覆蓋影響。

  • 通常使用use strict,將變量範圍限制在被聲明的函數體內。

5. 字符串

  • 轉義字符 ‘I ' m Ok’ \n換行 \t製表符 \字符 \x41 ANSI字符 \u4e2d unicode字符
  • 多行字符 ` `裏面能夠用${變量名}訪問變量
  • 字符串的索引操做 s[0],因爲字符串不可變,s[0]=‘a’執行正確但不會影響原字符串
  • 常見方法
    1. toUpperCase toLowerCase
    2. indexOf
    3. substring substring(1)得到去掉第一個字符之後的所有字符
    4. join 我比較喜歡稱它爲手拉手函數 [「abc」,「def」].join(「」)
  • 屬性:length

6. 數組 多維數組

屬性:length 直接給length賦值會致使Array的大小發生變化。

索引賦值:var arr=[1,2,3]; arr[5]=‘x’; 此時arr變成[1,2,3,undefine,undefine,‘x’]

常見方法:

  1. indexOf 指定元素的索引
  2. slice 與字符串的substring相似 slice(0,3) slice(1)得到去掉第一個元素的之後所有的元素,能夠用來複制Array
  3. push pop 在尾部添加/刪除元素 push能夠添加多個,pop刪除一個
  4. unshift 在頭部加若干元素 shift將Array的第一個元素刪除掉
  5. sort 排序
  6. reverse 反轉
  7. splice 俗稱萬能方法,從制定位置刪除若干元素後,再從該位置插入若干元素,返回刪除的若干元素的數組。
  8. concat 將當前Array與另外一個Array鏈接,可鏈接任意個Array,會把Array中的Array拆開
  9. join 兩兩之間手拉手,指定中間部分,會先將元素轉換爲字符串

7. 運算

  • || 短路求值 左爲真,取左,不然取右
  • && 短路求值 左爲假,取左,不然取右
  • !! 轉換爲boolean型

8. Map和set

對象能夠視爲Map或者dictionary的一種表示,即鍵值對。 對象中的屬性key必須爲字符串,實際上爲Number或其餘類型也能夠,故引入map

方法:

  1. set 添加新的key-value對,一個key放多值,會沖掉前面的
  2. get 獲取指定key的value
  3. has 是否存在指定key
  4. delete 刪除鍵值對

set爲不可重複的key的集合,用add添加,delete刪除

  • iterable 適用於Array map set

    1.可用for of來遍歷//此方法只遍歷屬於集合或字典自己的元素

    例子:
    1. 遍歷set: for (var x of a ){alert(a)};
    2. 遍歷Array:for(var x of a){alert(x[0]+'='x[1])}
  • for (var x in arr)的缺點,for of 對此的改進

    例如:var a=[1,2,3]; var a.name=‘hello’; for (var x in a){alert(x)};//會打印 ‘0’ ‘1’ ‘2’ ‘hello’
  • forEach方法—iterable內置的方法,傳入一個函數,每次迭代都執行該回調函數

    例如:iterable對象.forEach(function(element,index,this){})//對於set element和index爲同一個元素,由於它沒有索引。一個函數參數爲函數,此函數爲高階函數。

9. 流程控制

  • 條件判斷 if(){}else{}
    • null undefine 0 ‘’ 「」 NaN 都認爲是flase,其餘爲true
  • 循環
    • for(初始;調節;控制){}
    • while
    • do while();//注意後面有;號 至少會執行一次
    • for in 例如:var a =['A','B','C']; for (var i in a ){alert(i); alert(a[i]);//過濾到繼承的屬性用hasOwnProperty
      • Array的每個索引項視爲對象的屬性。
    • break continue

10. 函數

  • 值傳遞與引用傳遞

對於數字、字符串等是將它們的值傳遞給了函數參數,函數參數的改變不會影響函數外部的變量。

對於數組和對象等則是將對象(數組)的變量的值傳遞給了函數參數,這個變量保存的指向對象(數組)的地址。當函數改變這個地址指向的對象(數組)的內容時,同時也改變了函數外部變量指向的對象(數組)的內容;當函數改變的是變量的地址時,實際就與函數外部的變量失去了聯繫,變成了徹底不一樣的對象了,不會對函數外部對象形成改變。

var v1 = []
var v2 = {};
var v3 = {};
function foo(v1, v2, v3)//這裏分別是給v1 v2 v3 賦值新對象,對外部的對象不會有改變
{
    v1 = [1];
    v2 = [2];
    v3 = {a:3}
}

foo(v1, v2, v3);
alert (v1); // 空白 
alert (v2); // [object Object] 
alert (v3.a); // undefined
var v1 = []
var v2 = {};
var v3 = {a:0};
function foo(v1, v2, v3)//這裏不是賦值新對象,而是直接操做它,會形成函數外部對象改變
{
    v1.push (1);
    v2.a = 2;
    v3.a = 3;
}

foo(v1, v2, v3);
alert (v1); // 1 
alert (v2.a); // 2 
alert (v3.a); // 3

定義: 
1. function fun_name(prama1,prama2){…} 
2. var var_name = function (prama1,prama2){};//注意最後有一個分號 
方法2存在變量提高問題: 
例如: 
1. alarm(1); function alarm(){} 
2. alarm(1); var alarm= function (){}//此處由於變量提高,實際順序爲 var alarm; alarm(1); alarm = function(){};//會在alarm(1)那句報錯,alarm is undefine

函數結束的兩種狀況: 
1. return 
2. 執行到末尾,無return,返回undefine 
參數傳遞:

  1. arguments關鍵字 像Array,實際不是Array。其屬性length存着參數個數。

  2. ..rest關鍵字 若是參數過多,用for循環一個個取的話比較麻煩

    例如:

    1. function (a,b ){ var i,var rest=[]; if(arguments.length>2) { for(i=2;i<arguments.length;i++) rest.push(arguments[i]) } }
    2. function(a,b,...rest){}

返回值坑:

  1. return 多行
    return {

    name:‘foo’; }
    1. return //因爲javascript的默認加;機制,實際執行的爲return ; 故返回undefine
    {name:'foo'}

11. 變量提高、變量的做用域

  • 查找變量的機制:從自身函數開始,由內向外查找,若有同名,優先使用從內層找到的。 故內部函數能夠訪問外部函數的變量 function foo()

{

var x = 1;

function bar()

{

var y = x+1;

}

var z = y+1;//reference Error

} * 變量提高,是由變量的查找機制引發的。 ‘use strict’

function foo()

{

var x = ‘hello ’+y;

alert(x);//打印爲 hello undefine

var y = ‘bob’;

}

  • 因爲變量的查找機制,如今函數內查找,找到了,因而至關於這樣的順序 var y ; var x =「hello」+y; y=‘bob’。

  • 如何簡單理解? 將內部全部var 定義都放在函數體前面。

  • 名字空間:全局變量會綁定到window上,相同的全局變量會衝突

    • 減小衝突的方法把本身用的全部變量和函數所有綁定到一個全局變量中

      例如:

      var MYAPP={};//MYAPP在這裏叫名字空間 JQUERY YUI underscore都是這麼幹的

      MYAPP.name =「myapp」;

      MYAPP.version = 1.0;

      MYAPP.foo = function (){};

  • 局部做用域:默認做用域爲函數內部

    ‘use strict’

    function()

    {

    for(var i =0;i<100;i++){;}

    i+=100;//仍然能夠引用該變量

    }

    • 如何實現塊級做用域? 將上面的var 改成let
  • 常量

    曾經:var PI = 3.14 ;//用大寫變量名錶示不要修改它

    如今可用const,和let同樣具備塊級做用域。

    例如:const P1=3.14; 修改不報錯,可是沒有效果


12. 高階函數

  • 什麼是高階函數?

    一個函數的參數爲一個函數。例如:

    function add(x,y,f) {
    return f(x)+f(y);
    }
  • map

    有一個函數f(x)= x2,做用在[1,2,3,4,5,6,7,8,9]上,map的實現以下:

     map

    實現:

    function pow(x) {
     return x*x;
    }
    var arr=[1,2,3,4,5,6,7,8,9];
    arr.map(pow);

    等價形式:

    for(var i =0 ;i<arr.length;i++){//此代碼缺點:不易讀懂,而map能夠一行搞定
     result.push(pow(arr[i]));
    }
  • reduce

    Array.reduce方法在[x1,x2,x3,x4]上,效果是:

    [x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4);

    用例:對一個Array求和的reduce實現

    var arr=[1,3,5,7,9];
    arr.reduce(function(x,y){
    return x+y;
    });
  • filter

    filter與map相似,但其是根據return 的false true來決定每一個元素的去留。map reduce filter 對undefine的處理?對map來講undefine仍是undefine 對filter不理不理undefine

  • sort:對Array直接修改,返回的是同一個Array

    • 默認是按照ASCII碼排序

      用例:

      ['Google','Apple','Microsoft'].sort();//['Apple','Google','Microsoft']
      ['Google','Apple','Microsoft'].sort();//['Google','Microsoft','apple']
      [10,20,1,2].sort();//[1,10,2,20]爲何?先轉化爲字符串再string排序

      如何實現對數字排序?

      傳入排序方法函數

      var arr=[10,20,1,2];
      arr.sort(
      function(x,y)
      {
      if(x>y)
        return 1;//將x後移 實現升序排列if(x<y)
        return -1;
       return 0;
      }
      );

13. 閉包

  • 什麼是閉包,與高階函數類似的概念

    高階函數指接受一個函數做爲參數的函數。一樣函數也能夠做爲返回值,就叫閉包。

    function sum(arr) {
    return arr.reduce(
     function(x,y)
     {
       return x+y;
     }
    );
    }
    var f=sum([1,2,3,4,5]);//f接受一個函數
    f();//調用,纔是真正計算結果。

    內部函數sum能夠引用外部函數的參數和局部變量,相關參數和變量都般存在返回的函數中,稱爲「閉包」。

    每次調用都產生一個新函數,即便參數相同。

  • 閉包的用途

    • 私藏變量:

      用例:

      function count() {
      var arr =[];
      for(var i=1;i<=3;i++)
      {
      arr.push(function(){return i*i;});
      }
      return arr;
      }
      var result = count();
      result.map(funciton(x){console.log(x());})
      //會打印16 16 16 爲何不是1 4 9 

      因爲變量的查找機制,arr中function沒有找不到i的定義,因而向外層函數找,找到時此變量值爲4。

      返回函數不要引用任何循環變量,或者後續會發生變化的量

      必定要引用循環變量怎麼辦,用閉包私藏起來,方法:建立一個匿名函數並當即執行,匿名函數引用變量並返回內部函數

      function count() {
      var arr =[];
      for(var i=1;i<=3;i++)
      {
      arr.push(
       (function (n){
         return function(){return n*n;};
       })(i);//建立匿名函數並當即執行,匿名函數參數爲i
       );
      }
      return arr;
      }

      建立匿名函數名當即執行:

      function (x) {return x*x;}(x)
      //理論上的寫法;但與函數體的定義衝突,Syntax Error
      (function (x) {return x*x;})(x)
      //給函數的定義加上括號

      私藏的變量,即時是外部也沒法訪問,並不與函數對象的原變量並非同一個。例子:

      'use strict'function create_counter(initial) {
      var x = initial||0;
      return {//返回一個對象 
      inc:function(){
       x+=1;
       return x;
      }
      }
      }
      //使用var c1= create_counter();
      c1.inc();//1
      c1.inc();//2

      對於每一個返回的函數,其外部函數參數和局部變量,都是獨立的,而且相對於create_count.x也是獨立的

    • 封裝函數

      用例:常用x2和x3,可生成兩個函數,能夠簡化後期的參數輸入

      function make_pow(n) {
      return function(x){
           return Math.pow(x,n);
      };
      }
      var pow2=make_pow(2);
      var pow3=make_pow(3);
      pow2(5);//25
      pow3(7);//343

14. 箭頭函數

至關於匿名函數,當僅有一個表達式的時候,連{}和return 都省略了;有多條語句,就不能省略{}和return了。

f= x=> x*x;
x=>{
  if(x>0)
    return x*x;
  elsereturn -x*x*x;
}
(x,y)=>x*x+y*y;//兩個參數
()=>3.14;//無參數
(x,y,...rest)=>{};//可變參數
 x=>{foo:x};//返回一個對象//由於和函數體的定義衝突,須要改成
 x=>({foo:x})
  • 箭頭函數能夠解決函數內部定義函數this指向問題

    var obj={
    birth:1990,
    getAge: function () {
         var b = this.birth; // 1990
        var fn = function () {
             return new Date().getFullYear() - this.birth; // this指向window或undefined
         };
         return fn();
     }
    }
    改進方法1 bind
    getAge: function () {
         var b = this.birth; // 1990
        var fn = function () {
             return new Date().getFullYear() - this.birth; // this指向window或undefined
         };
         return fn().bind(this);
     }
    改進方法2 => :此時that就不須要了
    var fn=()=>new Date().getFullYear()-this.birth;
    //this此時指向window或undefine(strict模式下);

15. 生成器

  • 什麼是生成器? 標誌是 函數名前面有* 裏面有yield

    可在執行過程當中返回屢次,像一個能夠記住狀態的函數。生成器函數返回的是一個生成器對象。

    function * fab(max){
    var t,a=0,b=1,n=1;
    while(n<max)
    {
      yield a;
      t=a+b;
      a=b;
      b=t;
      n++;
    }
    return a;
    }
  • 調用生成器對象的兩個方法: next for of
    var f = fab(5);
    f.next();//{value:0,done:false}
    f.next();//{value:0,done:false}
    f.next();//{value:0,done:true}//next方法須要咱們根據返回對象的done屬性判斷是否完成。
    for (var x of fab(3))//用該方法不用判斷返回對象的done屬性
  • 生成器的用途 解決回調地獄

    ajax('http://url-1', data1, function (err, result) {
     if (err) {
         return handle(err);
     }
     ajax('http://url-2', data2, function (err, result) {
         if (err) {
             return handle(err);
         }
         ajax('http://url-3', data3, function (err, result) {
             if (err) {
                 return handle(err);
             }
             return success(result);
         });
     });
    });
    上面的寫法回調越多,越難看
    try {
     r1 = yield ajax('http://url-1', data1);
     r2 = yield ajax('http://url-2', data2);
     r3 = yield ajax('http://url-3', data3);
     success(r3);
    }
    catch (err) {
     handle(err);
    }
    //看上去同步的代碼,其實是異步的。只是寫法上的改變而已。
    yield與generator(*)結構,結合Promise的then catch,改善了異步回調的寫法,Async await生成方法被稱爲改善回調地獄的最後亮光。

16. 對象

  • 無序的集合類型,由若干的鍵值對構成
var xiaoming = { name:"小明",'middle-school':'No.1 middle school'};//最後一個屬性不要加,

訪問屬性用.或者[‘屬性名’] 注意middle-school屬性並不是變量名,只能用xiaoming['middle-school]的方式來訪問。

檢測是否有某屬性/方法用in 。 ‘name’ in xiaoming ; ‘toString’ in xiaoming; toString定義在Object中,xiaoming的原型鏈指向Object。

檢測時自身擁有的屬性,仍是繼承來的用hasOwnProperty方法


17. 方法

什麼叫方法?將一個函數綁定到一個對象

  • this 指向問題

例1:

var xiaoming ={
  name:"小明",
  birth:1990;
  age:function(){
    var y = new Date().getFullYear();
    return y -this.birth;
   }
}

執行xiaoming.age()//裏面的this指向xiaoming

例2://分開寫

function getAge(){
    var y = new Date().getFullYear();
    return y -this.birth;
   }
var xiaoming ={
  name:"小明",
  birth:1990;
}

執行xiaoming.getAge()//裏面的this指向xiaoming 執行getAge() //返回NAN this指向window

例3:

'use strict'var xiaoming ={
  name:"小明",
  birth:1990;
  age:function(){
    var y = new Date().getFullYear();
    return y -this.birth;
   }
}
var fn = xiaoming.age();
fn();//非strict下指向window,strict下指向undefine

例4:

var xiaoming ={
  name:"小明",
  birth:1990;
  age:function(){//函數內部定義函數this的問題function getAgeFromBirth()
    {
      var y = new Date().getFullYear();
      return y-this.birth;
    }
    return getAgeFromBirth();  
   }
}

執行xiaoming.age()//裏面的this非strict下指向window,strict下指向undefine。

修正:

var xiaoming ={
  name:"小明",
  birth:1990;
  age:function(){//函數內部定義函數this的問題
     that = this;    
     function getAgeFromBirth()
    {
      var y = new Date().getFullYear();
      return y-that.birth;
    } 
    return getAgeFromBirth();
   }
}
  • 對於函數內定義的函數,this非strict下指向window,strict下指向undefine。

  • 如何控制this的指向? call apply bind

    使用方式:

    1. 函數名.apply(對象,打包成Array的參數)
    2. 函數名.call(對象,依次傳入的參數)
  • 裝飾器模式的實現 使用apply實現

    例如:統計一下使用了多少次parseInt

    var count = 0;
    var oldparseInt = pareseInt;//保存原函數
    window.parseInt = function ()
    {
      count+=1;
      return oldparseInt.apply(null,arguments);
    }
    parseInt('10');
    parseInt('20');
    parseInt('30');

18. 標準對象

  • typeof typeof獲取對象的類型,是操做符,它返回一個字符串
    typeof 123;//'number'
    typeof NaN;//'number'
    typeof 'str';//'string'
    typeof true;//'boolean'
    typeof undefine;//'undefined'
    typeof Math.abs;//'function'
    typeof null;//'object'
    typeof [];//'object'
    typeof {};//'object'
  • 包裝對象 存在的問題:用了new Number/Boolean/String,類型變成了‘object’
    typeof new String('str');//'object'new String('str') ==='str'//false
    不要使用包裝對象
  • 類型轉換 Number() Boolean() String 轉換後衛相應的類型,非‘object’

總結:

  • 不要使用new Number()、new Boolean()、new String()建立包裝對象;

  • 用parseInt()或parseFloat()來轉換任意類型到number;

  • 用String()來轉換任意類型到string,或者直接調用某個對象的toString()方法;

  • 一般沒必要把任意類型轉換爲boolean再判斷,由於能夠直接寫if (myVar) {…};

  • typeof操做符能夠判斷出number、boolean、string、function和undefined;

  • 判斷Array要使用Array.isArray(arr);

  • 判斷null請使用myVar === null;

  • 判斷某個全局變量是否存在用typeof window.myVar === ‘undefined’;

  • 函數內部判斷某個變量是否存在用typeof myVar === ‘undefined’。

  • 最後有細心的同窗指出,任何對象都有toString()方法嗎?null和undefined就沒有!雖然null還假裝成了object類型。

  • 更細心的同窗指出,number對象調用toString()報SyntaxError:

    123.toString(); // SyntaxError

    遇到這種狀況,要特殊處理一下: 123..toString(); // ‘123’, 注意是兩個點! 由於Number類型有浮點數 (123).toString(); // ‘123’


19. Date對象

var now = new Date();
now;//var now = Date();
now;//wed jun 24 2015 19:49:22 GMT +800(CST)
now.getFullYear();//2015
now.getMonth();//0-11
now.getDate();//24 24號
now.getDay();//星期 這個比較特別 其餘的都是年月日時分秒毫秒
now.getHours();//24小時制
now.getMinutes();//49分鐘
now.getSeconds();//秒
now.getMilliseconds();//毫秒數
now.getTime();//時間戳 13位 以Number形式,表示1970年1月1日0點整通過的毫秒數,在GMT時區下
new Date(2015,5,19,20,3,3,123);//年月日時分秒毫秒時區
var d = Date.parse('2015-06-24T19:24:22.876+0800');//T後面接時間 +/-hh:mm表示相對於UTC超前/滯後的時間
var time = Date(d);//將時間戳轉化爲人能夠讀懂的時間
now.toLocaleString();//轉換爲當地時間
now.toTimeString();//UTC+8爲中國時間

時區知識:GMT 格林威治時間 UTC世界協調時間 DST夏日節約時間 CST同時表示4個時區,用+hh:mm表示區別


20. 正則表達式

匹配字符串強有力的武器,凡是符號規則的字符串。

入門:

直接給出某字符爲精確匹配

\d 匹配一個數字

\w一個字母或數字或與[a-zA-Z0-9]等效

. 任意一個字符

  • 變長任意個字符

  • 至少一個字符

? 0個或1個字符

{n} n個字符

{n,m} n-m個字符

例子: \d{3}\S+\d{3,8} 

\d{3}表示匹配3個數字 

\S+ 表示至少一個空白字符(空格 tab等)

\d{3,8}表示3-8個數字

 

\d{3}\-\d{3,8} 能夠匹配010-1235 

 

-是特殊字符須要轉義

進階:

[]表示字符範圍,如[0-9a-z]

[]可用A|B,表示匹配A或者B

表示行頭,在[]中表示非 ^\d必須以數字開頭

$表示行尾 \d$必須以數字結尾

正則表達式的建立

  1. /正則表達式/ 去掉裏面的正則表達式,則變成了行註釋的符號

  2. new RegExp(‘正則表達式’); ‘ABC\-001’ \實際爲一個,須要轉義 用途: 1.測試給定的字符串是否符合條件 2.用於切分字符串 split

    'ab c'.split('');//['a','b',' ',' ','c']存在多個空格符怎麼辦
    'ab c'.split(/\s+/);//['a','b',' ',' ','c'] 存在","怎麼辦?
    'a,b, c'.split(/[\s,]+/);//['a','b',' ',' ','c'] 存在";"呢?
    'a,b; c'.split(/[\s,;]+/);//['a','b',' ',' ','c']

    相關函數: match一個或多個,返回數組 search 第一個 replace split

  3. 分組和提取子串 子表達式的反向引用\2 \1

    var re = /^(\d{3}-(\d{3,8}))/;
    re.exec('010-12345');//['010-12345','010','12345'];
    re.exec('010 12345');//null
    /(\d)(\d)\2\1/;//能夠匹配5775 1221 之類的字符串 \2 反向引用子式2 \1反向引用子式1
  4. 全局搜索 g(可執行屢次匹配,不加指匹配一次) i(忽略大小寫) m多行匹配(影響多行中^$的匹配) 

    var r1=/test/g;//或者 var r1=new RegExp(‘test’,‘g’); 

    可執行屢次exec,每次exec會更新lastIndex屬性 lastIndex leftcontent rightcontent

    var s = 'JavaScript, VBScript, JScript and ECMAScript';
    var re=/[a-zA-Z]+Script/g;
    // 使用全局匹配:
    re.exec(s); // ['JavaScript']
    re.lastIndex; // 10
    re.exec(s); // ['VBScript']
    re.lastIndex; // 20
    re.exec(s); // ['JScript']
    re.lastIndex; // 29
    re.exec(s); // ['ECMAScript']
    re.lastIndex; // 44
    re.exec(s); // null,直到結束仍沒有匹配到

正則表達式的侷限:像‘2-30’ ‘4-31’這樣的非法日期,用正則表達式是識別不了或者實現困難,要配合程序來處理

正則表達式的貪婪匹配:

var re=/^(\d+)(0*)/;
re.exec('102300');//["102300","102300",""]//加個?表示非貪婪匹配
re=/^(\d+?)(0*)/;
re.exec('102300');//["102300","1023","00"]
re=/1{3}/;//匹配11111111 會獲得 111 111 第一次匹配會更新lastIndex屬性

元字符的分類:1.限定次數符 {n} {n,m} + * ? 2.選擇匹配符 3.分組組合與反向引用 4.特殊字符 5.字符匹配符 6.定位符


21. JSON

JSON以前一直使用XML,猶豫XML規範衆多,Douglas發明了JSON。

JSON中一共有number、boolean、string、null、array([])、object({…}),並規定死了字符集爲UTF-8,規定字符串必須用「」,

object的鍵也必須用「」。

方法:

  1. 序列化—字符串化

    1. 使用JSON類的stringify(對象,篩選屬性數組或一個函數,每一個鍵值對都會被函數先處理,縮進)。

    2. 寫出對象的TOJSON方法,返回JSON應該序列號的數據

  2. 反序列化—將其轉換爲對象

    1. 使用eval執行表達式,太過危險

    2. JSON.parse(字符串,函數) 傳入函數會處理鍵值對


22. 面向對象

javascript不區分類和實例的概念,而是經過原型(prototype)來實現。

var Student = {
    name: 'Robot',
    height: 1.2,
    run: function () {
        console.log(this.name + ' is running...');
    }
};

var xiaoming = {
    name: '小明'
};

xiaoming.__proto__ = Student;
//把xiaoming的原型指向了對象Student,看上去xiaoming彷彿是從Student繼承下來的

obj

javascript全部對象都是實例,繼承是把一個對象的原型指向另外一個對象。 實際中不要使用__proto__屬性改變一個對象原型,

可經過編寫函數配合Object.create(傳入原型)的方法:

例如:

// 原型對象:var Student = {
    name: 'Robot',
    height: 1.2,
    run: function () {
        console.log(this.name + ' is running...');
    }
};

function createStudent(name) {
    // 基於Student原型建立一個新對象:
var s = Object.create(Student);
    // 初始化新對象:
    s.name = name;
    return s;
}

var xiaoming = createStudent('小明');
xiaoming.run(); // 小明 is running...
xiaoming.__proto__ === Student; // true

22.1. 建立對象

每建立一個對象都會設置一個原型

  • 屬性/方法訪問機制

    當訪問obj.xxx屬性時,如今當前對象查找屬性,沒找到,再到原型對象找,還未找到,再到Object.prototype對象,

    若是仍然沒有找到,放回undefine

  • 原型鏈

    var arr=[1,2,3];
    //原型鏈爲 arr->Array.prototype->object.prototype->nullfunction foo(){return 0;}
    //原型鏈爲 foo->Function.prototype->object.prototype->null//Function中有Apply方法
  • 使用構造函數建立對象

    function Student(name) {//注意建立對象的函數首字母爲大寫,普通函數爲小寫this.name = name;
     this.hello = function () {
         alert('Hello, ' + this.name + '!');
     }
    }
    var xiaoming = new Student(' 小明');
    //寫new的做用 函數會變成構造函數: 1. 內部this指向新建立的對象 2 默認返回this
    //不寫new 返回undefine

    若是還有別的對象,其原型鏈爲:

xiaoming ↘

xiaohong -→ Student.prototype —-> Object.prototype —-> null

xiaojun ↗

obj2

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

xiaoming instanceof Student; // true
//函數Student剛好有個屬性prototype指向xiaoming、xiaohong的原型對象
//可是xiaoming、xiaohong這些對象可沒有prototype這個屬性,
//不過能夠用__proto__這個非標準用法來查看。
  • 如何共享同一個函數,而不是每一個對象各有一份代碼?

    xiaoming.hello === xiaohong.hello;//false

    將函數寫入prototype,修改代碼以下:

    function Student(name) {
      this.name = name;
    }
    Student.prototype.hello = function () {
      alert('Hello, ' + this.name + '!');
    };//注意此處有分號
  • 構造函數忘記寫new會怎樣?

    在strict模式下,this執行undefine,非stric模式下,this執行window,會建立全局變量;jslint能夠檢查出漏泄的new

  • 如何防止漏寫new? 編寫函數,將new封到函數裏面

    function Student(props) {
      this.name = props.name || '匿名'; // 默認值爲'匿名'
    this.grade = props.grade || 1; // 默認值爲1
    }
    Student.prototype.hello = function () {
      alert('Hello, ' + this.name + '!');
    };
    function createStudent(props) {
      return new Student(props || {})
    }
    var xiaoming = createStudent({//傳入參數
      name: '小明'
    });
    //好處 1.不用new來調用 2.傳入參數靈活,因爲參數是對象,咱們能夠把JSON轉爲對象直接傳入。
    xiaoming.grade; // 1 修改參數

22.2. 原型繼承

基於上街Student擴展出PrimaryStudent,能夠先定義出PrimaryStudent:

function PrimaryStudent(props) {
    // 調用Student構造函數,綁定this變量:
    Student.call(this, props);
    this.grade = props.grade || 1;
}

調用了Student構造函數不等於繼承了Student,PrimaryStudent建立的對象的原型是:

new PrimaryStudent() —> PrimaryStudent.prototype —> Object.prototype —> null

必須想辦法把原型鏈修改成:

new PrimaryStudent() —> PrimaryStudent.prototype —> Student.prototype —>Object.prototype —> null

PrimaryStudent對象既能夠調用PrimaryStdent.prototype定義獲得方法,也能夠調用原型鏈上的方法。

若是你想用最簡單粗暴的方法這麼幹:

PrimaryStudent.prototype = Student.prototype;

是不行的!若是這樣的話,PrimaryStudent和Student共享一個原型對象,那還要定義PrimaryStudent幹啥?

咱們必須藉助一箇中間對象來實現正確的原型鏈,這個中間對象的原型要指向Student.prototype。

爲了實現這一點,參考道爺(就是發明JSON的那個道格拉斯)的代碼,中間對象能夠用一個空函數F來實現:

pro

// PrimaryStudent構造函數:function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// 空函數F:function F() {
}

// 把F的原型指向Student.prototype: 1.先指明F對象建立模板爲Student,F內才能找到Student對象的屬性
F.prototype = Student.prototype;

// 把PrimaryStudent的原型指向一個新的F對象,F對象的原型正好指向Student.prototype:
PrimaryStudent.prototype = new F();//2// 把PrimaryStudent原型的構造函數修復爲PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;//3
// 繼續在PrimaryStudent原型(就是new F()對象)上定義方法:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

// 建立xiaoming:var xiaoming = new PrimaryStudent({
    name: '小明',
    grade: 2
});
xiaoming.name; // '小明'
xiaoming.grade; // 2// 驗證原型:
xiaoming.__proto__ === PrimaryStudent.prototype; // true
xiaoming.__proto__.__proto__ === Student.prototype; // true// 驗證繼承關係:
xiaoming instanceof PrimaryStudent; // true
xiaoming instanceof Student; // true
  • 進一步封裝

    若是把繼承這個動做用一個inherits()函數封裝起來,還能夠隱藏F的定義,並簡化代碼:

    function inherits(Child, Parent) {//此函數能夠複用
    var F = function () {};
     F.prototype = Parent.prototype;
     Child.prototype = new F();
     Child.prototype.constructor = Child;
    }

    使用:

    function Student(props) {
     this.name = props.name || 'Unnamed';
    }
    Student.prototype.hello = function () {
     alert('Hello, ' + this.name + '!');
    }
    function PrimaryStudent(props) {
     Student.call(this, props);
     this.grade = props.grade || 1;
    }
    // 實現原型繼承鏈:
    inherits(PrimaryStudent, Student);
    // 綁定其餘方法到PrimaryStudent原型:
    PrimaryStudent.prototype.getGrade = function () {
     return this.grade;
    };

    JavaScript的原型繼承實現方式就是:

    1. 定義新的構造函數,並在內部用call()調用但願「繼承」的構造函數,並綁定this;

    2. 藉助中間函數F實現原型鏈繼承,最好經過封裝的inherits函數完成;

    3. 繼續在新的構造函數的原型上定義新方法。

  • 新方法 使用class關鍵字來實現原型繼承

在上面的章節中咱們看到了JavaScript的對象模型是基於原型實現的,特色是簡單,缺點是理解起來比傳統的類-實例模型要困難,

最大的缺點是繼承的實現須要編寫大量代碼,而且須要正確實現原型鏈。

有沒有更簡單的寫法?有!

新的關鍵字class從ES6開始正式被引入到JavaScript中。class的目的就是讓定義類更簡單。

咱們先回顧用函數實現Student的方法:

function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}

若是用新的class關鍵字來編寫Student,能夠這樣寫:

class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '!');
    }
}

比較一下就能夠發現,class的定義包含了構造函數constructor和定義在原型對象上的函數hello()(注意沒有function關鍵字),

這樣就避免了Student.prototype.hello = function () {…}這樣分散的代碼。

最後,建立一個Student對象代碼和前面章節徹底同樣:

var xiaoming = new Student('小明');
xiaoming.hello();

class繼承

用class定義對象的另外一個巨大的好處是繼承更方便了。想想咱們從Student派生一個PrimaryStudent須要編寫的代碼量。

如今,原型繼承的中間對象,原型對象的構造函數等等都不須要考慮了,直接經過extends來實現:

class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // 記得用super調用父類的構造方法!this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

注意PrimaryStudent的定義也是class關鍵字實現的,而extends則表示原型鏈對象來自Student。子類的構造函數可能會與父類不太相同,

例如,PrimaryStudent須要name和grade兩個參數,而且須要經過super(name)來調用父類的構造函數,不然父類的name屬性沒法正常初始化。

PrimaryStudent已經自動得到了父類Student的hello方法,咱們又在子類中定義了新的myGrade方法。

ES6引入的class和原有的JavaScript原型繼承有什麼區別呢?實際上它們沒有任何區別,class的做用就是讓JavaScript引擎去實現原來須要咱們

本身編寫的原型鏈代碼。簡而言之,用class的好處就是極大地簡化了原型鏈代碼。

你必定會問,class這麼好用,能不能如今就用上?

如今用還早了點,由於不是全部的主流瀏覽器都支持ES6的class。若是必定要如今就用上,就須要一個工具把class代碼轉換爲傳統的prototype代碼,

能夠試試Babel這個工具。


23. 瀏覽器對象

  • 現有的瀏覽器內核與javascript引擎

    • IE 6~11:IE10開始支持ES6標準。 trident引擎
    • Chrome:Webkit內核 JavaScript引擎——V8
    • Sarafi:Webkit內核
    • Firefox:Gecko內核 JavaScript引擎OdinMonkey。
    • 移動設備上目前iOS和Android兩大陣營分別主要使用Apple的Safari和Google的Chrome,都是Webkit核心
  • window 對象不但充當全局做用域,並且表示瀏覽器窗口。

    • innerWidth和innerHeight屬性,能夠獲取瀏覽器窗口的內部寬度和高度。內部寬高是指除去菜單欄、工具欄、邊框等佔位元素後,用於顯示網頁的淨寬高。
    • 兼容性:IE<=8不支持。
    • 對應的,還有一個outerWidth和outerHeight屬性,能夠獲取瀏覽器窗口的整個寬高。
  • navigator對象表示瀏覽器的信息,最經常使用的屬性包括:

    • navigator.appName:瀏覽器名稱;
    • navigator.appVersion:瀏覽器版本;
    • navigator.language:瀏覽器設置的語言;
    • navigator.platform:操做系統類型;
    • navigator.userAgent:瀏覽器設定的User-Agent字符串。 注意,navigator的信息能夠很容易地被用戶修改,用if判斷瀏覽器版本很難維護,要善用短路運算符
      var width= window.innerWidth||document.body.clientWidth;
  • screen對象表示屏幕的信息,經常使用的屬性有:

    • screen.width:屏幕寬度,以像素爲單位;
    • screen.height:屏幕高度,以像素爲單位;
    • screen.colorDepth:返回顏色位數,如八、1六、24。
  • location對象表示當前頁面的URL信息。例如,一個完整的URL:http://www.example.com:8080/path/index.html?a=1&b=2#TOP

    屬性:

    • location.protocol; // ‘http’
    • location.host; // ‘www.example.com’
    • location.port; // ‘8080’
    • location.pathname; // ‘/path/index.html’
    • location.search; // ‘?a=1&b=2’
    • location.hash; // ‘TOP’
    • location.href //表示當前URL

    方法:

    • assign 加載一個新頁面。例如:location.assign(‘/discuss’);
    • reload 從新加載當前頁面
  • document對象表示當前頁面。因爲HTML在瀏覽器中以DOM形式表示爲樹形結構,document對象就是整個DOM樹的根節點。

    屬性:

    1. title 是從title標籤內容中讀取的,能夠動態修改。

      document.title = ‘努力學習JavaScript!’;

    2. cookie

      • cookie是什麼?

      Cookie是由服務器發送的key-value標示符。由於HTTP協議是無狀態的,可是服務器要區分究竟是哪一個用戶發過來的請求,就能夠用Cookie來區分。

      當一個用戶成功登陸後,服務器發送一個Cookie給瀏覽器,例如user=ABC123XYZ(加密的字符串)…,此後,瀏覽器訪問該網站時,會在請求頭附上這個Cookie,服務器根據Cookie便可區分出用戶。

      • cookie的安全

      javascript可經過document.cookie來讀取到當前頁面的cookie。若是瀏覽器嵌入了第三方的javascript,會形成巨大隱患。

      服務器在設置cookie時可使用httponly這樣的選項,這樣的cookie就不能經過javascript代碼讀取了。

    獲取節點的方法

    var menu = document.getElementById('');
    menu.tagName;//輸出標籤名var drinks = document.getElementByTagName('');
  • history

    方法:

    1. back 後退 在存在大量AJAX交互的頁面裏,粗暴的使用back方法會讓用戶感受憤怒。
    2. forwar 前進

    實現無刷新更換URL地址

    window.history.pushState({},0,url);

    利用ajax配合pushState翻頁無刷新的動做


24. 操做DOM

因爲HTML文檔被瀏覽器解析後就是一棵DOM樹,要改變HTML的結構,就須要經過JavaScript來操做DOM。

樹形結構:

  1. 更新:更新該DOM節點的內容,至關於更新了該DOM節點表示的HTML的內容;

  2. 遍歷:遍歷該DOM節點下的子節點,以便進行進一步操做;

  3. 添加:在該DOM節點下新增一個子節點,至關於動態增長了一個HTML節點;

  4. 刪除:將該節點從HTML中刪除,至關於刪掉了該DOM節點的內容以及它包含的全部子節點。

  5. 遍歷獲取DOM節點—在操做一個DOM節點前,咱們須要經過各類方式先拿到這個DOM節點。

    1. 經過document的方法和屬性

      1. getElementById(‘ID名’)
      2. getElementsByTagName(‘標籤名’)[索引號]
      3. getElementsByClassName(‘類名’)[索引號]

      得到某節點下的全部直屬點

      經過屬性:

      1. var cs= test.children;
      2. var cs= test.firstElementChild;
      3. var cs= test.lastElementChild;
    2. 經過選擇器方法querySelector 和QuerySelectorAll() 須要瞭解select的用法,相似於JQUERY 的選擇器。

node和element的區別?

node更爲廣義,包括了element、comment、CDATA_SECTION等多種,但絕大時候,咱們只關心element,由於他是能夠看到的,comment這種是不可見的。

  • 插入 若是dom節點是空的,能夠經過innetHTML來寫

    1. appendChild 插入到父節點的最後一個節點。例如:

      var d = document.createElement('style');
      d.setAttribute('type', 'text/css');
      d.innerHTML = 'p { color: red }';
      document.getElementsByTagName('head')[0].appendChild(d);
    2. parentElement.insertBefore(newElement,referenceElement) 插入到參照節點前

      haskell = document.createElement('p');
      haskell.id = 'haskell';
      haskell.innerText = 'Haskell';
      list.insertBefore(haskell, ref);
  • 刪除—要刪除一個節點,首先要得到該節點自己以及它的父節點,而後,調用父節點的removeChild把本身刪掉:

    • removeChild

      刪除後的節點雖然不在文檔樹中了,但其實它還在內存中,能夠隨時再次被添加到別的位置。

      children屬性是一個只讀屬性,而且它在子節點變化時會實時更新。
  • 更新—也就是修改

    1. innerHTML屬性 隱患經過網絡獲得的還有標籤的字符串,會形成XSS攻擊
    2. innerText或TextContent屬性 會自動對字符串進行HTML編碼,‘<span>’被編碼爲‘&lt;span&gt;’保證其不會產生任何HTML標籤。
      • innerText 讀取時沒法得到隱藏元素文本。

    修改CSS,經過節點的style屬性,因爲CSS容許font-size並不是javascript有效的屬性名,須要將其改成駝峯式,fontSize

    p.style.fontSize='20px';

25. 操做表單

表單爲能夠得到用戶輸入的內容,或者對輸入框設置新的內容。

  • 表單控件

    • 文本框,對應的<input type="text">,用於輸入文本;

    • 口令框,對應的<input type="password">,用於輸入口令;

    • 單選框,對應的<input type="radio">,用於選擇一項;同一組單選框name屬性必須一致

    • 複選框,對應的<input type="checkbox">,用於選擇多項;同一組複選框name必須一致

    • 下拉框,對應的<select><option value=""></option>,用於選擇一項;

    • 隱藏文本,對應的<input type="hidden">,用戶不可見,但表單提交時會把隱藏文本發送到服務器。

  • 獲取表單值的方式

    爲輸入框設置label標籤的函數,單擊該處文本,焦點會自動跑到對應輸入框上。增大了可單擊區域。

    • 經過value屬性來得到用戶輸入的值。
    • 單選框和複選框的特殊性,value是HTML預設的值,是否勾選,用checked來判斷
    // <label><input type="radio" name="weekday" id="monday" value="1"> Monday</label>
    // <label><input type="radio" name="weekday" id="tuesday" value="2"> Tuesday</label>
    var mon = document.getElementById('monday');
    var tue = document.getElementById('tuesday');
    mon.value; // '1'
    tue.value; // '2'
    mon.checked; // true或者false
    tue.checked; // true或者false
  • 設置表單值的方式

    • 直接修改vaule屬性的值
    • 單選框和複選框設置checked爲ture或false
  • HTML5表單控件

    <input type="date" value="2015-07-01">日期

    <input type="datetime-local" value="2015-07-01T02:03:04">日期和本地時間

    <input type="color" value="#ff0000">顏色選擇器

    對於不支持的瀏覽器會看成 type=「text」來顯示。

  • 提交表單的兩種方式

    1. 得到表單元素後調用它的submit();方法
    2. 瀏覽器默認點擊<button type="submit">時提交表單,或者用戶在最後一個輸入框按回車鍵。須要使用form的onSubmit屬性
      <!-- HTML -->
      <form id="test-form" onsubmit="return checkForm()">
      <input type="text" name="test">
      <button type="submit">Submit</button>
      </form>
      <script>
      function checkForm() {
      var form = document.getElementById('test-form');
      // 能夠在此修改form的input...// 繼續下一步:return true;  //return flase則不會提交
      }
      </script>
  • 防止提交表單時輸入框閃爍 在檢查和修改<input>時,要充分利用<input type="hidden">來傳遞數據。

例如,不少登陸表單但願用戶輸入用戶名和口令,可是,安全考慮,提交表單時不傳輸明文口令,而是口令的MD5。

普通JavaScript開發人員會直接修改<input>

這個作法看上去沒啥問題,但用戶輸入了口令提交時,口令框的顯示會忽然從幾個變成32個(由於MD5有32個字符)。

要想不改變用戶看到這個現象,能夠利用<input type="hidden">實現:

<form id="login-form" method="post" onsubmit="return checkForm()">
<input type="text" id="username" name="username">
<input type="password" id="input-password">
<input type="hidden" id="md5-password" name="password">
<button type="submit">Submit</button>
</form>
<script>
function checkForm() {
    var input_pwd = document.getElementById('input-password');
    var md5_pwd = document.getElementById('md5-password');
    // 把用戶輸入的明文變爲MD5:
    md5_pwd.value = toMD5(input_pwd.value);
    // 繼續下一步:return true;
}
</script>

26. 操做文件

在HTML表單中,能夠上傳文件的惟一控件就是<input type="file">

注意:當一個表單包含<input type="file">時,表單的enctype必須指定爲multipart/form-data,method必須指定爲post,

瀏覽器才能正確編碼並以multipart/form-data格式發送表單的數據。

出於安全考慮,瀏覽器只容許用戶點擊<input type="file">來選擇本地文件,用JavaScript對<input type="file">的value賦值是沒有任何效果的。

當用戶選擇了上傳某個文件後,JavaScript也沒法得到該文件的真實路徑。

一般,上傳的文件都由後臺服務器處理,JavaScript能夠在提交表單時對文件擴展名作檢查,以便防止用戶上傳無效格式的文件:

var f = document.getElementById('test-file-upload');
var filename = f.value; // 'C:\fakepath\test.png'
if (!filename || !(filename.endsWith('.jpg') || filename.endsWith('.png') )) {
    alert('Can only upload image file.');
    return false;
}

因爲JavaScript對用戶上傳的文件操做很是有限,之前都是用flash來上傳,能夠獲得上傳進度條等信息。

HTML5新增的File API容許JavaScript讀取文件內容,得到更多的文件信息。 可經過name size lastModifiedDate獲取信息。

// 讀取文件:var reader = new FileReader();
    reader.onload = function(e) {//回調函數,不知道何時讀完,讀完後自動調用咱們設置的函數var
            data = e.target.result; // 'data:image/jpeg;base64,/9j/4AAQSk...(base64編碼)...' 
        preview.style.backgroundImage = 'url(' + data + ')';
    };
    // 以DataURL的形式讀取文件:
    reader.readAsDataURL(file);//以Base64方式,經常使用於設置圖像

27. js的調試

  • chrome的console:console.log console.dir —展開變量 console.trace()打印當前函數的調用堆棧

28. javascript事件  

  • 事件冒泡

 

  • 事件捕獲

 

  • html事件

 

  • dom 0

 

  • dom 2
  • 取消事件冒泡

 

參考書籍《javascript高級程序設計第三版》 13章 事件部分  慕課網 DOM事件探祕

29. Ajax–Asynchronous javascript and xml

提交表單時會刷新頁面,告訴你操做成功/失敗,因爲網絡太慢或者其餘緣由,可能獲得404。 

web的運做原理:一次HTTP請求對應一個頁面 如何讓用戶停留在該頁面,用javascript發送請求並接受數據,更新頁面—Ajax

var request;
if (window.XMLHttpRequest) {//經過該屬性肯定是否支持標準XMLHttpRequest
// 不要判斷navigator.userAgent 1.能夠僞造 2.判斷IE版本複雜
    request = new XMLHttpRequest();
} else {
    request = new ActiveXObject('Microsoft.XMLHTTP');
}
request.onreadystatechange = function () { // 狀態發生變化時,函數被回調
if (request.readyState === 4) { // 成功完成// 判斷響應結果:
if (request.status === 200) {
            // 成功,經過responseText拿到響應的文本:
        return success(request.responseText);    
} else {
            // 失敗,根據響應碼判斷失敗緣由:
        return fail(request.status);    
}
    } else {
        // HTTP請求還在繼續...
    }
}
// 發送請求:
request.open('GET', '/api/categories');
//open方法有三個參數 第一個參數爲請求的方法 ,
//第二個爲請求的URL地址,第三個參數代表是否使用異步
//第三個參數默認爲true 使用異步,若是爲同步,瀏覽器中止響應知道Ajax完成。
request.send();
request.open('POST', '/api/categories');
request.setHeader//POST請求須要設置HEADER
senStr=
request.send();//POST請求須要在send中寫上要發送的參數;GET不須要,由於參數在URL中

跨域訪問—javascript的安全策略

javascript在發送請求時,URL域名必須和當前頁面一致,一致的意思是:

  1. 域名要相同(www.example.com和example.com不一樣)
  2. 協議相同(http和https不一樣)
  3. 端口號相同(80和8080不一樣)

若是要訪問外域(其餘網站)的URL呢?

  1. 使用flash
  2. 設置一個代理服務器進行轉發 proxy?url=‘http://www.sina.com’;由代理服務器返回結果,遵照了瀏覽器的同源策略,須要在服務器作開發。
  3. JSONP 只能使用GET請求,利用script標籤的src屬性,指明callback後,有外網生成js文件返回給客戶端,並在js文件中調用咱們寫好的函數,把參數填好並調用。
    <script src="http://api.126.net/....?callback=refreshPrice"></script>
  4. HTM5跨域訪問CORS coress origin resource shareing ajax

取決於sina.com是否願意將你加入Access-Control-Allow-Origin:或者*

上面這種跨域請求,稱之爲「簡單請求」。簡單請求包括GET、HEAD和POST(POST的Content-Type類型 

僅限application/x-www-form-urlencoded、multipart/form-data和text/plain),而且不能出現任何自定義頭(例如,X-Custom: 12345) CSS中的跨域請求

/* CSS */
@font-face {
  font-family: 'FontAwesome';
  src: url('http://cdn.com/fonts/fontawesome.ttf') format('truetype');
  /*CDN服務器的Access-Control=Allow-Origin不給權限,則沒法加載字體資源*/
}

對於PUT、DELETE以及其餘類型如application/json的POST請求,在發送AJAX請求以前,瀏覽器會先發送一個OPTIONS請求

(稱爲preflighted請求)到這個URL上,詢問目標服務器是否接受:

OPTIONS /path/to/resourceHTTP/1.1
Host: bar.com
Origin: http://my.com
Access-Control-Request-Method: POST

服務器必須響應並明確指出容許的Method,纔會繼續發送Ajax,不然拋出錯誤:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://my.com
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
Access-Control-Max-Age: 86400

因爲以POST、PUT方式傳送JSON格式的數據在REST中很常見,因此要跨域正確處理POST和PUT請求,服務器端必須正確響應OPTIONS請求。


30. Promise

在javascript中全部代碼都是單線程執行的。 全部javascript中全部的網絡操做,瀏覽器事件都必須異步執行,異步執行可用回調函數實現。

異步函數會在未來的某個時間點出發一個函數調用。 如Ajax

request.onreadystatechange = function () { // 狀態發生變化時,函數被回調
if (request.readyState === 4) { // 成功完成// 判斷響應結果:
if (request.status === 200) {
            // 成功,經過responseText拿到響應的文本:
return success(request.responseText);
        } else {
            // 失敗,根據響應碼判斷失敗緣由:
        return fail(request.status);    
}
    } else {
        // HTTP請求還在繼續...
    }
}

假若有以下的寫法,先統一執行ajax邏輯,不關心結果如何,

var ajax=ajaxGet("http://...");

而後根據成功、失敗調用sucess或者fail函數。 

ajax.ifSuccess(success); ajax.ifFail(fail);

 promise

Promise的then函數接受一個處理函數,此函數在處理成功時調用;catch接受一個處理函數,此函數在失敗時調用。 例如:

function test(resolve, reject) {
            //若是成功調用resolve
            resolve('200 OK');
            //若是失敗,調用reject
            reject('timeout in ' + timeOut + ' seconds.');
}
var p1 = new Promise(test);//test爲要執行的函數,接收兩個函數做爲參數,一個在成功時用,另外一個在失敗時
var p2 = p1.then(function (result) {
    console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
    console.log('失敗:' + reason);
});
var job1 = new Promise(function (resolve, reject) {
    log('start new Promise...');
    resolve(123);
});
job1.then(job2).then(job3).catch(handleError);//鏈式處理

同步

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同時執行p1和p2,並在它們都完成後執行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 得到一個Array: ['P1', 'P2']
});

有一個返回便可收工

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
//只須要得到先返回的結果便可,多餘是爲了容錯
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

Promise能夠把不少異步任務以並行和串行的方式組合起來執行。


31. Canvas

沒有canvas,繪圖只能用flash插件,用javascript與flash進行交互。

  • 因爲瀏覽器標準不一致,可在canvas標籤中添加一些說明性HTML,若是支持,瀏覽器會忽略canvas標籤的內容
    <canvas id="test-stock" width="300px" height="200px">
    <p>你的瀏覽器不支持canvas</p>
    </canvas>
  • 判斷瀏覽器是否支持canvas: 用javascript Canvas.getContext來測試瀏覽器是否支持Canvas

getContext(‘2d’)方法會讓咱們拿到CanvasRendringContext2D對象,全部繪圖都要經過這個對象來完成。

3D圖像要使用WebGL,getContext(‘Webgl’)

canvas座標系統

can

Stroke 勾勒邊框 反義詞 fill來進行填充

canvas除了能繪製基本形狀和文本,還能夠實現動畫、縮放、各類濾鏡、像素變換等高級操做,若是要實現很複雜的操做,從如下幾條優化:

  • 經過建立不可見的canvas來繪圖,將最終結果複製到可見的canvas中
  • 使用整數座標而不是浮點數
  • 建立多個canvas繪製不一樣的層,而不是在一個canvas中繪製特別複雜的圖
  • 背景圖片若是不變能夠直接使用img標籤並放到最底層


 



相關文章
相關標籤/搜索